From 23996a47812c34d1471903027c3c74d28d600f20 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Fri, 7 May 2021 18:23:20 +0100 Subject: [PATCH] Rearranged OrgBattle scripts --- .../004_Game classes/007_Game_Character.rb | 2 +- .../001_Challenge_BattleChallenge.rb | 425 +++++ .../001_Battle Frontier/002_Challenge_Data.rb | 223 +++ .../003_Challenge_ChooseFoes.rb | 154 ++ .../004_Challenge_Battles.rb | 145 ++ .../005_UI_BattleSwap.rb} | 0 .../001_Challenge_ChallengeRules.rb | 382 ++++ .../002_Challenge_Rulesets.rb | 324 ++++ .../003_Challenge_EntryRestrictions.rb | 393 +++++ .../004_Challenge_LevelAdjustment.rb | 227 +++ .../005_Challenge_BattleRules.rb | 97 ++ .../001_ChallengeGenerator_Data.rb | 344 ++++ .../002_ChallengeGenerator_Pokemon.rb | 366 ++++ .../003_ChallengeGenerator_Trainers.rb | 215 +++ .../004_ChallengeGenerator_BattleSim.rb | 433 +++++ .../003_OrgBattle.rb | 973 ----------- .../004_OrgBattle_Rules.rb | 1531 ----------------- .../005_OrgBattle_Generator.rb | 1318 -------------- 18 files changed, 3729 insertions(+), 3823 deletions(-) create mode 100644 Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb create mode 100644 Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb create mode 100644 Data/Scripts/018_Alternate battle modes/001_Battle Frontier/003_Challenge_ChooseFoes.rb create mode 100644 Data/Scripts/018_Alternate battle modes/001_Battle Frontier/004_Challenge_Battles.rb rename Data/Scripts/018_Alternate battle modes/{006_BattleSwap.rb => 001_Battle Frontier/005_UI_BattleSwap.rb} (100%) create mode 100644 Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/001_Challenge_ChallengeRules.rb create mode 100644 Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/002_Challenge_Rulesets.rb create mode 100644 Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/003_Challenge_EntryRestrictions.rb create mode 100644 Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/004_Challenge_LevelAdjustment.rb create mode 100644 Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/005_Challenge_BattleRules.rb create mode 100644 Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/001_ChallengeGenerator_Data.rb create mode 100644 Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/002_ChallengeGenerator_Pokemon.rb create mode 100644 Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/003_ChallengeGenerator_Trainers.rb create mode 100644 Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/004_ChallengeGenerator_BattleSim.rb delete mode 100644 Data/Scripts/018_Alternate battle modes/003_OrgBattle.rb delete mode 100644 Data/Scripts/018_Alternate battle modes/004_OrgBattle_Rules.rb delete mode 100644 Data/Scripts/018_Alternate battle modes/005_OrgBattle_Generator.rb diff --git a/Data/Scripts/004_Game classes/007_Game_Character.rb b/Data/Scripts/004_Game classes/007_Game_Character.rb index 88f358625..5886495c4 100644 --- a/Data/Scripts/004_Game classes/007_Game_Character.rb +++ b/Data/Scripts/004_Game classes/007_Game_Character.rb @@ -14,7 +14,7 @@ class Game_Character attr_accessor :character_hue attr_reader :opacity attr_reader :blend_type - attr_reader :direction + attr_accessor :direction attr_accessor :pattern attr_accessor :pattern_surf attr_accessor :lock_pattern diff --git a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb new file mode 100644 index 000000000..e1aaab862 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb @@ -0,0 +1,425 @@ +#=============================================================================== +# +#=============================================================================== +class BattleChallenge + attr_reader :currentChallenge + + BattleTowerID = 0 + BattlePalaceID = 1 + BattleArenaID = 2 + BattleFactoryID = 3 + + def initialize + @bc = BattleChallengeData.new + @currentChallenge = -1 + @types = {} + end + + def set(id, numrounds, rules) + @id = id + @numRounds = numrounds + @rules = rules + pbWriteCup(id, rules) + end + + def register(id, doublebattle, numrounds, numPokemon, battletype, mode = 1) + ensureType(id) + if battletype == BattleFactoryID + @bc.setExtraData(BattleFactoryData.new(@bc)) + numPokemon = 3 + battletype = BattleTowerID + end + @numRounds = numrounds + @rules = modeToRules(doublebattle, numPokemon, battletype, mode) + end + + def rules + if !@rules + @rules = modeToRules(self.data.doublebattle, self.data.numPokemon, + self.data.battletype, self.data.mode) + end + return @rules + end + + def modeToRules(doublebattle, numPokemon, battletype, mode) + rules = PokemonChallengeRules.new + # Set the battle type + case battletype + when BattlePalaceID + rules.setBattleType(BattlePalace.new) + when BattleArenaID + rules.setBattleType(BattleArena.new) + doublebattle = false + else # Factory works the same as Tower + rules.setBattleType(BattleTower.new) + end + # Set standard rules and maximum level + case mode + when 1 # Open Level + rules.setRuleset(StandardRules.new(numPokemon, GameData::GrowthRate.max_level)) + rules.setLevelAdjustment(OpenLevelAdjustment.new(30)) + when 2 # Battle Tent + rules.setRuleset(StandardRules.new(numPokemon, GameData::GrowthRate.max_level)) + rules.setLevelAdjustment(OpenLevelAdjustment.new(60)) + else + rules.setRuleset(StandardRules.new(numPokemon, 50)) + rules.setLevelAdjustment(OpenLevelAdjustment.new(50)) + end + # Set whether battles are single or double + if doublebattle + rules.addBattleRule(DoubleBattle.new) + else + rules.addBattleRule(SingleBattle.new) + end + return rules + end + + def start(*args) + t = ensureType(@id) + @currentChallenge = @id # must appear before pbStart + @bc.pbStart(t, @numRounds) + end + + def pbStart(challenge) + end + + def pbEnd + if @currentChallenge != -1 + ensureType(@currentChallenge).saveWins(@bc) + @currentChallenge = -1 + end + @bc.pbEnd + end + + def pbBattle + return @bc.extraData.pbBattle(self) if @bc.extraData # Battle Factory + opponent = pbGenerateBattleTrainer(self.nextTrainer, self.rules) + bttrainers = pbGetBTTrainers(@id) + trainerdata = bttrainers[self.nextTrainer] + ret = pbOrganizedBattleEx(opponent,self.rules, + pbGetMessageFromHash(MessageTypes::EndSpeechLose, trainerdata[4]), + pbGetMessageFromHash(MessageTypes::EndSpeechWin, trainerdata[3])) + return ret + end + + def pbInChallenge? + return pbInProgress? + end + + def pbInProgress? + return @bc.inProgress + end + + def pbResting? + return @bc.resting + end + + def extra; @bc.extraData; end + def decision; @bc.decision; end + def wins; @bc.wins; end + def swaps; @bc.swaps; end + def battleNumber; @bc.battleNumber; end + def nextTrainer; @bc.nextTrainer; end + def pbGoOn; @bc.pbGoOn; end + def pbAddWin; @bc.pbAddWin; end + def pbCancel; @bc.pbCancel; end + def pbRest; @bc.pbRest; end + def pbMatchOver?; @bc.pbMatchOver?; end + def pbGoToStart; @bc.pbGoToStart; end + + def setDecision(value) + @bc.decision = value + end + + def setParty(value) + @bc.setParty(value) + end + + def data + return nil if !pbInProgress? || @currentChallenge < 0 + return ensureType(@currentChallenge).clone + end + + def getCurrentWins(challenge) + return ensureType(challenge).currentWins + end + + def getPreviousWins(challenge) + return ensureType(challenge).previousWins + end + + def getMaxWins(challenge) + return ensureType(challenge).maxWins + end + + def getCurrentSwaps(challenge) + return ensureType(challenge).currentSwaps + end + + def getPreviousSwaps(challenge) + return ensureType(challenge).previousSwaps + end + + def getMaxSwaps(challenge) + return ensureType(challenge).maxSwaps + end + + private + + def ensureType(id) + @types[id] = BattleChallengeType.new if !@types[id] + return @types[id] + end +end + +#=============================================================================== +# +#=============================================================================== +class BattleChallengeData + attr_reader :battleNumber + attr_reader :numRounds + attr_reader :party + attr_reader :inProgress + attr_reader :resting + attr_reader :wins + attr_reader :swaps + attr_accessor :decision + attr_reader :extraData + + def initialize + reset + end + + def setExtraData(value) + @extraData = value + end + + def setParty(value) + if @inProgress + $Trainer.party = value + @party = value + else + @party = value + end + end + + def pbStart(t, numRounds) + @inProgress = true + @resting = false + @decision = 0 + @swaps = t.currentSwaps + @wins = t.currentWins + @battleNumber = 1 + @trainers = [] + raise _INTL("Number of rounds is 0 or less.") if numRounds <= 0 + @numRounds = numRounds + # Get all the trainers for the next set of battles + btTrainers = pbGetBTTrainers(pbBattleChallenge.currentChallenge) + while @trainers.length < @numRounds + newtrainer = pbBattleChallengeTrainer(@wins + @trainers.length, btTrainers) + found = false + for tr in @trainers + found = true if tr == newtrainer + end + @trainers.push(newtrainer) if !found + end + @start = [$game_map.map_id, $game_player.x, $game_player.y] + @oldParty = $Trainer.party + $Trainer.party = @party if @party + Game.save(safe: true) + end + + def pbGoToStart + if $scene.is_a?(Scene_Map) + $game_temp.player_transferring = true + $game_temp.player_new_map_id = @start[0] + $game_temp.player_new_x = @start[1] + $game_temp.player_new_y = @start[2] + $game_temp.player_new_direction = 8 + $scene.transfer_player + end + end + + def pbAddWin + return if !@inProgress + @battleNumber += 1 + @wins += 1 + end + + def pbAddSwap + @swaps += 1 if @inProgress + end + + def pbMatchOver? + return true if !@inProgress || @decision != 0 + return @battleNumber > @numRounds + end + + def pbRest + return if !@inProgress + @resting = true + pbSaveInProgress + end + + def pbGoOn + return if !@inProgress + @resting = false + pbSaveInProgress + end + + def pbCancel + $Trainer.party = @oldParty if @oldParty + reset + end + + def pbEnd + $Trainer.party = @oldParty + return if !@inProgress + save = (@decision != 0) + reset + $game_map.need_refresh = true + Game.save(safe: true) if save + end + + def nextTrainer + return @trainers[@battleNumber - 1] + end + + private + + def reset + @inProgress = false + @resting = false + @start = nil + @decision = 0 + @wins = 0 + @swaps = 0 + @battleNumber = 0 + @trainers = [] + @oldParty = nil + @party = nil + @extraData = nil + end + + def pbSaveInProgress + oldmapid = $game_map.map_id + oldx = $game_player.x + oldy = $game_player.y + olddirection = $game_player.direction + $game_map.map_id = @start[0] + $game_player.moveto2(@start[1], @start[2]) + $game_player.direction = 8 # facing up + Game.save(safe: true) + $game_map.map_id = oldmapid + $game_player.moveto2(oldx, oldy) + $game_player.direction = olddirection + end +end + +#=============================================================================== +# +#=============================================================================== +class BattleChallengeType + attr_accessor :currentWins + attr_accessor :previousWins + attr_accessor :maxWins + attr_accessor :currentSwaps + attr_accessor :previousSwaps + attr_accessor :maxSwaps + attr_reader :doublebattle + attr_reader :numPokemon + attr_reader :battletype + attr_reader :mode + + def initialize + @previousWins = 0 + @maxWins = 0 + @currentWins = 0 + @currentSwaps = 0 + @previousSwaps = 0 + @maxSwaps = 0 + end + + def saveWins(challenge) + if challenge.decision == 0 # if undecided + @currentWins = 0 + @currentSwaps = 0 + else + if challenge.decision == 1 # if won + @currentWins = challenge.wins + @currentSwaps = challenge.swaps + else # if lost + @currentWins = 0 + @currentSwaps = 0 + end + @maxWins = [@maxWins, challenge.wins].max + @previousWins = challenge.wins + @maxSwaps = [@maxSwaps, challenge.swaps].max + @previousSwaps = challenge.swaps + end + end +end + +#=============================================================================== +# Battle Factory data +#=============================================================================== +class BattleFactoryData + def initialize(bcdata) + @bcdata = bcdata + end + + def pbPrepareRentals + @rentals = pbBattleFactoryPokemon(pbBattleChallenge.rules, @bcdata.wins, @bcdata.swaps, []) + @trainerid = @bcdata.nextTrainer + bttrainers = pbGetBTTrainers(@bcdata.currentChallenge) + trainerdata = bttrainers[@trainerid] + @opponent = NPCTrainer.new( + pbGetMessageFromHash(MessageTypes::TrainerNames, trainerdata[1]), + trainerdata[0]) + opponentPkmn = pbBattleFactoryPokemon(pbBattleChallenge.rules, @bcdata.wins, @bcdata.swaps, @rentals) + @opponent.party = opponentPkmn.shuffle[0, 3] + end + + def pbChooseRentals + pbFadeOutIn { + scene = BattleSwapScene.new + screen = BattleSwapScreen.new(scene) + @rentals = screen.pbStartRent(@rentals) + @bcdata.pbAddSwap + pbBattleChallenge.setParty(@rentals) + } + end + + def pbPrepareSwaps + @oldopponent = @opponent.party + trainerid = @bcdata.nextTrainer + bttrainers = pbGetBTTrainers(@bcdata.currentChallenge) + trainerdata = bttrainers[trainerid] + @opponent = NPCTrainer.new( + pbGetMessageFromHash(MessageTypes::TrainerNames, trainerdata[1]), + trainerdata[0]) + opponentPkmn = pbBattleFactoryPokemon(challenge.rules, @bcdata.wins, @bcdata.swaps, + [].concat(@rentals).concat(@oldopponent)) + @opponent.party = opponentPkmn.shuffle[0, 3] + end + + def pbChooseSwaps + swapMade = true + pbFadeOutIn { + scene = BattleSwapScene.new + screen = BattleSwapScreen.new(scene) + swapMade = screen.pbStartSwap(@rentals, @oldopponent) + @bcdata.pbAddSwap if swapMade + @bcdata.setParty(@rentals) + } + return swapMade + end + + def pbBattle(challenge) + bttrainers = pbGetBTTrainers(@bcdata.currentChallenge) + trainerdata = bttrainers[@trainerid] + return pbOrganizedBattleEx(@opponent, challenge.rules, + pbGetMessageFromHash(MessageTypes::EndSpeechLose, trainerdata[4]), + pbGetMessageFromHash(MessageTypes::EndSpeechWin, trainerdata[3])) + end +end diff --git a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb new file mode 100644 index 000000000..98f45ab4d --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb @@ -0,0 +1,223 @@ +#=============================================================================== +# +#=============================================================================== +def pbBattleChallenge + $PokemonGlobal.challenge = BattleChallenge.new if !$PokemonGlobal.challenge + return $PokemonGlobal.challenge +end + +def pbBattleChallengeBattle + return pbBattleChallenge.pbBattle +end + +# Used in events +def pbHasEligible?(*arg) + return pbBattleChallenge.rules.ruleset.hasValidTeam?($Trainer.party) +end + +#=============================================================================== +# +#=============================================================================== +def pbGetBTTrainers(challengeID) + trlists = (load_data("Data/trainer_lists.dat") rescue []) + trlists.each { |tr| return tr[0] if !tr[5] && tr[2].include?(challengeID) } + trlists.each { |tr| return tr[0] if tr[5] } # is default list + return [] +end + +def pbGetBTPokemon(challengeID) + trlists = (load_data("Data/trainer_lists.dat") rescue []) + trlists.each { |tr| return tr[1] if !tr[5] && tr[2].include?(challengeID) } + trlists.each { |tr| return tr[1] if tr[5] } # is default list + return [] +end + +#=============================================================================== +# +#=============================================================================== +def pbEntryScreen(*arg) + retval = false + pbFadeOutIn { + scene = PokemonParty_Scene.new + screen = PokemonPartyScreen.new(scene, $Trainer.party) + ret = screen.pbPokemonMultipleEntryScreenEx(pbBattleChallenge.rules.ruleset) + # Set party + pbBattleChallenge.setParty(ret) if ret + # Continue (return true) if Pokémon were chosen + retval = (ret != nil && ret.length > 0) + } + return retval +end + +#=============================================================================== +# +#=============================================================================== +class Game_Player < Game_Character + def moveto2(x, y) + @x = x + @y = y + @real_x = @x * Game_Map::REAL_RES_X + @real_y = @y * Game_Map::REAL_RES_Y + @prelock_direction = 0 + end +end + +#=============================================================================== +# +#=============================================================================== +class Game_Event + def pbInChallenge? + return pbBattleChallenge.pbInChallenge? + end +end + +#=============================================================================== +# +#=============================================================================== +def pbBattleChallengeGraphic(event) + nextTrainer = pbBattleChallenge.nextTrainer + bttrainers = pbGetBTTrainers(pbBattleChallenge.currentChallenge) + filename = GameData::TrainerType.charset_filename_brief((bttrainers[nextTrainer][0] rescue nil)) + begin + filename = "NPC 01" if nil_or_empty?(filename) + bitmap = AnimatedBitmap.new("Graphics/Characters/" + filename) + bitmap.dispose + event.character_name = filename + rescue + event.character_name = "NPC 01" + end +end + +def pbBattleChallengeBeginSpeech + return "..." if !pbBattleChallenge.pbInProgress? + bttrainers = pbGetBTTrainers(pbBattleChallenge.currentChallenge) + tr = bttrainers[pbBattleChallenge.nextTrainer] + return (tr) ? pbGetMessageFromHash(MessageTypes::BeginSpeech, tr[2]) : "..." +end + +#=============================================================================== +# +#=============================================================================== +class PBPokemon + attr_accessor :species + attr_accessor :item + attr_accessor :nature + attr_accessor :move1 + attr_accessor :move2 + attr_accessor :move3 + attr_accessor :move4 + attr_accessor :ev + + # This method is how each Pokémon is compiled from the PBS files listing + # Battle Tower/Cup Pokémon. + def self.fromInspected(str) + insp = str.gsub(/^\s+/, "").gsub(/\s+$/, "") + pieces = insp.split(/\s*;\s*/) + species = (GameData::Species.exists?(pieces[0])) ? GameData::Species.get(pieces[0]).id : nil + item = (GameData::Item.exists?(pieces[1])) ? GameData::Item.get(pieces[1]).id : nil + nature = (GameData::Nature.exists?(pieces[2])) ? GameData::Nature.get(pieces[2]).id : nil + ev = pieces[3].split(/\s*,\s*/) + ev_array = [] + ev.each do |stat| + case stat.upcase + when "HP" then ev_array.push(:HP) + when "ATK" then ev_array.push(:ATTACK) + when "DEF" then ev_array.push(:DEFENSE) + when "SA", "SPATK" then ev_array.push(:SPECIAL_ATTACK) + when "SD", "SPDEF" then ev_array.push(:SPECIAL_DEFENSE) + when "SPD" then ev_array.push(:SPEED) + end + end + moves = pieces[4].split(/\s*,\s*/) + moveid = [] + for i in 0...Pokemon::MAX_MOVES + move_data = GameData::Move.try_get(moves[i]) + moveid.push(move_data.id) if move_data + end + if moveid.length == 0 + GameData::Move.each { |mov| moveid.push(mov.id); break } # Get any one move + end + return self.new(species, item, nature, moveid[0], moveid[1], moveid[2], moveid[3], ev_array) + end + + def self.fromPokemon(pkmn) + mov1 = (pkmn.moves[0]) ? pkmn.moves[0].id : nil + mov2 = (pkmn.moves[1]) ? pkmn.moves[1].id : nil + mov3 = (pkmn.moves[2]) ? pkmn.moves[2].id : nil + mov4 = (pkmn.moves[3]) ? pkmn.moves[3].id : nil + ev_array = [] + GameData::Stat.each_main do |s| + ev_array.push(s.id) if pkmn.ev[s.id] > 60 + end + return self.new(pkmn.species, pkmn.item_id, pkmn.nature, + mov1, mov2, mov3, mov4, ev_array) + end + + def initialize(species, item, nature, move1, move2, move3, move4, ev) + @species = species + itm = GameData::Item.try_get(item) + @item = itm ? itm.id : nil + @nature = nature + @move1 = move1 + @move2 = move2 + @move3 = move3 + @move4 = move4 + @ev = ev + end + + def inspect + c1 = GameData::Species.get(@species).id + c2 = (@item) ? GameData::Item.get(@item).id : "" + c3 = (@nature) ? GameData::Nature.get(@nature).id : "" + evlist = "" + @ev.each do |stat| + evlist += "," if evlist != "" + evlist += stat.real_name_brief + end + c4 = (@move1) ? GameData::Move.get(@move1).id : "" + c5 = (@move2) ? GameData::Move.get(@move2).id : "" + c6 = (@move3) ? GameData::Move.get(@move3).id : "" + c7 = (@move4) ? GameData::Move.get(@move4).id : "" + return "#{c1};#{c2};#{c3};#{evlist};#{c4},#{c5},#{c6},#{c7}" + end + + # Unused. + def tocompact + return "#{species},#{item},#{nature},#{move1},#{move2},#{move3},#{move4},#{ev}" + end + +=begin + def _dump(depth) + return [@species, @item, @nature, @move1, @move2, @move3, @move4, @ev].pack("vvCvvvvC") + end + + def self._load(str) + data = str.unpack("vvCvvvvC") + return self.new(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]) + end +=end + + def convertMove(move) + move = :FRUSTRATION if move == :RETURN && GameData::Move.exists?(:FRUSTRATION) + return move + end + + def createPokemon(level, iv, trainer) + pkmn = Pokemon.new(@species, level, trainer, false) + pkmn.item = @item + pkmn.personalID = rand(2**16) | rand(2**16) << 16 + pkmn.nature = nature + pkmn.happiness = 0 + pkmn.moves.push(Pokemon::Move.new(self.convertMove(@move1))) + pkmn.moves.push(Pokemon::Move.new(self.convertMove(@move2))) if @move2 + pkmn.moves.push(Pokemon::Move.new(self.convertMove(@move3))) if @move3 + pkmn.moves.push(Pokemon::Move.new(self.convertMove(@move4))) if @move4 + pkmn.moves.compact! + if ev.length > 0 + ev.each { |stat| pkmn.ev[stat] = Pokemon::EV_LIMIT / ev.length } + end + GameData::Stat.each_main { |s| pkmn.iv[s.id] = iv } + pkmn.calc_stats + return pkmn + end +end diff --git a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/003_Challenge_ChooseFoes.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/003_Challenge_ChooseFoes.rb new file mode 100644 index 000000000..4639246cb --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/003_Challenge_ChooseFoes.rb @@ -0,0 +1,154 @@ +#=============================================================================== +# Given an array of trainers and the number of wins the player already has, +# returns a random trainer index. The more wins, the later in the list the +# trainer comes from. +#=============================================================================== +def pbBattleChallengeTrainer(win_count, bttrainers) + # This table's start points and lengths are based on a bttrainers size of 300. + # They are scaled based on the actual size of bttrainers later. + table = [ # Each value is [minimum win count, range start point, range length] + [ 0, 0, 100], # 0-100 + [ 6, 80, 40], # 80-120 + [ 7, 80, 40], # 80-120 + [13, 120, 20], # 120-140 + [14, 100, 40], # 100-140 + [20, 140, 20], # 140-160 + [21, 120, 40], # 120-160 + [27, 160, 20], # 160-180 + [28, 140, 40], # 140-180 + [34, 180, 20], # 180-200 + [35, 160, 40], # 160-200 + [41, 200, 20], # 200-220 + [42, 180, 40], # 180-220 + [48, 220, 40], # 220-260 + [49, 200, 100] # 200-300 - This line is used for all higher win_counts + ] + slot = nil + table.each { |val| slot = val if val[0] <= win_count && slot[0] < val[0] } + return 0 if !slot + # Scale the start point and length based on how many trainers are in bttrainers + offset = slot[1] * bttrainers.length / 300 + length = slot[2] * bttrainers.length / 300 + # Return a random trainer index from the chosen range + return offset + rand(length) +end + +#=============================================================================== +# +#=============================================================================== +def pbGenerateBattleTrainer(idxTrainer, rules) + bttrainers = pbGetBTTrainers(pbBattleChallenge.currentChallenge) + btpokemon = pbGetBTPokemon(pbBattleChallenge.currentChallenge) + level = rules.ruleset.suggestedLevel + # Create the trainer + trainerdata = bttrainers[idxTrainer] + opponent = NPCTrainer.new( + pbGetMessageFromHash(MessageTypes::TrainerNames, trainerdata[1]), + trainerdata[0]) + # Determine how many IVs the trainer's Pokémon will have + indvalues = 31 + indvalues = 21 if idxTrainer < 220 + indvalues = 18 if idxTrainer < 200 + indvalues = 15 if idxTrainer < 180 + indvalues = 12 if idxTrainer < 160 + indvalues = 9 if idxTrainer < 140 + indvalues = 6 if idxTrainer < 120 + indvalues = 3 if idxTrainer < 100 + # Get the indices within bypokemon of the Pokémon the trainer may have + pokemonnumbers = trainerdata[5] + # The number of possible Pokémon is <= the required number; make them + # all Pokémon and use them + if pokemonnumbers.length <= rules.ruleset.suggestedNumber + for n in pokemonnumbers + rndpoke = btpokemon[n] + pkmn = rndpoke.createPokemon(level, indvalues, opponent) + opponent.party.push(pkmn) + end + return opponent + end + # There are more possible Pokémon than there are spaces available in the + # trainer's party; randomly choose Pokémon + loop do + opponent.party.clear + while opponent.party.length < rules.ruleset.suggestedNumber + rnd = pokemonnumbers[rand(pokemonnumbers.length)] + rndpoke = btpokemon[rnd] + pkmn = rndpoke.createPokemon(level, indvalues, opponent) + opponent.party.push(pkmn) + end + break if rules.ruleset.isValid?(opponent.party) + end + return opponent +end + +#=============================================================================== +# Generate a full team's worth of Pokémon which obey the given rules. +#=============================================================================== +def pbBattleFactoryPokemon(rules, win_count, swap_count, _rentals) + btpokemon = pbGetBTPokemon(pbBattleChallenge.currentChallenge) + level = rules.ruleset.suggestedLevel + pokemonNumbers = [0, 0] # Start and end indices in btpokemon + ivs = [0, 0] # Lower and higher IV values for Pokémon to use + iv_threshold = 6 # Number of Pokémon that use the lower IV + set = [win_count / 7, 7].min # The set of 7 battles win_count is part of (minus 1) + # Choose a range of Pokémon in btpokemon to randomly select from. The higher + # the set number, the later the range lies within btpokemon (typically). + # This table's start point and end point values are based on a btpokemon size + # of 882. They are scaled based on the actual size of btpokemon. + if level == GameData::GrowthRate.max_level # Open Level (Level 100) + table = [ + [372, 467], + [468, 563], + [564, 659], + [660, 755], + [372, 881], + [372, 881], + [372, 881], + [372, 881] # This line is used for all higher sets + ] + else + table = [ + [110, 199], + [162, 266], + [267, 371], + [372, 467], + [468, 563], + [564, 659], + [660, 755], + [372, 849] # This line is used for all higher sets + ] + end + pokemonNumbers[0] = table[set][0] * btpokemon.length / 882 + pokemonNumbers[1] = table[set][1] * btpokemon.length / 882 + # Choose two IV values for Pokémon to use (the one for the current set, and + # the one for the next set). The iv_threshold below determines which of these + # two values a given Pokémon uses. The higher the set number, the higher these + # values are. + ivtable = [3, 6, 9, 12, 15, 21, 31, 31] # Last value is used for all higher sets + ivs = [ivtable[set], ivtable[[set + 1, 7].min]] + # Choose a threshold, which is the number of Pokémon with the lower IV out of + # the two chosen above. The higher the swap_count, the lower this threshold + # (i.e. the more Pokémon will have the higher IV). + thresholds = [ # Each value is [minimum swap count, threshold value] + [ 0, 6], + [15, 5], + [22, 4], + [29, 3], + [36, 2], + [43, 1] + ] + thresholds.each { |val| iv_threshold = val[1] if swap_count >= val[0] } + # Randomly choose Pokémon from the range to fill the party with + party = [] + loop do + party.clear + while party.length < Settings::MAX_PARTY_SIZE + rnd = pokemonNumbers[0] + rand(pokemonNumbers[1] - pokemonNumbers[0] + 1) + rndpoke = btpokemon[rnd] + indvalue = (party.length < iv_threshold) ? ivs[0] : ivs[1] + party.push(rndpoke.createPokemon(level, indvalue, nil)) + end + break if rules.ruleset.isValid?(party) + end + return party +end diff --git a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/004_Challenge_Battles.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/004_Challenge_Battles.rb new file mode 100644 index 000000000..68167c9d8 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/004_Challenge_Battles.rb @@ -0,0 +1,145 @@ +#=============================================================================== +# +#=============================================================================== +class BattleType + def pbCreateBattle(scene, trainer1, trainer2) + return PokeBattle_Battle.new(scene, trainer1.party, trainer2.party, trainer1, trainer2) + end +end + +#=============================================================================== +# +#=============================================================================== +class BattleTower < BattleType + def pbCreateBattle(scene, trainer1, trainer2) + return PokeBattle_RecordedBattle.new(scene, trainer1.party, trainer2.party, trainer1, trainer2) + end +end + +#=============================================================================== +# +#=============================================================================== +class BattlePalace < BattleType + def pbCreateBattle(scene, trainer1, trainer2) + return PokeBattle_RecordedBattlePalace.new(scene, trainer1.party, trainer2.party, trainer1, trainer2) + end +end + +#=============================================================================== +# +#=============================================================================== +class BattleArena < BattleType + def pbCreateBattle(scene, trainer1, trainer2) + return PokeBattle_RecordedBattleArena.new(scene, trainer1.party, trainer2.party, trainer1, trainer2) + end +end + +#=============================================================================== +# +#=============================================================================== +def pbOrganizedBattleEx(opponent, challengedata, endspeech, endspeechwin) + # Skip battle if holding Ctrl in Debug mode + if Input.press?(Input::CTRL) && $DEBUG + pbMessage(_INTL("SKIPPING BATTLE...")) + pbMessage(_INTL("AFTER WINNING...")) + endspeech.each { |msg| pbMessage(msg || "...") } + $PokemonTemp.lastbattle = nil + return true + end + $Trainer.heal_party + # Remember original data, to be restored after battle + challengedata = PokemonChallengeRules.new if !challengedata + oldlevels = challengedata.adjustLevels($Trainer.party, opponent.party) + olditems = $Trainer.party.transform { |p| p.item_id } + olditems2 = opponent.party.transform { |p| p.item_id } + # Create the battle scene (the visual side of it) + scene = pbNewBattleScene + # Create the battle class (the mechanics side of it) + battle = challengedata.createBattle(scene, $Trainer, opponent) + battle.internalBattle = false + battle.endSpeeches = [endspeech] + battle.endSpeechesWin = [endspeechwin] + # Set various other properties in the battle class + pbPrepareBattle(battle) + # Perform the battle itself + decision = 0 + pbBattleAnimation(pbGetTrainerBattleBGM(opponent)) { + pbSceneStandby { + decision = battle.pbStartBattle + } + } + Input.update + # Restore both parties to their original levels + challengedata.unadjustLevels($Trainer.party, opponent.party, oldlevels) + # Heal both parties and restore their original items + $Trainer.party.each_with_index do |pkmn, i| + pkmn.heal + pkmn.makeUnmega + pkmn.makeUnprimal + pkmn.item = olditems[i] + end + opponent.party.each_with_index do |pkmn, i| + pkmn.heal + pkmn.makeUnmega + pkmn.makeUnprimal + pkmn.item = olditems2[i] + end + # Save the record of the battle + $PokemonTemp.lastbattle = nil + if decision == 1 || decision == 2 || decision == 5 # if win, loss or draw + $PokemonTemp.lastbattle = battle.pbDumpRecord + end + # Return true if the player won the battle, and false if any other result + return (decision == 1) +end + +#=============================================================================== +# Methods that record and play back a battle. +#=============================================================================== +def pbRecordLastBattle + $PokemonGlobal.lastbattle = $PokemonTemp.lastbattle + $PokemonTemp.lastbattle = nil +end + +def pbPlayLastBattle + pbPlayBattle($PokemonGlobal.lastbattle) +end + +def pbPlayBattle(battledata) + return if !battledata + scene = pbNewBattleScene + scene.abortable = true + lastbattle = Marshal.restore(StringInput.new(battledata)) + case lastbattle[0] + when BattleChallenge::BattleTowerID + battleplayer = PokeBattle_BattlePlayer.new(scene, lastbattle) + when BattleChallenge::BattlePalaceID + battleplayer = PokeBattle_BattlePalacePlayer.new(scene, lastbattle) + when BattleChallenge::BattleArenaID + battleplayer = PokeBattle_BattleArenaPlayer.new(scene, lastbattle) + end + bgm = BattlePlayerHelper.pbGetBattleBGM(lastbattle) + pbBattleAnimation(bgm) { + pbSceneStandby { + battleplayer.pbStartBattle + } + } +end + +#=============================================================================== +# Debug playback methods. +#=============================================================================== +def pbDebugPlayBattle + params = ChooseNumberParams.new + params.setRange(0, 500) + params.setInitialValue(0) + params.setCancelValue(-1) + num = pbMessageChooseNumber(_INTL("Choose a battle."), params) + if num >= 0 + pbPlayBattleFromFile(sprintf("Battles/Battle%03d.dat", num)) + end +end + +def pbPlayBattleFromFile(filename) + pbRgssOpen(filename, "rb") { |f| pbPlayBattle(f.read) } +end diff --git a/Data/Scripts/018_Alternate battle modes/006_BattleSwap.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/005_UI_BattleSwap.rb similarity index 100% rename from Data/Scripts/018_Alternate battle modes/006_BattleSwap.rb rename to Data/Scripts/018_Alternate battle modes/001_Battle Frontier/005_UI_BattleSwap.rb diff --git a/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/001_Challenge_ChallengeRules.rb b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/001_Challenge_ChallengeRules.rb new file mode 100644 index 000000000..1980874c1 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/001_Challenge_ChallengeRules.rb @@ -0,0 +1,382 @@ +#=============================================================================== +# +#=============================================================================== +class PokemonChallengeRules + attr_reader :ruleset + attr_reader :battletype + attr_reader :levelAdjustment + + def initialize(ruleset = nil) + @ruleset = (ruleset) ? ruleset : PokemonRuleSet.new + @battletype = BattleTower.new + @levelAdjustment = nil + @battlerules = [] + end + + def copy + ret = PokemonChallengeRules.new(@ruleset.copy) + ret.setBattleType(@battletype) + ret.setLevelAdjustment(@levelAdjustment) + for rule in @battlerules + ret.addBattleRule(rule) + end + return ret + end + + def setRuleset(rule) + @ruleset = rule + return self + end + + def setBattleType(rule) + @battletype = rule + return self + end + + def setLevelAdjustment(rule) + @levelAdjustment = rule + return self + end + + def number + return self.ruleset.number + end + + def setNumber(number) + self.ruleset.setNumber(number) + return self + end + + def setDoubleBattle(value) + if value + self.ruleset.setNumber(4) + self.addBattleRule(DoubleBattle.new) + else + self.ruleset.setNumber(3) + self.addBattleRule(SingleBattle.new) + end + return self + end + + def adjustLevels(party1, party2) + return @levelAdjustment.adjustLevels(party1, party2) if @levelAdjustment + return nil + end + + def unadjustLevels(party1, party2, adjusts) + @levelAdjustment.unadjustLevels(party1, party2, adjusts) if @levelAdjustment && adjusts + end + + def adjustLevelsBilateral(party1,party2) + if @levelAdjustment && @levelAdjustment.type == LevelAdjustment::BothTeams + return @levelAdjustment.adjustLevels(party1, party2) + end + return nil + end + + def unadjustLevelsBilateral(party1,party2,adjusts) + if @levelAdjustment && adjusts && @levelAdjustment.type == LevelAdjustment::BothTeams + @levelAdjustment.unadjustLevels(party1, party2, adjusts) + end + end + + def addPokemonRule(rule) + self.ruleset.addPokemonRule(rule) + return self + end + + def addLevelRule(minLevel,maxLevel,totalLevel) + self.addPokemonRule(MinimumLevelRestriction.new(minLevel)) + self.addPokemonRule(MaximumLevelRestriction.new(maxLevel)) + self.addSubsetRule(TotalLevelRestriction.new(totalLevel)) + self.setLevelAdjustment(TotalLevelAdjustment.new(minLevel, maxLevel, totalLevel)) + return self + end + + def addSubsetRule(rule) + self.ruleset.addSubsetRule(rule) + return self + end + + def addTeamRule(rule) + self.ruleset.addTeamRule(rule) + return self + end + + def addBattleRule(rule) + @battlerules.push(rule) + return self + end + + def createBattle(scene, trainer1, trainer2) + battle = @battletype.pbCreateBattle(scene, trainer1, trainer2) + for p in @battlerules + p.setRule(battle) + end + return battle + end +end + +#=============================================================================== +# Stadium Cups rules +#=============================================================================== +def pbPikaCupRules(double) + ret = PokemonChallengeRules.new + ret.addPokemonRule(StandardRestriction.new) + ret.addLevelRule(15, 20, 50) + ret.addTeamRule(SpeciesClause.new) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SleepClause.new) + ret.addBattleRule(FreezeClause.new) + ret.addBattleRule(SelfKOClause.new) + ret.setDoubleBattle(double) + ret.setNumber(3) + return ret +end + +def pbPokeCupRules(double) + ret = PokemonChallengeRules.new + ret.addPokemonRule(StandardRestriction.new) + ret.addLevelRule(50, 55, 155) + ret.addTeamRule(SpeciesClause.new) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SleepClause.new) + ret.addBattleRule(FreezeClause.new) + ret.addBattleRule(SelfdestructClause.new) + ret.setDoubleBattle(double) + ret.setNumber(3) + return ret +end + +def pbPrimeCupRules(double) + ret = PokemonChallengeRules.new + ret.setLevelAdjustment(OpenLevelAdjustment.new(GameData::GrowthRate.max_level)) + ret.addTeamRule(SpeciesClause.new) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SleepClause.new) + ret.addBattleRule(FreezeClause.new) + ret.addBattleRule(SelfdestructClause.new) + ret.setDoubleBattle(double) + return ret +end + +def pbFancyCupRules(double) + ret = PokemonChallengeRules.new + ret.addPokemonRule(StandardRestriction.new) + ret.addLevelRule(25, 30, 80) + ret.addPokemonRule(HeightRestriction.new(2)) + ret.addPokemonRule(WeightRestriction.new(20)) + ret.addPokemonRule(BabyRestriction.new) + ret.addTeamRule(SpeciesClause.new) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SleepClause.new) + ret.addBattleRule(FreezeClause.new) + ret.addBattleRule(PerishSongClause.new) + ret.addBattleRule(SelfdestructClause.new) + ret.setDoubleBattle(double) + ret.setNumber(3) + return ret +end + +def pbLittleCupRules(double) + ret = PokemonChallengeRules.new + ret.addPokemonRule(StandardRestriction.new) + ret.addPokemonRule(UnevolvedFormRestriction.new) + ret.setLevelAdjustment(EnemyLevelAdjustment.new(5)) + ret.addPokemonRule(MaximumLevelRestriction.new(5)) + ret.addTeamRule(SpeciesClause.new) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SleepClause.new) + ret.addBattleRule(FreezeClause.new) + ret.addBattleRule(SelfdestructClause.new) + ret.addBattleRule(PerishSongClause.new) + ret.addBattleRule(SonicBoomClause.new) + ret.setDoubleBattle(double) + return ret +end + +def pbStrictLittleCupRules(double) + ret = PokemonChallengeRules.new + ret.addPokemonRule(StandardRestriction.new) + ret.addPokemonRule(UnevolvedFormRestriction.new) + ret.setLevelAdjustment(EnemyLevelAdjustment.new(5)) + ret.addPokemonRule(MaximumLevelRestriction.new(5)) + ret.addPokemonRule(LittleCupRestriction.new) + ret.addTeamRule(SpeciesClause.new) + ret.addBattleRule(SleepClause.new) + ret.addBattleRule(EvasionClause.new) + ret.addBattleRule(OHKOClause.new) + ret.addBattleRule(SelfKOClause.new) + ret.setDoubleBattle(double) + ret.setNumber(3) + return ret +end + +#=============================================================================== +# Battle Frontier rules +#=============================================================================== +def pbBattleTowerRules(double, openlevel) + ret = PokemonChallengeRules.new + if openlevel + ret.setLevelAdjustment(OpenLevelAdjustment.new(60)) + else + ret.setLevelAdjustment(CappedLevelAdjustment.new(50)) + end + ret.addPokemonRule(StandardRestriction.new) + ret.addTeamRule(SpeciesClause.new) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SoulDewBattleClause.new) + ret.setDoubleBattle(double) + return ret +end + +def pbBattlePalaceRules(double, openlevel) + return pbBattleTowerRules(double, openlevel).setBattleType(BattlePalace.new) +end + +def pbBattleArenaRules(openlevel) + return pbBattleTowerRules(false, openlevel).setBattleType(BattleArena.new) +end + +def pbBattleFactoryRules(double, openlevel) + ret = PokemonChallengeRules.new + if openlevel + ret.setLevelAdjustment(FixedLevelAdjustment.new(100)) + ret.addPokemonRule(MaximumLevelRestriction.new(100)) + else + ret.setLevelAdjustment(FixedLevelAdjustment.new(50)) + ret.addPokemonRule(MaximumLevelRestriction.new(50)) + end + ret.addTeamRule(SpeciesClause.new) + ret.addPokemonRule(BannedSpeciesRestriction.new(:UNOWN)) + ret.addTeamRule(ItemClause.new) + ret.addBattleRule(SoulDewBattleClause.new) + ret.setDoubleBattle(double).setNumber(0) + return ret +end + +#=============================================================================== +# Other Interesting Rulesets +#=============================================================================== +=begin +# Official Species Restriction +.addPokemonRule(BannedSpeciesRestriction.new( + :MEWTWO, :MEW, + :LUGIA, :HOOH, :CELEBI, + :KYOGRE, :GROUDON, :RAYQUAZA, :JIRACHI, :DEOXYS, + :DIALGA, :PALKIA, :GIRATINA, :MANAPHY, :PHIONE, + :DARKRAI, :SHAYMIN, :ARCEUS)) +.addBattleRule(SoulDewBattleClause.new) + +# New Official Species Restriction +.addPokemonRule(BannedSpeciesRestriction.new( + :MEW, + :CELEBI, + :JIRACHI, :DEOXYS, + :MANAPHY, :PHIONE, :DARKRAI, :SHAYMIN, :ARCEUS)) +.addBattleRule(SoulDewBattleClause.new) + +# Pocket Monsters Stadium +PokemonChallengeRules.new +.addPokemonRule(SpeciesRestriction.new( + :VENUSAUR, :CHARIZARD, :BLASTOISE, :BEEDRILL, :FEAROW, + :PIKACHU, :NIDOQUEEN, :NIDOKING, :DUGTRIO, :PRIMEAPE, + :ARCANINE, :ALAKAZAM, :MACHAMP, :GOLEM, :MAGNETON, + :CLOYSTER, :GENGAR, :ONIX, :HYPNO, :ELECTRODE, + :EXEGGUTOR, :CHANSEY, :KANGASKHAN, :STARMIE, :SCYTHER, + :JYNX, :PINSIR, :TAUROS, :GYARADOS, :LAPRAS, + :DITTO, :VAPOREON, :JOLTEON, :FLAREON, :AERODACTYL, + :SNORLAX, :ARTICUNO, :ZAPDOS, :MOLTRES, :DRAGONITE +)) + +# 1999 Tournament Rules +PokemonChallengeRules.new +.addTeamRule(SpeciesClause.new) +.addPokemonRule(ItemsDisallowedClause.new) +.addBattleRule(SleepClause.new) +.addBattleRule(FreezeClause.new) +.addBattleRule(SelfdestructClause.new) +.setDoubleBattle(false) +.setLevelRule(1, 50, 150) +.addPokemonRule(BannedSpeciesRestriction.new( + :VENUSAUR, :DUGTRIO, :ALAKAZAM, :GOLEM, :MAGNETON, + :GENGAR, :HYPNO, :ELECTRODE, :EXEGGUTOR, :CHANSEY, + :KANGASKHAN, :STARMIE, :JYNX, :TAUROS, :GYARADOS, + :LAPRAS, :DITTO, :VAPOREON, :JOLTEON, :SNORLAX, + :ARTICUNO, :ZAPDOS, :DRAGONITE, :MEWTWO, :MEW)) + +# 2005 Tournament Rules +PokemonChallengeRules.new +.addPokemonRule(BannedSpeciesRestriction.new( + :DRAGONITE, :MEW, :MEWTWO, + :TYRANITAR, :LUGIA, :CELEBI, :HOOH, + :GROUDON, :KYOGRE, :RAYQUAZA, :JIRACHI, :DEOXYS)) +.setDoubleBattle(true) +.addLevelRule(1, 50, 200) +.addTeamRule(ItemClause.new) +.addPokemonRule(BannedItemRestriction.new(:SOULDEW, :ENIGMABERRY)) +.addBattleRule(SleepClause.new) +.addBattleRule(FreezeClause.new) +.addBattleRule(SelfdestructClause.new) +.addBattleRule(PerishSongClause.new) + +# 2008 Tournament Rules +PokemonChallengeRules.new +.addPokemonRule(BannedSpeciesRestriction.new( + :MEWTWO, :MEW, + :TYRANITAR, :LUGIA, :HOOH, :CELEBI, + :GROUDON, :KYOGRE, :RAYQUAZA, :JIRACHI, :DEOXYS, + :PALKIA, :DIALGA, :PHIONE, :MANAPHY, :ROTOM, :SHAYMIN, :DARKRAI)) +.setDoubleBattle(true) +.addLevelRule(1, 50, 200) +.addTeamRule(NicknameClause.new) +.addTeamRule(ItemClause.new) +.addBattleRule(SoulDewBattleClause.new) + +# 2010 Tournament Rules +PokemonChallengeRules.new +.addPokemonRule(BannedSpeciesRestriction.new( + :MEW, + :CELEBI, + :JIRACHI, :DEOXYS, + :PHIONE, :MANAPHY, :SHAYMIN, :DARKRAI, :ARCEUS)) +.addSubsetRule(RestrictedSpeciesSubsetRestriction.new( + :MEWTWO, + :LUGIA, :HOOH, + :GROUDON, :KYOGRE, :RAYQUAZA, + :PALKIA, :DIALGA, :GIRATINA)) +.setDoubleBattle(true) +.addLevelRule(1, 100, 600) +.setLevelAdjustment(CappedLevelAdjustment.new(50)) +.addTeamRule(NicknameClause.new) +.addTeamRule(ItemClause.new) +.addPokemonRule(SoulDewClause.new) + +# Pokemon Colosseum -- Anything Goes +PokemonChallengeRules.new +.addLevelRule(1, 100, 600) +.addBattleRule(SleepClause.new) +.addBattleRule(FreezeClause.new) +.addBattleRule(SelfdestructClause.new) +.addBattleRule(PerishSongClause.new) + +# Pokemon Colosseum -- Max Lv. 50 +PokemonChallengeRules.new +.addLevelRule(1, 50, 300) +.addTeamRule(SpeciesClause.new) +.addTeamRule(ItemClause.new) +.addBattleRule(SleepClause.new) +.addBattleRule(FreezeClause.new) +.addBattleRule(SelfdestructClause.new) +.addBattleRule(PerishSongClause.new) + +# Pokemon Colosseum -- Max Lv. 100 +PokemonChallengeRules.new +.addLevelRule(1, 100, 600) +.addTeamRule(SpeciesClause.new) +.addTeamRule(ItemClause.new) +.addBattleRule(SleepClause.new) +.addBattleRule(FreezeClause.new) +.addBattleRule(SelfdestructClause.new) +.addBattleRule(PerishSongClause.new) +=end diff --git a/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/002_Challenge_Rulesets.rb b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/002_Challenge_Rulesets.rb new file mode 100644 index 000000000..4babeffa7 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/002_Challenge_Rulesets.rb @@ -0,0 +1,324 @@ +#=============================================================================== +# +#=============================================================================== +class PokemonRuleSet + def initialize(number = 0) + @pokemonRules = [] + @teamRules = [] + @subsetRules = [] + @minLength = 1 + @number = number + end + + def copy + ret = PokemonRuleSet.new(@number) + for rule in @pokemonRules + ret.addPokemonRule(rule) + end + for rule in @teamRules + ret.addTeamRule(rule) + end + for rule in @subsetRules + ret.addSubsetRule(rule) + end + return ret + end + + def minLength + return (@minLength) ? @minLength : self.maxLength + end + + def maxLength + return (@number < 0) ? 6 : @number + end + alias number maxLength + + def minTeamLength + return [1, self.minLength].max + end + + def maxTeamLength + return [6, self.maxLength].max + end + + # Returns the length of a valid subset of a Pokemon team. + def suggestedNumber + return self.maxLength + end + + # Returns a valid level to assign to each member of a valid Pokemon team. + def suggestedLevel + minLevel = 1 + maxLevel = GameData::GrowthRate.max_level + num = self.suggestedNumber + for rule in @pokemonRules + if rule.is_a?(MinimumLevelRestriction) + minLevel = rule.level + elsif rule.is_a?(MaximumLevelRestriction) + maxLevel = rule.level + end + end + totalLevel = maxLevel * num + for rule in @subsetRules + totalLevel = rule.level if rule.is_a?(TotalLevelRestriction) + end + return [maxLevel, minLevel].max if totalLevel >= maxLevel * num + return [totalLevel / self.suggestedNumber, minLevel].max + end + + def setNumberRange(minValue, maxValue) + @minLength = [1, minValue].max + @number = [maxValue, 6].min + return self + end + + def setNumber(value) + return setNumberRange(value, value) + end + + # This rule checks either: + # - the entire team to determine whether a subset of the team meets the rule, or + # - whether the entire team meets the rule. If the condition holds for the + # entire team, the condition must also hold for any possible subset of the + # team with the suggested number. + # Examples of team rules: + # - No two Pokemon can be the same species. + # - No two Pokemon can hold the same items. + def addTeamRule(rule) + @teamRules.push(rule) + return self + end + + # This rule checks: + # - the entire team to determine whether a subset of the team meets the rule, or + # - a list of Pokemon whose length is equal to the suggested number. For an + # entire team, the condition must hold for at least one possible subset of + # the team, but not necessarily for the entire team. + # A subset rule is "number-dependent", that is, whether the condition is likely + # to hold depends on the number of Pokemon in the subset. + # Example of a subset rule: + # - The combined level of X Pokemon can't exceed Y. + def addSubsetRule(rule) + @teamRules.push(rule) + return self + end + + def addPokemonRule(rule) + @pokemonRules.push(rule) + return self + end + + def clearTeamRules + @teamRules.clear + return self + end + + def clearSubsetRules + @subsetRules.clear + return self + end + + def clearPokemonRules + @pokemonRules.clear + return self + end + + def isPokemonValid?(pkmn) + return false if !pkmn + for rule in @pokemonRules + return false if !rule.isValid?(pkmn) + end + return true + end + + def hasRegistrableTeam?(list) + return false if !list || list.length < self.minTeamLength + pbEachCombination(list, self.maxTeamLength) { |comb| + return true if canRegisterTeam?(comb) + } + return false + end + + # Returns true if the team's length is greater or equal to the suggested number + # and is 6 or less, the team as a whole meets the requirements of any team + # rules, and at least one subset of the team meets the requirements of any + # subset rules. Each Pokemon in the team must be valid. + def canRegisterTeam?(team) + return false if !team || team.length < self.minTeamLength + return false if team.length > self.maxTeamLength + teamNumber = [self.maxLength, team.length].min + for pkmn in team + return false if !isPokemonValid?(pkmn) + end + for rule in @teamRules + return false if !rule.isValid?(team) + end + if @subsetRules.length > 0 + pbEachCombination(team, teamNumber) { |comb| + isValid = true + for rule in @subsetRules + next if rule.isValid?(comb) + isValid = false + break + end + return true if isValid + } + return false + end + return true + end + + # Returns true if the team's length is greater or equal to the suggested number + # and at least one subset of the team meets the requirements of any team rules + # and subset rules. Not all Pokemon in the team have to be valid. + def hasValidTeam?(team) + return false if !team || team.length < self.minTeamLength + teamNumber = [self.maxLength, team.length].min + validPokemon = [] + for pkmn in team + validPokemon.push(pkmn) if isPokemonValid?(pkmn) + end + return false if validPokemon.length < teamNumber + if @teamRules.length > 0 + pbEachCombination(team, teamNumber) { |comb| return true if isValid?(comb) } + return false + end + return true + end + + # Returns true if the team's length meets the subset length range requirements + # and the team meets the requirements of any team rules and subset rules. Each + # Pokemon in the team must be valid. + def isValid?(team, error = nil) + if team.length < self.minLength + error.push(_INTL("Choose a Pokémon.")) if error && self.minLength == 1 + error.push(_INTL("{1} Pokémon are needed.", self.minLength)) if error && self.minLength > 1 + return false + elsif team.length > self.maxLength + error.push(_INTL("No more than {1} Pokémon may enter.", self.maxLength)) if error + return false + end + for pkmn in team + next if isPokemonValid?(pkmn) + if pkmn + error.push(_INTL("This team is not allowed.", pkmn.name)) if error + else + error.push(_INTL("{1} is not allowed.", pkmn.name)) if error + end + return false + end + for rule in @teamRules + next if rule.isValid?(team) + error.push(rule.errorMessage) if error + return false + end + for rule in @subsetRules + next if rule.isValid?(team) + error.push(rule.errorMessage) if error + return false + end + return true + end +end + +#=============================================================================== +# +#=============================================================================== +class StandardRules < PokemonRuleSet + attr_reader :number + + def initialize(number, level = nil) + super(number) + addPokemonRule(StandardRestriction.new) + addPokemonRule(SpeciesClause.new) + addPokemonRule(ItemClause.new) + addPokemonRule(MaximumLevelRestriction.new(level)) if level + end +end + +########################################### +# Generation IV Cups +########################################### +#=============================================================================== +# +#=============================================================================== +class StandardCup < StandardRules + def initialize + super(3, 50) + end + + def name + return _INTL("Standard Cup") + end +end + +#=============================================================================== +# +#=============================================================================== +class DoubleCup < StandardRules + def initialize + super(4, 50) + end + + def name + return _INTL("Double Cup") + end +end + +#=============================================================================== +# +#=============================================================================== +class FancyCup < PokemonRuleSet + def initialize + super(3) + addPokemonRule(StandardRestriction.new) + addPokemonRule(MaximumLevelRestriction.new(30)) + addSubsetRule(TotalLevelRestriction.new(80)) + addPokemonRule(HeightRestriction.new(2)) + addPokemonRule(WeightRestriction.new(20)) + addPokemonRule(BabyRestriction.new) + addPokemonRule(SpeciesClause.new) + addPokemonRule(ItemClause.new) + end + + def name + return _INTL("Fancy Cup") + end +end + +#=============================================================================== +# +#=============================================================================== +class LittleCup < PokemonRuleSet + def initialize + super(3) + addPokemonRule(StandardRestriction.new) + addPokemonRule(MaximumLevelRestriction.new(5)) + addPokemonRule(BabyRestriction.new) + addPokemonRule(SpeciesClause.new) + addPokemonRule(ItemClause.new) + end + + def name + return _INTL("Little Cup") + end +end + +#=============================================================================== +# +#=============================================================================== +class LightCup < PokemonRuleSet + def initialize + super(3) + addPokemonRule(StandardRestriction.new) + addPokemonRule(MaximumLevelRestriction.new(50)) + addPokemonRule(WeightRestriction.new(99)) + addPokemonRule(BabyRestriction.new) + addPokemonRule(SpeciesClause.new) + addPokemonRule(ItemClause.new) + end + + def name + return _INTL("Light Cup") + end +end diff --git a/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/003_Challenge_EntryRestrictions.rb b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/003_Challenge_EntryRestrictions.rb new file mode 100644 index 000000000..fc28b042d --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/003_Challenge_EntryRestrictions.rb @@ -0,0 +1,393 @@ +#=============================================================================== +# +#=============================================================================== +class StandardRestriction + def isValid?(pkmn) + return false if !pkmn || pkmn.egg? + # Species with disadvantageous abilities are not banned + pkmn.species_data.abilities.each do |a| + return true if [:TRUANT, :SLOWSTART].include?(a) + end + # Certain named species are not banned + return true if [:DRAGONITE, :SALAMENCE, :TYRANITAR].include?(pkmn.species) + # Certain named species are banned + return false if [:WYNAUT, :WOBBUFFET].include?(pkmn.species) + # Species with total base stat 600 or more are banned + bst = 0 + pkmn.baseStats.each_value { |s| bst += s } + return false if bst >= 600 + # Is valid + return true + end +end + +#=============================================================================== +# +#=============================================================================== +class HeightRestriction + def initialize(maxHeightInMeters) + @level = maxHeightInMeters + end + + def isValid?(pkmn) + height = (pkmn.is_a?(Pokemon)) ? pkmn.height : GameData::Species.get(pkmn).height + return height <= (@level * 10).round + end +end + +#=============================================================================== +# +#=============================================================================== +class WeightRestriction + def initialize(maxWeightInKg) + @level = maxWeightInKg + end + + def isValid?(pkmn) + weight = (pkmn.is_a?(Pokemon)) ? pkmn.weight : GameData::Species.get(pkmn).weight + return weight <= (@level * 10).round + end +end + +#=============================================================================== +# Unused +#=============================================================================== +class NegativeExtendedGameClause + def isValid?(pkmn) + return false if pkmn.isSpecies?(:ARCEUS) + return false if pkmn.hasItem?(:MICLEBERRY) + return false if pkmn.hasItem?(:CUSTAPBERRY) + return false if pkmn.hasItem?(:JABOCABERRY) + return false if pkmn.hasItem?(:ROWAPBERRY) + end +end + +#=============================================================================== +# +#=============================================================================== +$babySpeciesData = {} + +class BabyRestriction + def isValid?(pkmn) + if !$babySpeciesData[pkmn.species] + $babySpeciesData[pkmn.species] = pkmn.species_data.get_baby_species + end + return pkmn.species == $babySpeciesData[pkmn.species] + end +end + +#=============================================================================== +# +#=============================================================================== +$canEvolve = {} + +class UnevolvedFormRestriction + def isValid?(pkmn) + if !$babySpeciesData[pkmn.species] + $babySpeciesData[pkmn.species] = pkmn.species_data.get_baby_species + end + return false if pkmn.species != $babySpeciesData[pkmn.species] + if $canEvolve[pkmn.species].nil? + $canEvolve[pkmn.species] = (pkmn.species_data.get_evolutions(true).length > 0) + end + return $canEvolve[pkmn.species] + end +end + +#=============================================================================== +# +#=============================================================================== +module NicknameChecker + @@names = {} + + def getName(species) + n = @@names[species] + return n if n + n = GameData::Species.get(species).name + @@names[species] = n.upcase + return n + end + + def check(name, species) + name = name.upcase + return true if name == getName(species) + return false if @@names.values.include?(name) + GameData::Species.each do |species_data| + next if species_data.species == species || species_data.form != 0 + return false if getName(species_data.id) == name + end + return true + end +end + +#=============================================================================== +# No two Pokemon can have the same nickname. +# No nickname can be the same as the (real) name of another Pokemon character. +#=============================================================================== +class NicknameClause + def isValid?(team) + for i in 0...team.length - 1 + for j in i + 1...team.length + return false if team[i].name == team[j].name + return false if !NicknameChecker.check(team[i].name, team[i].species) + end + end + return true + end + + def errorMessage + return _INTL("No identical nicknames.") + end +end + +#=============================================================================== +# +#=============================================================================== +class NonEggRestriction + def isValid?(pkmn) + return pkmn && !pkmn.egg? + end +end + +#=============================================================================== +# +#=============================================================================== +class AblePokemonRestriction + def isValid?(pkmn) + return pkmn && pkmn.able? + end +end + +#=============================================================================== +# +#=============================================================================== +class SpeciesRestriction + def initialize(*specieslist) + @specieslist = specieslist.clone + end + + def isSpecies?(species, specieslist) + return specieslist.include?(species) + end + + def isValid?(pkmn) + return isSpecies?(pkmn.species, @specieslist) + end +end + +#=============================================================================== +# +#=============================================================================== +class BannedSpeciesRestriction + def initialize(*specieslist) + @specieslist = specieslist.clone + end + + def isSpecies?(species, specieslist) + return specieslist.include?(species) + end + + def isValid?(pkmn) + return !isSpecies?(pkmn.species, @specieslist) + end +end + +#=============================================================================== +# +#=============================================================================== +class RestrictedSpeciesRestriction + def initialize(maxValue, *specieslist) + @specieslist = specieslist.clone + @maxValue = maxValue + end + + def isSpecies?(species, specieslist) + return specieslist.include?(species) + end + + def isValid?(team) + count = 0 + team.each do |pkmn| + count += 1 if pkmn && isSpecies?(pkmn.species, @specieslist) + end + return count <= @maxValue + end +end + +#=============================================================================== +# +#=============================================================================== +class RestrictedSpeciesTeamRestriction < RestrictedSpeciesRestriction + def initialize(*specieslist) + super(4, *specieslist) + end +end + +#=============================================================================== +# +#=============================================================================== +class RestrictedSpeciesSubsetRestriction < RestrictedSpeciesRestriction + def initialize(*specieslist) + super(2, *specieslist) + end +end + +#=============================================================================== +# +#=============================================================================== +class SameSpeciesClause + def isValid?(team) + species = [] + team.each do |pkmn| + species.push(pkmn.species) if pkmn && !species.include?(pkmn.species) + end + return species.length == 1 + end + + def errorMessage + return _INTL("Pokémon must be the same species.") + end +end + +#=============================================================================== +# +#=============================================================================== +class SpeciesClause + def isValid?(team) + species = [] + team.each do |pkmn| + next if !pkmn + return false if species.include?(pkmn.species) + species.push(pkmn.species) + end + return true + end + + def errorMessage + return _INTL("Pokémon can't be the same species.") + end +end + +#=============================================================================== +# +#=============================================================================== +class MinimumLevelRestriction + attr_reader :level + + def initialize(minLevel) + @level = minLevel + end + + def isValid?(pkmn) + return pkmn.level >= @level + end +end + +#=============================================================================== +# +#=============================================================================== +class MaximumLevelRestriction + attr_reader :level + + def initialize(maxLevel) + @level = maxLevel + end + + def isValid?(pkmn) + return pkmn.level <= @level + end +end + +#=============================================================================== +# +#=============================================================================== +class TotalLevelRestriction + attr_reader :level + + def initialize(level) + @level = level + end + + def isValid?(team) + totalLevel = 0 + team.each { |pkmn| totalLevel += pkmn.level if pkmn } + return totalLevel <= @level + end + + def errorMessage + return _INTL("The combined levels exceed {1}.", @level) + end +end + +#=============================================================================== +# +#=============================================================================== +class BannedItemRestriction + def initialize(*itemlist) + @itemlist = itemlist.clone + end + + def isSpecies?(item,itemlist) + return itemlist.include?(item) + end + + def isValid?(pkmn) + return !pkmn.item_id || !isSpecies?(pkmn.item_id, @itemlist) + end +end + +#=============================================================================== +# +#=============================================================================== +class ItemsDisallowedClause + def isValid?(pkmn) + return !pkmn.hasItem? + end +end + +#=============================================================================== +# +#=============================================================================== +class SoulDewClause + def isValid?(pkmn) + return !pkmn.hasItem?(:SOULDEW) + end +end + +#=============================================================================== +# +#=============================================================================== +class ItemClause + def isValid?(team) + items = [] + team.each do |pkmn| + next if !pkmn || !pkmn.hasItem? + return false if items.include?(pkmn.item_id) + items.push(pkmn.item_id) + end + return true + end + + def errorMessage + return _INTL("No identical hold items.") + end +end + +#=============================================================================== +# +#=============================================================================== +class LittleCupRestriction + def isValid?(pkmn) + return false if pkmn.hasItem?(:BERRYJUICE) + return false if pkmn.hasItem?(:DEEPSEATOOTH) + return false if pkmn.hasMove?(:SONICBOOM) + return false if pkmn.hasMove?(:DRAGONRAGE) + return false if pkmn.isSpecies?(:SCYTHER) + return false if pkmn.isSpecies?(:SNEASEL) + return false if pkmn.isSpecies?(:MEDITITE) + return false if pkmn.isSpecies?(:YANMA) + return false if pkmn.isSpecies?(:TANGELA) + return false if pkmn.isSpecies?(:MURKROW) + return true + end +end diff --git a/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/004_Challenge_LevelAdjustment.rb b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/004_Challenge_LevelAdjustment.rb new file mode 100644 index 000000000..889a49101 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/004_Challenge_LevelAdjustment.rb @@ -0,0 +1,227 @@ +#=============================================================================== +# +#=============================================================================== +class LevelAdjustment + BothTeams = 0 + EnemyTeam = 1 + MyTeam = 2 + BothTeamsDifferent = 3 + + def initialize(adjustment) + @adjustment = adjustment + end + + def type + @adjustment + end + + def self.getNullAdjustment(thisTeam, _otherTeam) + ret = [] + thisTeam.each_with_index { |pkmn, i| ret[i] = pkmn.level } + return ret + end + + def getAdjustment(thisTeam, otherTeam) + return self.getNullAdjustment(thisTeam, otherTeam) + end + + def getOldExp(team1, _team2) + ret = [] + team1.each_with_index { |pkmn, i| ret[i] = pkmn.exp } + return ret + end + + def unadjustLevels(team1, team2, adjustments) + team1.each_with_index do |pkmn, i| + next if !adjustments[0][i] || pkmn.exp == adjustments[0][i] + pkmn.exp = adjustments[0][i] + pkmn.calc_stats + end + team2.each_with_index do |pkmn, i| + next if !adjustments[1][i] || pkmn.exp == adjustments[1][i] + pkmn.exp = adjustments[1][i] + pkmn.calc_stats + end + end + + def adjustLevels(team1, team2) + adj1 = nil + adj2 = nil + ret = [getOldExp(team1, team2), getOldExp(team2, team1)] + if @adjustment == BothTeams || @adjustment == MyTeam + adj1 = getAdjustment(team1, team2) + elsif @adjustment == BothTeamsDifferent + adj1 = getMyAdjustment(team1, team2) + end + if @adjustment == BothTeams || @adjustment == EnemyTeam + adj2 = getAdjustment(team2, team1) + elsif @adjustment == BothTeamsDifferent + adj2 = getTheirAdjustment(team2, team1) + end + if adj1 + team1.each_with_index do |pkmn, i| + next if pkmn.level == adj1[i] + pkmn.level = adj1[i] + pkmn.calc_stats + end + end + if adj2 + team2.each_with_index do |pkmn, i| + next if pkmn.level == adj2[i] + pkmn.level = adj2[i] + pkmn.calc_stats + end + end + return ret + end +end + +#=============================================================================== +# +#=============================================================================== +class FixedLevelAdjustment < LevelAdjustment + def initialize(level) + super(LevelAdjustment::BothTeams) + @level = level.clamp(1, GameData::GrowthRate.max_level) + end + + def getAdjustment(thisTeam, _otherTeam) + ret = [] + thisTeam.each_with_index { |pkmn, i| ret[i] = @level } + return ret + end +end + +#=============================================================================== +# +#=============================================================================== +class TotalLevelAdjustment < LevelAdjustment + def initialize(minLevel, maxLevel, totalLevel) + super(LevelAdjustment::EnemyTeam) + @minLevel = minLevel.clamp(1, GameData::GrowthRate.max_level) + @maxLevel = maxLevel.clamp(1, GameData::GrowthRate.max_level) + @totalLevel=totalLevel + end + + def getAdjustment(thisTeam, _otherTeam) + ret = [] + total = 0 + thisTeam.each_with_index do |pkmn, i| + ret[i] = @minLevel + total += @minLevel + end + loop do + work = false + thisTeam.each_with_index do |pkmn, i| + next if ret[i] >= @maxLevel || total >= @totalLevel + ret[i] += 1 + total += 1 + work = true + end + break if !work + end + return ret + end +end + +#=============================================================================== +# +#=============================================================================== +class CombinedLevelAdjustment < LevelAdjustment + def initialize(my, their) + super(LevelAdjustment::BothTeamsDifferent) + @my = my + @their = their + end + + def getMyAdjustment(myTeam,theirTeam) + return @my.getAdjustment(myTeam, theirTeam) if @my + return LevelAdjustment.getNullAdjustment(myTeam, theirTeam) + end + + def getTheirAdjustment(theirTeam,myTeam) + return @their.getAdjustment(theirTeam, myTeam) if @their + return LevelAdjustment.getNullAdjustment(theirTeam, myTeam) + end +end + +#=============================================================================== +# +#=============================================================================== +class SinglePlayerCappedLevelAdjustment < CombinedLevelAdjustment + def initialize(level) + super(CappedLevelAdjustment.new(level), FixedLevelAdjustment.new(level)) + end +end + +#=============================================================================== +# +#=============================================================================== +class CappedLevelAdjustment < LevelAdjustment + def initialize(level) + super(LevelAdjustment::BothTeams) + @level = level.clamp(1, GameData::GrowthRate.max_level) + end + + def getAdjustment(thisTeam, _otherTeam) + ret = [] + thisTeam.each_with_index { |pkmn, i| ret[i] = [pkmn.level, @level].min } + return ret + end +end + +#=============================================================================== +# Unused +#=============================================================================== +class LevelBalanceAdjustment < LevelAdjustment + def initialize(minLevel) + super(LevelAdjustment::BothTeams) + @minLevel = minLevel + end + + def getAdjustment(thisTeam, _otherTeam) + ret = [] + thisTeam.each_with_index do |pkmn, i| + ret[i] = (113 - (pbBaseStatTotal(pkmn.species) * 0.072)).round + end + return ret + end +end + +#=============================================================================== +# +#=============================================================================== +class EnemyLevelAdjustment < LevelAdjustment + def initialize(level) + super(LevelAdjustment::EnemyTeam) + @level = level.clamp(1, GameData::GrowthRate.max_level) + end + + def getAdjustment(thisTeam, _otherTeam) + ret = [] + thisTeam.each_with_index { |pkmn, i| ret[i] = @level } + return ret + end +end + +#=============================================================================== +# +#=============================================================================== +class OpenLevelAdjustment < LevelAdjustment + def initialize(minLevel = 1) + super(LevelAdjustment::EnemyTeam) + @minLevel = minLevel + end + + def getAdjustment(thisTeam, otherTeam) + maxLevel = 1 + otherTeam.each do |pkmn| + level = pkmn.level + maxLevel = level if maxLevel < level + end + maxLevel = @minLevel if maxLevel < @minLevel + ret = [] + thisTeam.each_with_index { |pkmn, i| ret[i] = maxLevel } + return ret + end +end diff --git a/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/005_Challenge_BattleRules.rb b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/005_Challenge_BattleRules.rb new file mode 100644 index 000000000..215f252a9 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/002_Battle Frontier rules/005_Challenge_BattleRules.rb @@ -0,0 +1,97 @@ +#=============================================================================== +# +#=============================================================================== +class BattleRule + def setRule(battle); end +end + +#=============================================================================== +# +#=============================================================================== +class DoubleBattle < BattleRule + def setRule(battle); battle.setBattleMode("double"); end +end + +#=============================================================================== +# +#=============================================================================== +class SingleBattle < BattleRule + def setRule(battle); battle.setBattleMode("single"); end +end + +#=============================================================================== +# +#=============================================================================== +class SoulDewBattleClause < BattleRule + def setRule(battle); battle.rules["souldewclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class SleepClause < BattleRule + def setRule(battle); battle.rules["sleepclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class FreezeClause < BattleRule + def setRule(battle); battle.rules["freezeclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class EvasionClause < BattleRule + def setRule(battle); battle.rules["evasionclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class OHKOClause < BattleRule + def setRule(battle); battle.rules["ohkoclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class PerishSongClause < BattleRule + def setRule(battle); battle.rules["perishsong"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class SelfKOClause < BattleRule + def setRule(battle); battle.rules["selfkoclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class SelfdestructClause < BattleRule + def setRule(battle); battle.rules["selfdestructclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class SonicBoomClause < BattleRule + def setRule(battle); battle.rules["sonicboomclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class ModifiedSleepClause < BattleRule + def setRule(battle); battle.rules["modifiedsleepclause"] = true; end +end + +#=============================================================================== +# +#=============================================================================== +class SkillSwapClause < BattleRule + def setRule(battle); battle.rules["skillswapclause"] = true; end +end diff --git a/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/001_ChallengeGenerator_Data.rb b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/001_ChallengeGenerator_Data.rb new file mode 100644 index 000000000..243a8d04a --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/001_ChallengeGenerator_Data.rb @@ -0,0 +1,344 @@ +#=============================================================================== +# +#=============================================================================== +class BaseStatRestriction + def initialize(mn, mx) + @mn = mn + @mx = mx + end + + def isValid?(pkmn) + bst = baseStatTotal(pkmn.species) + return bst >= @mn && bst <= @mx + end +end + +#=============================================================================== +# +#=============================================================================== +class NonlegendaryRestriction + def isValid?(pkmn) + return true if !pkmn.genderless? + return false if pkmn.species_data.egg_groups.include?(:Undiscovered) + return true + end +end + +#=============================================================================== +# +#=============================================================================== +class InverseRestriction + def initialize(r) + @r = r + end + + def isValid?(pkmn) + return !@r.isValid?(pkmn) + end +end + +#=============================================================================== +# +#=============================================================================== +=begin +[3/10] +0-266 - 0-500 +[106] +267-372 - 380-500 +[95] +373-467 - 400-555 (nonlegendary) +468-563 - 400-555 (nonlegendary) +564-659 - 400-555 (nonlegendary) +660-755 - 400-555 (nonlegendary) +756-799 - 580-600 [legendary] (compat1==15 or compat2==15, genderbyte=255) +800-849 - 500- +850-881 - 580- +=end + +def withRestr(_rule, minbs, maxbs, legendary) + ret = PokemonChallengeRules.new.addPokemonRule(BaseStatRestriction.new(minbs, maxbs)) + if legendary == 0 + ret.addPokemonRule(NonlegendaryRestriction.new) + elsif legendary == 1 + ret.addPokemonRule(InverseRestriction.new(NonlegendaryRestriction.new)) + end + return ret +end + +def pbArrangeByTier(pokemonlist, rule) + tiers = [ + withRestr(rule, 0, 500, 0), + withRestr(rule, 380, 500, 0), + withRestr(rule, 400, 555, 0), + withRestr(rule, 400, 555, 0), + withRestr(rule, 400, 555, 0), + withRestr(rule, 400, 555, 0), + withRestr(rule, 580, 680, 1), + withRestr(rule, 500, 680, 0), + withRestr(rule, 580, 680, 2) + ] + tierPokemon = [] + tiers.length.times do + tierPokemon.push([]) + end + # Sort each Pokémon into tiers. Which tier a Pokémon is put in deoends on the + # Pokémon's position within pokemonlist (later = higher tier). pokemonlist is + # already roughly arranged by rank from weakest to strongest. + for i in 0...pokemonlist.length + next if !rule.ruleset.isPokemonValid?(pokemonlist[i]) + validtiers = [] + for j in 0...tiers.length + validtiers.push(j) if tiers[j].ruleset.isPokemonValid?(pokemonlist[i]) + end + if validtiers.length > 0 + vt = validtiers.length * i / pokemonlist.length + tierPokemon[validtiers[vt]].push(pokemonlist[i]) + end + end + # Now for each tier, sort the Pokemon in that tier by their BST (lowest first). + ret = [] + for i in 0...tiers.length + tierPokemon[i].sort! { |a, b| + bstA = baseStatTotal(a.species) + bstB = baseStatTotal(b.species) + (bstA == bstB) ? a.species <=> b.species : bstA <=> bstB + } + ret.concat(tierPokemon[i]) + end + return ret +end + +#=============================================================================== +# +#=============================================================================== +def pbReplenishBattlePokemon(party, rule) + while party.length < 20 + pkmn = pbRandomPokemonFromRule(rule, nil) + found = false + for pk in party + next if !isBattlePokemonDuplicate(pkmn, pk) + found = true + break + end + party.push(pkmn) if !found + end +end + +def isBattlePokemonDuplicate(pk, pk2) + return false if pk.species != pk2.species + moves1 = [] + moves2 = [] + for i in 0...Pokemon::MAX_MOVES + moves1.push((pk.moves[i]) ? pk.moves[i].id : nil) + moves2.push((pk2.moves[i]) ? pk2.moves[i].id : nil) + end + moves1.sort! + moves2.sort! + # Accept as same if moves are same and there are four moves each + return true if moves1 == moves2 && moves1[Pokemon::MAX_MOVES - 1] + same_evs = true + GameData::Stat.each_main { |s| same_evs = false if pk.ev[s.id] != pk2.ev[s.id] } + return pk.item_id == pk2.item_id && pk.nature_id == pk2.nature_id && same_evs +end + +def pbRemoveDuplicates(party) + ret = [] + for pk in party + found = false + count = 0 + firstIndex = -1 + for i in 0...ret.length + pk2 = ret[i] + if isBattlePokemonDuplicate(pk, pk2) + found = true + break + end + if pk.species == pk2.species + firstIndex = i if count == 0 + count += 1 + end + end + if !found + ret.delete_at(firstIndex) if count >= 10 + ret.push(pk) + end + end + return ret +end + +#=============================================================================== +# +#=============================================================================== +def pbGenerateChallenge(rule, tag) + oldrule = rule + yield(_INTL("Preparing to generate teams")) + rule = rule.copy.setNumber(2) + yield(nil) + party = load_data(tag + ".rxdata") rescue [] + teams = load_data(tag + "teams.rxdata") rescue [] + if teams.length < 10 + btpokemon = pbGetBTPokemon(tag) + if btpokemon && btpokemon.length != 0 + suggestedLevel = rule.ruleset.suggestedLevel + for pk in btpokemon + pkmn = pk.createPokemon(suggestedLevel, 31, nil) + party.push(pkmn) if rule.ruleset.isPokemonValid?(pkmn) + end + end + end + yield(nil) + party = pbRemoveDuplicates(party) + yield(nil) + maxteams = 600 + cutoffrating = 65 + toolowrating = 40 + iterations = 11 + iterations.times do |iter| + save_data(party, tag + ".rxdata") + yield(_INTL("Generating teams ({1} of {2})", iter + 1, iterations)) + i = 0 + while i < teams.length + yield(nil) if i % 10 == 0 + pbReplenishBattlePokemon(party, rule) + if teams[i].rating < cutoffrating && teams[i].totalGames >= 80 + teams[i] = RuledTeam.new(party, rule) + elsif teams[i].length < 2 + teams[i] = RuledTeam.new(party, rule) + elsif i >= maxteams + teams[i] = nil + teams.compact! + elsif teams[i].totalGames >= 250 + # retire + for j in 0...teams[i].length + party.push(teams[i][j]) + end + teams[i] = RuledTeam.new(party,rule) + elsif teams[i].rating < toolowrating + teams[i] = RuledTeam.new(party,rule) + end + i += 1 + end + save_data(teams, tag + "teams.rxdata") + yield(nil) + while teams.length < maxteams + yield(nil) if teams.length % 10 == 0 + pbReplenishBattlePokemon(party, rule) + teams.push(RuledTeam.new(party, rule)) + end + save_data(party, tag + ".rxdata") + teams = teams.sort { |a, b| b.rating <=> a.rating } + yield(_INTL("Simulating battles ({1} of {2})", iter + 1, iterations)) + i = 0 + loop do + changed = false + teams.length.times { |j| + yield(nil) + other = j + 5.times do + other = rand(teams.length) + next if other == j + end + next if other == j + changed = true + pbRuledBattle(teams[j], teams[other], rule) + } + i += 1 + gameCount = 0 + for team in teams + gameCount += team.games + end + yield(nil) + if gameCount / teams.length >= 12 + for team in teams + games = team.games + team.updateRating + end + break + end + end + teams.sort! { |a, b| b.rating <=> a.rating } + save_data(teams, tag + "teams.rxdata") + end + party = [] + yield(nil) + teams.sort! { |a, b| a.rating <=> b.rating } + for team in teams + next if team.rating <= cutoffrating + for i in 0...team.length + party.push(team[i]) + end + end + rule = oldrule + yield(nil) + party = pbRemoveDuplicates(party) + yield(_INTL("Writing results")) + party = pbArrangeByTier(party, rule) + yield(nil) + pbTrainerInfo(party, tag, rule) { yield(nil) } + yield(nil) +end + +#=============================================================================== +# +#=============================================================================== +def pbWriteCup(id, rules) + return if !$DEBUG + trlists = (load_data("Data/trainer_lists.dat") rescue []) + list = [] + for i in 0...trlists.length + tr = trlists[i] + if tr[5] + list.push("*" + (tr[3].sub(/\.txt$/, ""))) + else + list.push((tr[3].sub(/\.txt$/, ""))) + end + end + cmd = 0 + if trlists.length != 0 + cmd = pbMessage(_INTL("Generate Pokémon teams for this challenge?"), + [_INTL("NO"), _INTL("YES, USE EXISTING"), _INTL("YES, USE NEW")], 1) + else + cmd = pbMessage(_INTL("Generate Pokémon teams for this challenge?"), + [_INTL("YES"), _INTL("NO")], 2) + if cmd == 0 + cmd = 2 + elsif cmd == 1 + cmd = 0 + end + end + return if cmd == 0 # No + if cmd == 1 # Yes, use existing + cmd = pbMessage(_INTL("Choose a challenge."), list, -1) + if cmd >= 0 + pbMessage(_INTL("This challenge will use the Pokémon list from {1}.", list[cmd])) + for i in 0...trlists.length + tr = trlists[i] + while !tr[5] && tr[2].include?(id) + tr[2].delete(id) + end + end + trlists[cmd][2].push(id) if !trlists[cmd][5] + save_data(trlists, "Data/trainer_lists.dat") + Graphics.update + Compiler.write_trainer_lists + end + return + elsif cmd == 2 # Yes, use new + return if !pbConfirmMessage(_INTL("This may take a long time. Are you sure?")) + mw = pbCreateMessageWindow + t = Time.now + pbGenerateChallenge(rules, id) { |message| + if Time.now - t >= 5 + Graphics.update + t = Time.now + end + if message + pbMessageDisplay(mw, message, false) + Graphics.update + t = Time.now + end + } + pbDisposeMessageWindow(mw) + pbMessage(_INTL("Team generation complete.")) + end +end diff --git a/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/002_ChallengeGenerator_Pokemon.rb b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/002_ChallengeGenerator_Pokemon.rb new file mode 100644 index 000000000..0befd6e26 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/002_ChallengeGenerator_Pokemon.rb @@ -0,0 +1,366 @@ +$baseStatTotal = {} +$babySpecies = {} +$minimumLevel = {} +$evolutions = {} +$legalMoves = {} # For each species, all the moves they have access to +$legalMovesLevel = 0 # Level for which $legalMoves were calculated +$tmMoves = nil # Array of all moves teachable by a HM/TM/TR + +def pbBaseStatTotal(species) + baseStats = GameData::Species.get(species).base_stats + ret = 0 + baseStats.each { |s| ret += s } + return ret +end + +def baseStatTotal(species) + $baseStatTotal[species] = pbBaseStatTotal(species) if !$baseStatTotal[species] + return $baseStatTotal[species] +end + +def babySpecies(species) + $babySpecies[species] = GameData::Species.get(species).get_baby_species if !$babySpecies[species] + return $babySpecies[species] +end + +def minimumLevel(species) + $minimumLevel[species] = GameData::Species.get(species).minimum_level if !$minimumLevel[species] + return $minimumLevel[species] +end + +def evolutions(species) + $evolutions[species] = GameData::Species.get(species).get_evolutions(true) if !$evolutions[species] + return $evolutions[species] +end + +#=============================================================================== +# +#=============================================================================== +# Used to replace Sketch with any other move. +def pbRandomMove + keys = GameData::Move::DATA.keys + loop do + move_id = keys[rand(keys.length)] + move = GameData::Move.get(move_id) + next if move.id_number > 384 || move.id == :SKETCH || move.id == :STRUGGLE + return move.id + end +end + +def pbGetLegalMoves2(species, maxlevel) + species_data = GameData::Species.get(species) + moves = [] + return moves if !species_data + # Populate available moves array (moves) + species_data.moves.each { |m| addMove(moves, m[1], 2) if m[0] <= maxlevel } + if !$tmMoves + $tmMoves = [] + GameData::Item.each { |i| $tmMoves.push(i.move) if i.is_machine? } + end + species_data.tutor_moves.each { |m| addMove(moves, m, 0) if $tmMoves.include?(m) } + babyspecies = babySpecies(species) + GameData::Species.get(babyspecies).egg_moves.each { |m| addMove(moves, m, 2) } + # + movedatas = [] + for move in moves + movedatas.push([move, GameData::Move.get(move)]) + end + # Delete less powerful moves + deleteAll = proc { |a, item| + while a.include?(item) + a.delete(item) + end + } + for move in moves + md = GameData::Move.get(move) + for move2 in movedatas + # If we have a move that always hits, remove all other moves with no + # effect of the same type and <= base power + if md.function_code == "0A5" && move2[1].function_code == "000" && # Always hits + md.type == move2[1].type && md.base_damage >= move2[1].base_damage + deleteAll.call(moves, move2[0]) + # If we have two status moves that have the same function code, delete the + # one with lower accuracy (Supersonic vs. Confuse Ray, etc.) + elsif md.function_code == move2[1].function_code && md.base_damage == 0 && + move2[1].base_damage == 0 && md.accuracy > move2[1].accuracy + deleteAll.call(moves, move2[0]) + # Delete poison-causing moves if we have a move that causes toxic + elsif md.function_code == "006" && move2[1].function_code == "005" + deleteAll.call(moves, move2[0]) + # If we have two moves with the same function code and type, and one of + # them is damaging and has 10/15/the same PP as the other move and EITHER + # does more damage than the other move OR does the same damage but is more + # accurate, delete the other move (Surf, Flamethrower, Thunderbolt, etc.) + elsif md.function_code == move2[1].function_code && md.base_damage != 0 && + md.type == move2[1].type && + (md.total_pp == 15 || md.total_pp == 10 || md.total_pp == move2[1].total_pp) && + (md.base_damage > move2[1].base_damage || + (md.base_damage == move2[1].base_damage && md.accuracy > move2[1].accuracy)) + deleteAll.call(moves, move2[0]) + end + end + end + return moves +end + +def addMove(moves, move, base) + data = GameData::Move.get(move) + return if moves.include?(data.id) + return if [:BUBBLE, :BUBBLEBEAM].include?(data.id) # Never add these moves + count = base + 1 # Number of times to add move to moves + count = base if data.function_code == "000" && data.base_damage <= 40 + if data.base_damage <= 30 || [:GROWL, :TAILWHIP, :LEER].include?(data.id) + count = base + end + if data.base_damage >= 60 || + [:REFLECT, :LIGHTSCREEN, :SAFEGUARD, :SUBSTITUTE, :FAKEOUT].include?(data.id) + count = base + 2 + end + if data.base_damage >= 80 && data.type == :NORMAL + count = base + 3 + end + if [:PROTECT, :DETECT, :TOXIC, :AERIALACE, :WILLOWISP, :SPORE, :THUNDERWAVE, + :HYPNOSIS, :CONFUSERAY, :ENDURE, :SWORDSDANCE].include?(data.id) + count = base + 3 + end + count.times { moves.push(data.id) } +end + +# Returns whether moves contains any move with the same type as thismove but +# with a higher base damage than it. +def hasMorePowerfulMove(moves, thismove) + thisdata = GameData::Move.get(thismove) + return false if thisdata.base_damage == 0 + for move in moves + next if !move + moveData = GameData::Move.get(move) + if moveData.type == thisdata.type && moveData.base_damage > thisdata.base_damage + return true + end + end + return false +end + +#=============================================================================== +# Generate a random Pokémon that adheres to the given rules. +#=============================================================================== +def pbRandomPokemonFromRule(rules, trainer) + pkmn = nil + iteration = -1 + loop do + iteration += 1 + species = nil + level = rules.ruleset.suggestedLevel + keys = GameData::Species::DATA.keys + loop do + loop do + species = keys[rand(keys.length)] + break if GameData::Species.get(species).form == 0 + end + r = rand(20) + bst = baseStatTotal(species) + next if level < minimumLevel(species) + if iteration % 2 == 0 + next if r < 16 && bst < 400 + next if r < 13 && bst < 500 + else + next if bst > 400 + next if r < 10 && babySpecies(species) != species + end + next if r < 10 && babySpecies(species) == species + next if r < 7 && evolutions(species).length > 0 + break + end + ev = [] + GameData::Stat.each_main { |s| ev.push(s.id) if rand(100) < 50 } + nature = nil + keys = GameData::Nature::DATA.keys + loop do + nature = keys[rand(keys.length)] + nature_data = GameData::Nature.get(nature) + if [:LAX, :GENTLE].include?(nature_data.id) || nature_data.stat_changes.length == 0 + next if rand(20) < 19 + else + raised_emphasis = false + lowered_emphasis = false + nature_data.stat_changes.each do |change| + next if !ev.include?(change[0]) + raised_emphasis = true if change[1] > 0 + lowered_emphasis = true if change[1] < 0 + end + next if rand(10) < 6 && !raised_emphasis + next if rand(10) < 9 && lowered_emphasis + end + break + end + $legalMoves = {} if level != $legalMovesLevel + $legalMovesLevel = level + $legalMoves[species] = pbGetLegalMoves2(species, level) if !$legalMoves[species] + itemlist = [ + :ORANBERRY, :SITRUSBERRY, :ADAMANTORB, :BABIRIBERRY, + :BLACKSLUDGE, :BRIGHTPOWDER, :CHESTOBERRY, :CHOICEBAND, + :CHOICESCARF, :CHOICESPECS, :CHOPLEBERRY, :DAMPROCK, + :DEEPSEATOOTH, :EXPERTBELT, :FLAMEORB, :FOCUSSASH, + :FOCUSBAND, :HEATROCK, :LEFTOVERS, :LIFEORB, :LIGHTBALL, + :LIGHTCLAY, :LUMBERRY, :OCCABERRY, :PETAYABERRY, :SALACBERRY, + :SCOPELENS, :SHEDSHELL, :SHELLBELL, :SHUCABERRY, :LIECHIBERRY, + :SILKSCARF, :THICKCLUB, :TOXICORB, :WIDELENS, :YACHEBERRY, + :HABANBERRY, :SOULDEW, :PASSHOBERRY, :QUICKCLAW, :WHITEHERB + ] + # Most used: Leftovers, Life Orb, Choice Band, Choice Scarf, Focus Sash + item = nil + loop do + if rand(40) == 0 + item = :LEFTOVERS + break + end + item = itemlist[rand(itemlist.length)] + next if !item + case item + when :LIGHTBALL + next if species != :PIKACHU + when :SHEDSHELL + next if species != :FORRETRESS && species != :SKARMORY + when :SOULDEW + next if species != :LATIOS && species != :LATIAS + when :FOCUSSASH + next if baseStatTotal(species) > 450 && rand(10) < 8 + when :ADAMANTORB + next if species != :DIALGA + when :PASSHOBERRY + next if species != :STEELIX + when :BABIRIBERRY + next if species != :TYRANITAR + when :HABANBERRY + next if species != :GARCHOMP + when :OCCABERRY + next if species != :METAGROSS + when :CHOPLEBERRY + next if species != :UMBREON + when :YACHEBERRY + next if ![:TORTERRA, :GLISCOR, :DRAGONAIR].include?(species) + when :SHUCABERRY + next if species != :HEATRAN + when :DEEPSEATOOTH + next if species != :CLAMPERL + when :THICKCLUB + next if ![:CUBONE, :MAROWAK].include?(species) + when :LIECHIBERRY + ev.push(:ATTACK) if !ev.include?(:ATTACK) && rand(100) < 50 + when :SALACBERRY + ev.push(:SPEED) if !ev.include?(:SPEED) && rand(100) < 50 + when :PETAYABERRY + ev.push(:SPECIAL_ATTACK) if !ev.include?(:SPECIAL_ATTACK) && rand(100) < 50 + end + break + end + if level < 10 && GameData::Item.exists?(:ORANBERRY) + item = :ORANBERRY if rand(40) == 0 || item == :SITRUSBERRY + elsif level > 20 && GameData::Item.exists?(:SITRUSBERRY) + item = :SITRUSBERRY if rand(40) == 0 || item == :ORANBERRY + end + moves = $legalMoves[species] + sketch = false + if moves[0] == :SKETCH + sketch = true + for m in 0...Pokemon::MAX_MOVES + moves[m] = pbRandomMove + end + end + next if moves.length == 0 + if (moves | []).length < Pokemon::MAX_MOVES + moves = [:TACKLE] if moves.length == 0 + moves |= [] + else + newmoves = [] + rest = GameData::Move.exists?(:REST) ? :REST : nil + spitup = GameData::Move.exists?(:SPITUP) ? :SPITUP : nil + swallow = GameData::Move.exists?(:SWALLOW) ? :SWALLOW : nil + stockpile = GameData::Move.exists?(:STOCKPILE) ? :STOCKPILE : nil + snore = GameData::Move.exists?(:SNORE) ? :SNORE : nil + sleeptalk = GameData::Move.exists?(:SLEEPTALK) ? :SLEEPTALK : nil + loop do + newmoves.clear + while newmoves.length < [moves.length, Pokemon::MAX_MOVES].min + m = moves[rand(moves.length)] + next if rand(100) < 50 && hasMorePowerfulMove(moves, m) + newmoves.push(m) if m && !newmoves.include?(m) + end + if (newmoves.include?(spitup) || newmoves.include?(swallow)) && + !newmoves.include?(stockpile) + next unless sketch + end + if (!newmoves.include?(spitup) && !newmoves.include?(swallow)) && + newmoves.include?(stockpile) + next unless sketch + end + if newmoves.include?(sleeptalk) && !newmoves.include?(rest) + next unless (sketch || !moves.include?(rest)) && rand(100) < 20 + end + if newmoves.include?(snore) && !newmoves.include?(rest) + next unless (sketch || !moves.include?(rest)) && rand(100) < 20 + end + totalbasedamage = 0 + hasPhysical = false + hasSpecial = false + hasNormal = false + for move in newmoves + d = GameData::Move.get(move) + if d.base_damage >= 1 + totalbasedamage += d.base_damage + hasNormal = true if d.type == :NORMAL + hasPhysical = true if d.category == 0 + hasSpecial = true if d.category == 1 + end + end + if !hasPhysical && ev.include?(:ATTACK) + # No physical attack, but emphasizes Attack + next if rand(100) < 80 + end + if !hasSpecial && ev.include?(:SPECIAL_ATTACK) + # No special attack, but emphasizes Special Attack + next if rand(100) < 80 + end + r = rand(10) + next if r > 6 && totalbasedamage > 180 + next if r > 8 && totalbasedamage > 140 + next if totalbasedamage == 0 && rand(100) < 95 + ############ + # Moves accepted + if hasPhysical && !hasSpecial + ev.push(:ATTACK) if rand(100) < 80 + ev.delete(:SPECIAL_ATTACK) if rand(100) < 80 + end + if !hasPhysical && hasSpecial + ev.delete(:ATTACK) if rand(100) < 80 + ev.push(:SPECIAL_ATTACK) if rand(100) < 80 + end + item = :LEFTOVERS if !hasNormal && item == :SILKSCARF + moves = newmoves + break + end + end + if item == :LIGHTCLAY && !moves.any? { |m| m == :LIGHTSCREEN || m = :REFLECT } + item = :LEFTOVERS + end + if item == :BLACKSLUDGE + type1 = GameData::Species.get(species).type1 + type2 = GameData::Species.get(species).type2 || type1 + item = :LEFTOVERS if type1 != :POISON && type2 != :POISON + end + if item == :HEATROCK && !moves.any? { |m| m == :SUNNYDAY } + item = :LEFTOVERS + end + if item == :DAMPROCK && !moves.any? { |m| m == :RAINDANCE } + item = :LEFTOVERS + end + if moves.any? { |m| m == :REST } + item = :LUMBERRY if rand(100) < 33 + item = :CHESTOBERRY if rand(100) < 25 + end + pk = PBPokemon.new(species, item, nature, moves[0], moves[1], moves[2], moves[3], ev) + pkmn = pk.createPokemon(level, 31, trainer) + break if rules.ruleset.isPokemonValid?(pkmn) + end + return pkmn +end diff --git a/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/003_ChallengeGenerator_Trainers.rb b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/003_ChallengeGenerator_Trainers.rb new file mode 100644 index 000000000..bea2210b5 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/003_ChallengeGenerator_Trainers.rb @@ -0,0 +1,215 @@ +#=============================================================================== +# +#=============================================================================== +def getTypes(species) + species_data = GameData::Species.get(species) + type1 = species_data.type1 + type2 = species_data.type2 + return (type1 == type2) ? [type1] : [type1, type2] +end + +#=============================================================================== +# If no trainers are defined for the current challenge, generate a set of random +# ones for it. If pokemonlist is given, assign Pokémon from it to all trainers. +# Save the results in the appropriate PBS files. +#=============================================================================== +def pbTrainerInfo(pokemonlist, trfile, rules) + bttrainers = pbGetBTTrainers(trfile) + btpokemon = pbGetBTPokemon(trfile) + # No battle trainers found; fill bttrainers with 200 randomly chosen ones from + # all that exist (with a base money < 100) + if bttrainers.length == 0 + for i in 0...200 + yield(nil) if block_given? && i % 50 == 0 + trainerid = nil + if GameData::TrainerType.exists?(:YOUNGSTER) && rand(30) == 0 + trainerid = :YOUNGSTER + else + tr_typekeys = GameData::TrainerType::DATA.keys + loop do + tr_type = tr_typekeys[rand(tr_typekeys.length)] + tr_type_data = GameData::TrainerType.get(tr_type) + next if tr_type_data.base_money >= 100 + trainerid = tr_type_data.id + end + end + # Create a random name for the trainer + gender = GameData::TrainerType.get(trainerid).gender + randomName = getRandomNameEx(gender, nil, 0, 12) + # Add the trainer to bttrainers + tr = [trainerid, randomName, _INTL("Here I come!"), _INTL("Yes, I won!"), + _INTL("Man, I lost!"), []] + bttrainers.push(tr) + end + # Sort all the randomly chosen trainers by their base money (smallest first) + bttrainers.sort! { |a, b| + money1 = GameData::TrainerType.get(a[0]).base_money + money2 = GameData::TrainerType.get(b[0]).base_money + next (money1 == money2) ? a[0].to_s <=> b[0].to_s : money1 <=> money2 + } + end + yield(nil) if block_given? + # Set all Pokémon in pokemonlist to the appropriate level, and determine their + # type(s) and whether they are valid for the given rules + suggestedLevel = rules.ruleset.suggestedLevel + rulesetTeam = rules.ruleset.copy.clearPokemonRules + pkmntypes = [] + validities = [] + for pkmn in pokemonlist + pkmn.level = suggestedLevel if pkmn.level != suggestedLevel + pkmntypes.push(getTypes(pkmn.species)) + validities.push(rules.ruleset.isPokemonValid?(pkmn)) + end + # For each trainer in bttrainers, come up with a set of Pokémon taken from + # pokemonlist for that trainer, and copy the trainer and their set of Pokémon + # to newbttrainers + newbttrainers = [] + for btt in 0...bttrainers.length + yield(nil) if block_given? && btt % 50 == 0 + trainerdata = bttrainers[btt] + pokemonnumbers = trainerdata[5] || [] + # Find all the Pokémon available to the trainer, and count up how often + # those Pokémon have each type + species = [] + types = {} + GameData::Type.each { |t| types[t.id] = 0 } + for pn in pokemonnumbers + pkmn = btpokemon[pn] + species.push(pkmn.species) + t = getTypes(pkmn.species) + t.each { |typ| types[typ] += 1 } + end + species |= [] # remove duplicates + # Scale down the counts of each type to the range 0 -> 10 + count = 0 + GameData::Type.each do |t| + if types[t.id] >= 5 + types[t.id] /= 4 + types[t.id] = 10 if types[t.id] > 10 + else + types[t.id] = 0 + end + count += types[t.id] + end + types[:NORMAL] = 1 if count == 0 # All type counts are 0; add 1 to Normal + # Trainer had no Pokémon available to it; make all the type counts 1 + if pokemonnumbers.length == 0 + GameData::Type.each { |t| types[t.id] = 1 } + end + # Get Pokémon from pokemonlist, if there are any, and make sure enough are + # gotten that a valid team can be made from them + numbers = [] + if pokemonlist + # For each valid Pokémon in pokemonlist, add its index within pokemonlist + # to numbers, but only if that species is available to the trainer. + # Pokémon are less likely to be added if it is positioned later in + # pokemonlist, or if the trainer is positioned earlier in bttrainers (i.e. + # later trainers get better Pokémon). + numbersPokemon = [] + for index in 0...pokemonlist.length + next if !validities[index] + pkmn = pokemonlist[index] + absDiff = ((index * 8 / pokemonlist.length) - (btt * 8 / bttrainers.length)).abs + if species.include?(pkmn.species) + weight = [32, 12, 5, 2, 1, 0, 0, 0][[absDiff, 7].min] + if rand(40) < weight + numbers.push(index) + numbersPokemon.push(pokemonlist[index]) + end + else + # Pokémon's species isn't available to the trainer; try adding it + # anyway (more likely to add it if the trainer has access to more + # Pokémon of the same type(s) as this Pokémon) + t = pkmntypes[index] + t.each { |typ| + weight = [32, 12, 5, 2, 1, 0, 0, 0][[absDiff, 7].min] + weight *= types[typ] + if rand(40) < weight + numbers.push(index) + numbersPokemon.push(pokemonlist[index]) + end + } + end + end + numbers |= [] # Remove duplicates + # If there aren't enough Pokémon to form a full team, or a valid team + # can't be made from them, fill up numbers with Pokémon in pokemonlist + # that EITHER have the same species as one available to the trainer OR has + # a type that is available to the trainer, until a valid team can be + # formed from what's in numbers + if numbers.length < Settings::MAX_PARTY_SIZE || + !rulesetTeam.hasValidTeam?(numbersPokemon) + for index in 0...pokemonlist.length + pkmn = pokemonlist[index] + next if !validities[index] + if species.include?(pkmn.species) + numbers.push(index) + numbersPokemon.push(pokemonlist[index]) + else + t = pkmntypes[index] + t.each { |typ| + if types[typ] > 0 && !numbers.include?(index) + numbers.push(index) + numbersPokemon.push(pokemonlist[index]) + break + end + } + end + break if numbers.length >= Settings::MAX_PARTY_SIZE && rules.ruleset.hasValidTeam?(numbersPokemon) + end + # If there STILL aren't enough Pokémon to form a full team, or a valid + # team can't be made from them, add random Pokémon from pokemonlist + # until a valid team can be formed from what's in numbers + if numbers.length < Settings::MAX_PARTY_SIZE || !rules.ruleset.hasValidTeam?(numbersPokemon) + while numbers.length < pokemonlist.length && + (numbers.length < Settings::MAX_PARTY_SIZE || !rules.ruleset.hasValidTeam?(numbersPokemon)) + index = rand(pokemonlist.length) + if !numbers.include?(index) + numbers.push(index) + numbersPokemon.push(pokemonlist[index]) + end + end + end + end + numbers.sort! + end + # Add the trainer's data, including all Pokémon that should be available to + # it (from pokemonlist), to newbttrainers + newbttrainers.push([trainerdata[0], trainerdata[1], trainerdata[2], + trainerdata[3], trainerdata[4], numbers]) + end + yield(nil) if block_given? + # Add the trainer and Pokémon data from above to trainer_lists.dat, and then + # create all PBS files from it + pbpokemonlist = [] + for pkmn in pokemonlist + pbpokemonlist.push(PBPokemon.fromPokemon(pkmn)) + end + trlists = (load_data("Data/trainer_lists.dat") rescue []) + hasDefault = false + trIndex = -1 + for i in 0...trlists.length + next if !trlists[i][5] + hasDefault = true + break + end + for i in 0...trlists.length + if trlists[i][2].include?(trfile) + trIndex = i + trlists[i][0] = newbttrainers + trlists[i][1] = pbpokemonlist + trlists[i][5] = !hasDefault + end + end + yield(nil) if block_given? + if trIndex < 0 + info = [newbttrainers, pbpokemonlist, [trfile], + trfile + "tr.txt", trfile + "pm.txt", !hasDefault] + trlists.push(info) + end + yield(nil) if block_given? + save_data(trlists, "Data/trainer_lists.dat") + yield(nil) if block_given? + Compiler.write_trainer_lists + yield(nil) if block_given? +end diff --git a/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/004_ChallengeGenerator_BattleSim.rb b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/004_ChallengeGenerator_BattleSim.rb new file mode 100644 index 000000000..254a49fc1 --- /dev/null +++ b/Data/Scripts/018_Alternate battle modes/003_Battle Frontier generator/004_ChallengeGenerator_BattleSim.rb @@ -0,0 +1,433 @@ +#=============================================================================== +# +#=============================================================================== +class RuledTeam + attr_accessor :team + + def initialize(party, rule) + count = rule.ruleset.suggestedNumber + @team = [] + retnum = [] + loop do + for i in 0...count + retnum[i] = rand(party.length) + @team[i] = party[retnum[i]] + party.delete_at(retnum[i]) + end + break if rule.ruleset.isValid?(@team) + end + @totalGames = 0 + @rating = PlayerRating.new + @history = MatchHistory.new(@rating) + end + + def [](i) + @team[i] + end + + def length + return @team.length + end + + def rating + @rating.winChancePercent + end + + def ratingData + @rating + end + + def ratingRaw + [@rating.rating, @rating.deviation, @rating.volatility, @rating.winChancePercent] + end + + def compare(other) + @rating.compare(other.ratingData) + end + + def totalGames + (@totalGames || 0) + self.games + end + + def addMatch(other,score) + @history.addMatch(other.ratingData, score) + end + + def games + @history.length + end + + def updateRating + @totalGames = 0 if !@totalGames + oldgames = self.games + @history.updateAndClear() + newgames = self.games + @totalGames += (oldgames - newgames) + end + + def toStr + return "[" + @rating.to_i.to_s + "," + @games.to_i.to_s + "]" + end + + def load(party) + ret = [] + for i in 0...team.length + ret.push(party[team[i]]) + end + return ret + end +end + +#=============================================================================== +# +#=============================================================================== +class SingleMatch + attr_reader :opponentRating + attr_reader :opponentDeviation + attr_reader :score + attr_reader :kValue + + def initialize(opponentRating, opponentDev, score, kValue = 16) + @opponentRating = opponentRating + @opponentDeviation = opponentDev + @score = score # -1=draw, 0=lose, 1=win + @kValue = kValue + end +end + +#=============================================================================== +# +#=============================================================================== +class MatchHistory + include Enumerable + + def initialize(thisPlayer) + @matches = [] + @thisPlayer = thisPlayer + end + + def [](i) + @matches[i] + end + + def length + @matches.length + end + + def each + @matches.each { |item| yield item } + end + + def addMatch(otherPlayer, result) + # 1=I won; 0=Other player won; -1: Draw + @matches.push(SingleMatch.new(otherPlayer.rating, otherPlayer.deviation, result)) + end + + def updateAndClear + @thisPlayer.update(@matches) + @matches.clear + end +end + +#=============================================================================== +# +#=============================================================================== +class PlayerRatingElo + attr_reader :rating + K_VALUE = 16 + + def initialize + @rating = 1600.0 + @deviation = 0 + @volatility = 0 + @estimatedRating = nil + end + + def winChancePercent + return @estimatedRating if @estimatedRating + x = (1 + 10.0**((@rating - 1600.0) / 400.0)) + @estimatedRating = (x == 0 ? 1.0 : 1.0 / x) + return @estimatedRating + end + + def update(matches) + return if matches.length == 0 + stake = 0 + matches.length.times do + score = (match.score == -1) ? 0.5 : match.score + e = (1 + 10.0**((@rating - match.opponentRating) / 400.0)) + stake += match.kValue * (score - e) + end + @rating += stake + end +end + +#=============================================================================== +# +#=============================================================================== +class PlayerRating + attr_reader :volatility + attr_reader :deviation + attr_reader :rating + + def initialize + @rating = 1500.0 + @deviation = 350.0 + @volatility = 0.9 + @estimatedRating = nil + end + + def winChancePercent + return @estimatedRating if @estimatedRating + if @deviation > 100 + # https://www.smogon.com/forums/threads/make-sense-of-your-shoddy-battle-rating.55764/ + otherRating = 1500.0 + otherDeviation = 350.0 + s = Math.sqrt(100000.0 + @deviation * @deviation + otherDeviation * otherDeviation) + g = 10.0**((otherRating - @rating) * 0.79 / s) + @estimatedRating = (1.0 / (1.0 + g)) * 100.0 # Percent chance that I win against opponent + else + # GLIXARE method + rds = @deviation * @deviation + sqr = Math.sqrt(15.905694331435 * (rds + 221781.21786254)) + inner = (1500.0 - @rating) * Math::PI / sqr + @estimatedRating = (10000.0 / (1.0 + (10.0**inner)) + 0.5) / 100.0 + end + return @estimatedRating + end + + def update(matches, system = 1.2) + volatility = volatility2 + deviation = deviation2 + rating = rating2 + if matches.length == 0 + setDeviation2(Math.sqrt(deviation * deviation + volatility * volatility)) + return + end + g = [] + e = [] + score = [] + for i in 0...matches.length + match = matches[i] + g[i] = getGFactor(match.opponentDeviation) + e[i] = getEFactor(rating, match.opponentRating, g[i]) + score[i] = match.score + end + # Estimated variance + variance = 0.0 + for i in 0...matches.length + variance += g[i] * g[i] * e[i] * (1 - e[i]) + end + variance = 1.0 / variance + # Improvement sum + sum = 0.0 + for i in 0...matches.length + v = score[i] + sum += g[i] * (v.to_f - e[i]) if v != -1 + end + volatility = getUpdatedVolatility(volatility, deviation, variance, sum, system) + # Update deviation + t = deviation * deviation + volatility * volatility + deviation = 1.0 / Math.sqrt(1.0 / t + 1.0 / variance) + # Update rating + rating = rating + deviation * deviation * sum + setRating2(rating) + setDeviation2(deviation) + setVolatility2(volatility) + end + + private + + attr_writer :volatility + + alias volatility2 volatility + + def rating2 + return (@rating - 1500.0) / 173.7178 + end + + def deviation2 + return @deviation / 173.7178 + end + + def getGFactor(deviation) + # deviation is not yet in glicko2 + deviation /= 173.7178 + return 1.0 / Math.sqrt(1.0 + (3.0 * deviation * deviation) / (Math::PI * Math::PI)) + end + + def getEFactor(rating, opponentRating, g) + # rating is already in glicko2 + # opponentRating is not yet in glicko2 + opponentRating = (opponentRating - 1500.0) / 173.7178 + return 1.0 / (1.0 + Math.exp(-g * (rating - opponentRating))) + end + + def setVolatility2(value) + @volatility = value + end + + def setRating2(value) + @estimatedRating = nil + @rating = (value * 173.7178) + 1500.0 + end + + def setDeviation2(value) + @estimatedRating = nil + @deviation = value * 173.7178 + end + + def getUpdatedVolatility(volatility, deviation, variance, improvementSum, system) + improvement = improvementSum * variance + a = Math.log(volatility * volatility) + squSystem = system * system + squDeviation = deviation * deviation + squVariance = variance + variance + squDevplusVar = squDeviation + variance + x0 = a + 100.times { # Up to 100 iterations to avoid potentially infinite loops + e = Math.exp(x0) + d = squDevplusVar + e + squD = d * d + i = improvement / d + h1 = -(x0 - a) / squSystem - 0.5 * e * i * i + h2 = -1.0 / squSystem - 0.5 * e * squDevplusVar / squD + h2 += 0.5 * squVariance * e * (squDevplusVar - e) / (squD * d) + x1 = x0 + x0 -= h1 / h2 + break if ((x1 - x0).abs < 0.000001) + } + return Math.exp(x0 / 2.0) + end +end + +#=============================================================================== +# +#=============================================================================== +def pbDecideWinnerEffectiveness(move, otype1, otype2, ability, scores) + data = GameData::Move.get(move) + return 0 if data.base_damage == 0 + atype = data.type + typemod = Effectiveness::NORMAL_EFFECTIVE_ONE ** 2 + if ability != :LEVITATE || data.type != :GROUND + mod1 = Effectiveness.calculate_one(atype, otype1) + mod2 = (otype1 == otype2) ? Effectiveness::NORMAL_EFFECTIVE_ONE : Effectiveness.calculate_one(atype, otype2) + if ability == :WONDERGUARD + mod1 = Effectiveness::NORMAL_EFFECTIVE_ONE if mod1 <= Effectiveness::NORMAL_EFFECTIVE_ONE + mod2 = Effectiveness::NORMAL_EFFECTIVE_ONE if mod2 <= Effectiveness::NORMAL_EFFECTIVE_ONE + end + typemod = mod1 * mod2 + end + return scores[0] if typemod == 0 # Ineffective + return scores[1] if typemod == 1 # Doubly not very effective + return scores[2] if typemod == 2 # Not very effective + return scores[3] if typemod == 4 # Normal effective + return scores[4] if typemod == 8 # Super effective + return scores[5] if typemod == 16 # Doubly super effective + return 0 +end + +def pbDecideWinnerScore(party0, party1, rating) + score = 0 + types1 = [] + types2 = [] + abilities = [] + for j in 0...party1.length + types1.push(party1[j].type1) + types2.push(party1[j].type2) + abilities.push(party1[j].ability_id) + end + for i in 0...party0.length + for move in party0[i].moves + next if !move + for j in 0...party1.length + score += pbDecideWinnerEffectiveness(move.id, + types1[j], types2[j], abilities[j], [-16, -8, 0, 4, 12, 20]) + end + end + basestatsum = baseStatTotal(party0[i].species) + score += basestatsum / 10 + score += 10 if party0[i].item # Not in Battle Dome ranking + end + score += rating + rand(32) + return score +end + +def pbDecideWinner(party0, party1, rating0, rating1) + rating0 = (rating0 * 15.0 / 100).round + rating1 = (rating1 * 15.0 / 100).round + score0 = pbDecideWinnerScore(party0, party1, rating0) + score1 = pbDecideWinnerScore(party1, party0, rating1) + if score0 == score1 + return 5 if rating0 == rating1 + return (rating0 > rating1) ? 1 : 2 + else + return (score0 > score1) ? 1 : 2 + end +end + +#=============================================================================== +# +#=============================================================================== +def pbRuledBattle(team1, team2, rule) + decision = 0 + if rand(100) != 0 + party1 = [] + party2 = [] + team1.length.times { |i| party1.push(team1[i]) } + team2.length.times { |i| party2.push(team2[i]) } + decision = pbDecideWinner(party1, party2, team1.rating, team2.rating) + else + level = rule.ruleset.suggestedLevel + t_type = nil + GameData::TrainerType.each { |t| t_type = t.id; break } + trainer1 = NPCTrainer.new("PLAYER1", t_type) + trainer2 = NPCTrainer.new("PLAYER2", t_type) + items1 = [] + items2 = [] + team1.each_with_index do |p, i| + next if !p + if p.level != level + p.level = level + p.calc_stats + end + items1[i] = p.item_id + trainer1.party.push(p) + end + team2.each_with_index do |p, i| + next if !p + if p.level != level + p.level = level + p.calc_stats + end + items2[i] = p.item_id + trainer2.party.push(p) + end + scene = PokeBattle_DebugSceneNoLogging.new + battle = rule.createBattle(scene, trainer1, trainer2) + battle.debug = true + battle.controlPlayer = true + battle.internalBattle = false + decision = battle.pbStartBattle + team1.each_with_index do |p, i| + next if !p + p.heal + p.item = items1[i] + end + team2.each_with_index do |p, i| + next if !p + p.heal + p.item = items2[i] + end + end + if decision == 1 # Team 1 wins + team1.addMatch(team2, 1) + team2.addMatch(team1, 0) + elsif decision == 2 # Team 2 wins + team1.addMatch(team2, 0) + team2.addMatch(team1, 1) + else + team1.addMatch(team2, -1) + team2.addMatch(team1, -1) + end +end diff --git a/Data/Scripts/018_Alternate battle modes/003_OrgBattle.rb b/Data/Scripts/018_Alternate battle modes/003_OrgBattle.rb deleted file mode 100644 index d18ccc325..000000000 --- a/Data/Scripts/018_Alternate battle modes/003_OrgBattle.rb +++ /dev/null @@ -1,973 +0,0 @@ -#=============================================================================== -# Pokémon Organized Battle -#=============================================================================== -def pbHasEligible?(*arg) - return pbBattleChallenge.rules.ruleset.hasValidTeam?($Trainer.party) -end - - - -class PBPokemon - attr_accessor :species - attr_accessor :item - attr_accessor :nature - attr_accessor :move1 - attr_accessor :move2 - attr_accessor :move3 - attr_accessor :move4 - attr_accessor :ev - - def initialize(species,item,nature,move1,move2,move3,move4,ev) - @species = species - itm = GameData::Item.try_get(item) - @item = itm ? itm.id : nil - @nature = nature - @move1 = move1 ? move1 : nil - @move2 = move2 ? move2 : nil - @move3 = move3 ? move3 : nil - @move4 = move4 ? move4 : nil - @ev = ev - end - - # This method is how each Pokémon is compiled from the PBS files listing - # Battle Tower/Cup Pokémon. - def self.fromInspected(str) - insp=str.gsub(/^\s+/,"").gsub(/\s+$/,"") - pieces=insp.split(/\s*;\s*/) - species = (GameData::Species.exists?(pieces[0])) ? GameData::Species.get(pieces[0]).id : nil - item = (GameData::Item.exists?(pieces[1])) ? GameData::Item.get(pieces[1]).id : nil - nature = (GameData::Nature.exists?(pieces[2])) ? GameData::Nature.get(pieces[2]).id : nil - ev = pieces[3].split(/\s*,\s*/) - ev_array = [] - ev.each do |stat| - case stat.upcase - when "HP" then ev_array.push(:HP) - when "ATK" then ev_array.push(:ATTACK) - when "DEF" then ev_array.push(:DEFENSE) - when "SA", "SPATK" then ev_array.push(:SPECIAL_ATTACK) - when "SD", "SPDEF" then ev_array.push(:SPECIAL_DEFENSE) - when "SPD" then ev_array.push(:SPEED) - end - end - moves=pieces[4].split(/\s*,\s*/) - moveid=[] - for i in 0...Pokemon::MAX_MOVES - move_data = GameData::Move.try_get(moves[i]) - moveid.push(move_data.id) if move_data - end - if moveid.length==0 - GameData::Move.each { |mov| moveid.push(mov.id); break } - end - return self.new(species, item, nature, moveid[0], moveid[1], moveid[2], moveid[3], ev_array) - end - - def self.fromPokemon(pokemon) - mov1 = (pokemon.moves[0]) ? pokemon.moves[0].id : nil - mov2 = (pokemon.moves[1]) ? pokemon.moves[1].id : nil - mov3 = (pokemon.moves[2]) ? pokemon.moves[2].id : nil - mov4 = (pokemon.moves[3]) ? pokemon.moves[3].id : nil - ev_array = [] - GameData::Stat.each_main do |s| - ev_array.push(s.id) if pokemon.ev[s.id] > 60 - end - return self.new(pokemon.species,pokemon.item_id,pokemon.nature, - mov1,mov2,mov3,mov4,ev_array) - end - - # Unused. - def self.constFromStr(mod,str) - maxconst=0 - for constant in mod.constants - maxconst=[maxconst,mod.const_get(constant.to_sym)].max - end - for i in 1..maxconst - val=mod.getName(i) - next if nil_or_empty?(val) - return i if val==str - end - return 0 - end - - # Unused. - def self.fromString(str) - return self.fromstring(str) - end - - # Unused. - def self.fromstring(str) - s=str.split(/\s*,\s*/) - species=GameData::Species.get(s[1]).id - item=s[2].to_sym - nature=GameData::Nature.get(s[3]).id - move1=GameData::Move.get(s[4]).id - move2=(s.length>=12) ? GameData::Move.get(s[5]).id : nil - move3=(s.length>=13) ? GameData::Move.get(s[6]).id : nil - move4=(s.length>=14) ? GameData::Move.get(s[7]).id : nil - slen = s.length - 6 - ev_array = [] - GameData::Stat.each_main do |s| - ev_array.push(s.id) if s[slen + s.pbs_order].to_i > 0 - end - return self.new(species,item,nature,move1,move2,move3,move4,ev_array) - end - -=begin - def _dump(depth) - return [@species,@item,@nature,@move1,@move2, - @move3,@move4,@ev].pack("vvCvvvvC") - end - - def self._load(str) - data=str.unpack("vvCvvvvC") - return self.new( - data[0],data[1],data[2],data[3], - data[4],data[5],data[6],data[7] - ) - end -=end - - def inspect - c1=GameData::Species.get(@species).id.to_s - c2=(@item) ? GameData::Item.get(@item).id.to_s : "" - c3=(@nature) ? GameData::Nature.get(@nature).id.to_s : "" - evlist = "" - @ev.each do |stat| - evlist += "," if evlist != "" - evlist += stat.real_name_brief - end - c4=(@move1) ? GameData::Move.get(@move1).id_to_s : "" - c5=(@move2) ? GameData::Move.get(@move2).id_to_s : "" - c6=(@move3) ? GameData::Move.get(@move3).id_to_s : "" - c7=(@move4) ? GameData::Move.get(@move4).id_to_s : "" - return "#{c1};#{c2};#{c3};#{evlist};#{c4},#{c5},#{c6},#{c7}" - end - - # Unused. - def tocompact - return "#{species},#{item},#{nature},#{move1},#{move2},#{move3},#{move4},#{ev}" - end - - def convertMove(move) - move = :FRUSTRATION if move == :RETURN && GameData::Move.exists?(:FRUSTRATION) - return move - end - - def createPokemon(level,iv,trainer) - pokemon=Pokemon.new(@species,level,trainer,false) - pokemon.item = @item - pokemon.personalID = rand(2**16) | rand(2**16) << 16 - pokemon.nature = nature - pokemon.happiness=0 - pokemon.moves[0] = Pokemon::Move.new(self.convertMove(@move1)) - pokemon.moves[1] = (@move2) ? Pokemon::Move.new(self.convertMove(@move2)) : nil - pokemon.moves[2] = (@move3) ? Pokemon::Move.new(self.convertMove(@move3)) : nil - pokemon.moves[3] = (@move4) ? Pokemon::Move.new(self.convertMove(@move4)) : nil - pokemon.moves.compact! - if ev.length > 0 - ev.each { |stat| pokemon.ev[stat] = Pokemon::EV_LIMIT / ev.length } - end - GameData::Stat.each_main { |s| pokemon.iv[s.id] = iv } - pokemon.calc_stats - return pokemon - end -end - - - -def pbGetBTTrainers(challengeID) - trlists=(load_data("Data/trainer_lists.dat") rescue []) - for i in 0...trlists.length - tr=trlists[i] - if !tr[5] && tr[2].include?(challengeID) - return tr[0] - end - end - for i in 0...trlists.length - tr=trlists[i] - if tr[5] # is default list - return tr[0] - end - end - return [] -end - -def pbGetBTPokemon(challengeID) - trlists=(load_data("Data/trainer_lists.dat") rescue []) - for tr in trlists - if !tr[5] && tr[2].include?(challengeID) - return tr[1] - end - end - for tr in trlists - if tr[5] # is default list - return tr[1] - end - end - return [] -end - - - -class Game_Player < Game_Character - attr_accessor :direction - - def moveto2(x, y) - @x = x - @y = y - @real_x = @x * 128 - @real_y = @y * 128 - @prelock_direction = 0 - end -end - - - -class BattleChallengeType - attr_accessor :currentWins - attr_accessor :previousWins - attr_accessor :maxWins - attr_accessor :currentSwaps - attr_accessor :previousSwaps - attr_accessor :maxSwaps - attr_reader :doublebattle - attr_reader :numPokemon - attr_reader :battletype - attr_reader :mode - - def initialize - @previousWins=0 - @maxWins=0 - @currentWins=0 - @currentSwaps=0 - @previousSwaps=0 - @maxSwaps=0 - end - - def saveWins(challenge) - if challenge.decision==0 # if undecided - @currentWins=0 - @currentSwaps=0 - else - if challenge.decision==1 # if won - @currentWins=challenge.wins - @currentSwaps=challenge.swaps - else # if lost - @currentWins=0 - @currentSwaps=0 - end - @maxWins=[@maxWins,challenge.wins].max - @previousWins=challenge.wins - @maxSwaps=[@maxSwaps,challenge.swaps].max - @previousSwaps=challenge.swaps - end - end -end - - - -class BattleChallengeData - attr_reader :resting - attr_reader :wins - attr_reader :swaps - attr_reader :inProgress - attr_reader :battleNumber - attr_reader :numRounds - attr_accessor :decision - attr_reader :party - attr_reader :extraData - - def setExtraData(value) - @extraData=value - end - - def pbAddWin - if @inProgress - @battleNumber+=1 - @wins+=1 - end - end - - def pbAddSwap - if @inProgress - @swaps+=1 - end - end - - def pbMatchOver? - return true if !@inProgress - return true if @decision!=0 - return (@battleNumber>@numRounds) - end - - def initialize - reset - end - - def nextTrainer - return @trainers[@battleNumber-1] - end - - def pbGoToStart - if $scene.is_a?(Scene_Map) - $game_temp.player_transferring = true - $game_temp.player_new_map_id = @start[0] - $game_temp.player_new_x = @start[1] - $game_temp.player_new_y = @start[2] - $game_temp.player_new_direction = 8 - $scene.transfer_player - end - end - - def setParty(value) - if @inProgress - $Trainer.party=value - @party=value - else - @party=value - end - end - - def pbStart(t,numRounds) - @inProgress=true - @resting=false - @decision=0 - @swaps=t.currentSwaps - @wins=t.currentWins - @battleNumber=1 - @trainers=[] - raise _INTL("Number of rounds is 0 or less.") if numRounds<=0 - @numRounds=numRounds - btTrainers=pbGetBTTrainers(pbBattleChallenge.currentChallenge) - while @trainers.length<@numRounds - newtrainer=pbBattleChallengeTrainer(@wins+@trainers.length,btTrainers) - found=false - for tr in @trainers - found=true if tr==newtrainer - end - @trainers.push(newtrainer) if !found - end - @start=[$game_map.map_id,$game_player.x,$game_player.y] - @oldParty=$Trainer.party - $Trainer.party=@party if @party - Game.save(safe: true) - end - - def pbCancel - $Trainer.party=@oldParty if @oldParty - reset - end - - def pbEnd - $Trainer.party=@oldParty - return if !@inProgress - save=(@decision!=0) - reset - $game_map.need_refresh=true - Game.save(safe: true) if save - end - - def pbGoOn - return if !@inProgress - @resting=false - pbSaveInProgress - end - - def pbRest - return if !@inProgress - @resting=true - pbSaveInProgress - end - - private - - def reset - @inProgress=false - @resting=false - @start=nil - @decision=0 - @wins=0 - @swaps=0 - @battleNumber=0 - @trainers=[] - @oldParty=nil - @party=nil - @extraData=nil - end - - def pbSaveInProgress - oldmapid=$game_map.map_id - oldx=$game_player.x - oldy=$game_player.y - olddirection=$game_player.direction - $game_map.map_id=@start[0] - $game_player.moveto2(@start[1],@start[2]) - $game_player.direction=8 # facing up - Game.save(safe: true) - $game_map.map_id=oldmapid - $game_player.moveto2(oldx,oldy) - $game_player.direction=olddirection - end -end - - - -class BattleChallenge - attr_reader :currentChallenge - BattleTowerID = 0 - BattlePalaceID = 1 - BattleArenaID = 2 - BattleFactoryID = 3 - - def initialize - @bc=BattleChallengeData.new - @currentChallenge=-1 - @types={} - end - - def rules - if !@rules - @rules=modeToRules(self.data.doublebattle, - self.data.numPokemon, - self.data.battletype,self.data.mode) - end - return @rules - end - - def modeToRules(doublebattle,numPokemon,battletype,mode) - rules=PokemonChallengeRules.new - if battletype==BattlePalaceID - rules.setBattleType(BattlePalace.new) - elsif battletype==BattleArenaID - rules.setBattleType(BattleArena.new) - doublebattle=false - else - rules.setBattleType(BattleTower.new) - end - if mode==1 # Open Level - rules.setRuleset(StandardRules(numPokemon,GameData::GrowthRate.max_level)) - rules.setLevelAdjustment(OpenLevelAdjustment.new(30)) - elsif mode==2 # Battle Tent - rules.setRuleset(StandardRules(numPokemon,GameData::GrowthRate.max_level)) - rules.setLevelAdjustment(OpenLevelAdjustment.new(60)) - else - rules.setRuleset(StandardRules(numPokemon,50)) - rules.setLevelAdjustment(OpenLevelAdjustment.new(50)) - end - if doublebattle - rules.addBattleRule(DoubleBattle.new) - else - rules.addBattleRule(SingleBattle.new) - end - return rules - end - - def set(id,numrounds,rules) - @id=id - @numRounds=numrounds - @rules=rules - pbWriteCup(id,rules) - end - - def start(*args) - t = ensureType(@id) - @currentChallenge=@id # must appear before pbStart - @bc.pbStart(t,@numRounds) - end - - def register(id,doublebattle,numrounds,numPokemon,battletype,mode=1) - ensureType(id) - if battletype==BattleFactoryID - @bc.setExtraData(BattleFactoryData.new(@bc)) - numPokemon=3 - battletype=BattleTowerID - end - @numRounds=numrounds - @rules=modeToRules(doublebattle,numPokemon,battletype,mode) - end - - def pbInChallenge? - return pbInProgress? - end - - def data - return nil if !pbInProgress? || @currentChallenge<0 - return ensureType(@currentChallenge).clone - end - - def getCurrentWins(challenge) - return ensureType(challenge).currentWins - end - - def getPreviousWins(challenge) - return ensureType(challenge).previousWins - end - - def getMaxWins(challenge) - return ensureType(challenge).maxWins - end - - def getCurrentSwaps(challenge) - return ensureType(challenge).currentSwaps - end - - def getPreviousSwaps(challenge) - return ensureType(challenge).previousSwaps - end - - def getMaxSwaps(challenge) - return ensureType(challenge).maxSwaps - end - - def pbStart(challenge) - end - - def pbEnd - if @currentChallenge!=-1 - ensureType(@currentChallenge).saveWins(@bc) - @currentChallenge=-1 - end - @bc.pbEnd - end - - def pbBattle - return @bc.extraData.pbBattle(self) if @bc.extraData - opponent=pbGenerateBattleTrainer(self.nextTrainer,self.rules) - bttrainers=pbGetBTTrainers(@id) - trainerdata=bttrainers[self.nextTrainer] - ret=pbOrganizedBattleEx(opponent,self.rules, - pbGetMessageFromHash(MessageTypes::EndSpeechLose,trainerdata[4]), - pbGetMessageFromHash(MessageTypes::EndSpeechWin,trainerdata[3])) - return ret - end - - def pbInProgress? - return @bc.inProgress - end - - def pbResting? - return @bc.resting - end - - def setDecision(value) - @bc.decision=value - end - - def setParty(value) - @bc.setParty(value) - end - - def extra; @bc.extraData; end - def decision; @bc.decision; end - def wins; @bc.wins; end - def swaps; @bc.swaps; end - def battleNumber; @bc.battleNumber; end - def nextTrainer; @bc.nextTrainer; end - def pbGoOn; @bc.pbGoOn; end - def pbAddWin; @bc.pbAddWin; end - def pbCancel; @bc.pbCancel; end - def pbRest; @bc.pbRest; end - def pbMatchOver?; @bc.pbMatchOver?; end - def pbGoToStart; @bc.pbGoToStart; end - - private - - def ensureType(id) - if @types.is_a?(Array) - oldtypes=@types - @types={} - for i in 0...oldtypes.length - @types[i]=oldtypes[i] if oldtypes[i] - end - end - @types[id]=BattleChallengeType.new if !@types[id] - return @types[id] - end -end - - - -def pbRecordLastBattle - $PokemonGlobal.lastbattle = $PokemonTemp.lastbattle - $PokemonTemp.lastbattle = nil -end - -def pbPlayBattle(battledata) - return if !battledata - scene = pbNewBattleScene - scene.abortable = true - lastbattle = Marshal.restore(StringInput.new(battledata)) - case lastbattle[0] - when BattleChallenge::BattleTowerID - battleplayer = PokeBattle_BattlePlayer.new(scene,lastbattle) - when BattleChallenge::BattlePalaceID - battleplayer = PokeBattle_BattlePalacePlayer.new(scene,lastbattle) - when BattleChallenge::BattleArenaID - battleplayer = PokeBattle_BattleArenaPlayer.new(scene,lastbattle) - end - bgm = BattlePlayerHelper.pbGetBattleBGM(lastbattle) - pbBattleAnimation(bgm) { - pbSceneStandby { - battleplayer.pbStartBattle - } - } -end - -def pbDebugPlayBattle - params = ChooseNumberParams.new - params.setRange(0,500) - params.setInitialValue(0) - params.setCancelValue(-1) - num = pbMessageChooseNumber(_INTL("Choose a battle."),params) - if num>=0 - pbPlayBattleFromFile(sprintf("Battles/Battle%03d.dat",num)) - end -end - -def pbPlayLastBattle - pbPlayBattle($PokemonGlobal.lastbattle) -end - -def pbPlayBattleFromFile(filename) - pbRgssOpen(filename,"rb") { |f| pbPlayBattle(f.read) } -end - - - -class Game_Event - def pbInChallenge? - return pbBattleChallenge.pbInChallenge? - end -end - - - -def pbBattleChallenge - if !$PokemonGlobal.challenge - $PokemonGlobal.challenge=BattleChallenge.new - end - return $PokemonGlobal.challenge -end - -def pbBattleChallengeTrainer(numwins,bttrainers) - table=[ - 0,5,0,100, - 6,6,80,40, - 7,12,80,40, - 13,13,120,20, - 14,19,100,40, - 20,20,140,20, - 21,26,120,40, - 27,27,160,20, - 28,33,140,40, - 34,34,180,20, - 35,40,160,40, - 41,41,200,20, - 42,47,180,40, - 48,48,220,40, - 49,-1,200,100 - ] - for i in 0...table.length/4 - if table[i*4]<=numwins - if (table[i*4+1]<0 || table[i*4+1]>=numwins) - offset=((table[i*4+2]*bttrainers.length).floor/300).floor - length=((table[i*4+3]*bttrainers.length).floor/300).floor - return (offset+rand(length)).floor - end - end - end - return 0 -end - -def pbBattleChallengeGraphic(event) - nextTrainer=pbBattleChallenge.nextTrainer - bttrainers=pbGetBTTrainers(pbBattleChallenge.currentChallenge) - filename=GameData::TrainerType.charset_filename_brief((bttrainers[nextTrainer][0] rescue nil)) - begin - filename = "NPC 01" if nil_or_empty?(filename) - bitmap=AnimatedBitmap.new("Graphics/Characters/"+filename) - bitmap.dispose - event.character_name=filename - rescue - event.character_name="NPC 01" - end -end - -def pbBattleChallengeBeginSpeech - if !pbBattleChallenge.pbInProgress? - return "..." - else - bttrainers=pbGetBTTrainers(pbBattleChallenge.currentChallenge) - tr=bttrainers[pbBattleChallenge.nextTrainer] - return tr ? pbGetMessageFromHash(MessageTypes::BeginSpeech,tr[2]) : "..." - end -end - -def pbEntryScreen(*arg) - retval = false - pbFadeOutIn { - scene = PokemonParty_Scene.new - screen = PokemonPartyScreen.new(scene,$Trainer.party) - ret = screen.pbPokemonMultipleEntryScreenEx(pbBattleChallenge.rules.ruleset) - # Set party - pbBattleChallenge.setParty(ret) if ret - # Continue (return true) if Pokémon were chosen - retval = (ret!=nil && ret.length>0) - } - return retval -end - -def pbBattleChallengeBattle - return pbBattleChallenge.pbBattle -end - - - -class BattleFactoryData - def initialize(bcdata) - @bcdata=bcdata - end - - def pbPrepareRentals - @rentals=pbBattleFactoryPokemon(1,@bcdata.wins,@bcdata.swaps,[]) - @trainerid=@bcdata.nextTrainer - bttrainers=pbGetBTTrainers(@bcdata.currentChallenge) - trainerdata=bttrainers[@trainerid] - @opponent=NPCTrainer.new( - pbGetMessageFromHash(MessageTypes::TrainerNames,trainerdata[1]), - trainerdata[0]) - opponentPkmn=pbBattleFactoryPokemon(1,@bcdata.wins,@bcdata.swaps,@rentals) - @opponent.party=opponentPkmn.shuffle[0,3] - end - - def pbChooseRentals - pbFadeOutIn { - scene = BattleSwapScene.new - screen = BattleSwapScreen.new(scene) - @rentals = screen.pbStartRent(@rentals) - @bcdata.pbAddSwap - pbBattleChallenge.setParty(@rentals) - } - end - - def pbBattle(challenge) - bttrainers=pbGetBTTrainers(@bcdata.currentChallenge) - trainerdata=bttrainers[@trainerid] - return pbOrganizedBattleEx(@opponent,challenge.rules, - pbGetMessageFromHash(MessageTypes::EndSpeechLose,trainerdata[4]), - pbGetMessageFromHash(MessageTypes::EndSpeechWin,trainerdata[3])) - end - - def pbPrepareSwaps - @oldopponent=@opponent.party - trainerid=@bcdata.nextTrainer - bttrainers=pbGetBTTrainers(@bcdata.currentChallenge) - trainerdata=bttrainers[trainerid] - @opponent=NPCTrainer.new( - pbGetMessageFromHash(MessageTypes::TrainerNames,trainerdata[1]), - trainerdata[0]) - opponentPkmn=pbBattleFactoryPokemon( - challenge.rules,@bcdata.wins,@bcdata.swaps, - [].concat(@rentals).concat(@oldopponent)) - @opponent.party=opponentPkmn.shuffle[0,3] - end - - def pbChooseSwaps - swapMade = true - pbFadeOutIn { - scene = BattleSwapScene.new - screen = BattleSwapScreen.new(scene) - swapMade = screen.pbStartSwap(@rentals,@oldopponent) - if swapMade - @bcdata.pbAddSwap - end - @bcdata.setParty(@rentals) - } - return swapMade - end -end - - - -def pbBattleFactoryPokemon(rule,numwins,numswaps,_rentals) - table=nil - btpokemon=pbGetBTPokemon(pbBattleChallenge.currentChallenge) - ivtable=[ - 0,6,3,6, - 7,13,6,9, - 14,20,9,12, - 21,27,12,15, - 28,34,15,21, - 35,41,21,31, - 42,-1,31,31 - ] - groups=[ - 1,14,6,0, - 15,21,5,1, - 22,28,4,2, - 29,35,3,3, - 36,42,2,4, - 43,-1,1,5 - ] - if rule.ruleset.suggestedLevel!=100 - table=[ - 0,6,110,199, - 7,13,162,266, - 14,20,267,371, - 21,27,372,467, - 28,34,468,563, - 35,41,564,659, - 42,48,660,755, - 49,-1,372,849 - ] - else # Open Level (Level 100) - table=[ - 0,6,372,467, - 7,13,468,563, - 14,20,564,659, - 21,27,660,755, - 28,34,372,881, - 35,41,372,881, - 42,48,372,881, - 49,-1,372,881 - ] - end - pokemonNumbers=[0,0] - ivs=[0,0] - ivgroups=[6,0] - for i in 0...table.length/4 - if table[i*4]<=numwins - if (table[i*4+1]<0 || table[i*4+1]>=numwins) - pokemonNumbers=[ - table[i*4+2]*btpokemon.length/882, - table[i*4+3]*btpokemon.length/882 - ] - end - end - end - for i in 0...ivtable.length/4 - if ivtable[i*4]<=numwins - if (ivtable[i*4+1]<0 || ivtable[i*4+1]>=numwins) - ivs=[ivtable[i*4+2],ivtable[i*4+3]] - end - end - end - for i in 0...groups.length/4 - if groups[i*4]<=numswaps - if (groups[i*4+1]<0 || groups[i*4+1]>=numswaps) - ivgroups=[groups[i*4+2],groups[i*4+3]] - end - end - end - party=[] - loop do - party.clear - while party.length < Settings::MAX_PARTY_SIZE - rnd=pokemonNumbers[0]+rand(pokemonNumbers[1]-pokemonNumbers[0]+1) - rndpoke=btpokemon[rnd] - indvalue=(party.length (maxHeightInMeters * 10).round -end - -def pbTooHeavy?(pkmn, maxWeightInKg) - weight = (pkmn.is_a?(Pokemon)) ? pkmn.weight : GameData::Species.get(pkmn).weight - return weight > (maxWeightInKg * 10).round -end - - - -class LevelAdjustment - BothTeams = 0 - EnemyTeam = 1 - MyTeam = 2 - BothTeamsDifferent = 3 - - def type - @adjustment - end - - def initialize(adjustment) - @adjustment=adjustment - end - - def self.getNullAdjustment(thisTeam,_otherTeam) - ret=[] - for i in 0...thisTeam.length - ret[i]=thisTeam[i].level - end - return ret - end - - def getAdjustment(thisTeam,otherTeam) - return self.getNullAdjustment(thisTeam,otherTeam) - end - - def getOldExp(team1,_team2) - ret=[] - for i in 0...team1.length - ret.push(team1[i].exp) - end - return ret - end - - def unadjustLevels(team1,team2,adjustments) - for i in 0...team1.length - exp=adjustments[0][i] - if exp && team1[i].exp!=exp - team1[i].exp=exp - team1[i].calc_stats - end - end - for i in 0...team2.length - exp=adjustments[1][i] - if exp && team2[i].exp!=exp - team2[i].exp=exp - team2[i].calc_stats - end - end - end - - def adjustLevels(team1,team2) - adj1=nil - adj2=nil - ret=[getOldExp(team1,team2),getOldExp(team2,team1)] - if @adjustment==BothTeams || @adjustment==MyTeam - adj1=getAdjustment(team1,team2) - elsif @adjustment==BothTeamsDifferent - adj1=getMyAdjustment(team1,team2) - end - if @adjustment==BothTeams || @adjustment==EnemyTeam - adj2=getAdjustment(team2,team1) - elsif @adjustment==BothTeamsDifferent - adj2=getTheirAdjustment(team2,team1) - end - if adj1 - for i in 0...team1.length - if team1[i].level!=adj1[i] - team1[i].level=adj1[i] - team1[i].calc_stats - end - end - end - if adj2 - for i in 0...team2.length - if team2[i].level!=adj2[i] - team2[i].level=adj2[i] - team2[i].calc_stats - end - end - end - return ret - end -end - - - -class LevelBalanceAdjustment < LevelAdjustment - def initialize(minLevel) - super(LevelAdjustment::BothTeams) - @minLevel=minLevel - end - - def getAdjustment(thisTeam,_otherTeam) - ret=[] - for i in 0...thisTeam.length - ret[i]=pbBalancedLevelFromBST(thisTeam[i].species) - end - return ret - end -end - - - -class EnemyLevelAdjustment < LevelAdjustment - def initialize(level) - super(LevelAdjustment::EnemyTeam) - @level = level.clamp(1, GameData::GrowthRate.max_level) - end - - def getAdjustment(thisTeam,_otherTeam) - ret=[] - for i in 0...thisTeam.length - ret[i]=@level - end - return ret - end -end - - - -class CombinedLevelAdjustment < LevelAdjustment - def initialize(my,their) - super(LevelAdjustment::BothTeamsDifferent) - @my=my - @their=their - end - - def getMyAdjustment(myTeam,theirTeam) - return @my ? @my.getAdjustment(myTeam,theirTeam) : - LevelAdjustment.getNullAdjustment(myTeam,theirTeam) - end - - def getTheirAdjustment(theirTeam,myTeam) - return @their ? @their.getAdjustment(theirTeam,myTeam) : - LevelAdjustment.getNullAdjustment(theirTeam,myTeam) - end -end - - - -class SinglePlayerCappedLevelAdjustment < CombinedLevelAdjustment - def initialize(level) - super(CappedLevelAdjustment.new(level),FixedLevelAdjustment.new(level)) - end -end - - - -class CappedLevelAdjustment < LevelAdjustment - def initialize(level) - super(LevelAdjustment::BothTeams) - @level = level.clamp(1, GameData::GrowthRate.max_level) - end - - def getAdjustment(thisTeam,_otherTeam) - ret=[] - for i in 0...thisTeam.length - ret[i]=[thisTeam[i].level,@level].min - end - return ret - end -end - - - -class FixedLevelAdjustment < LevelAdjustment - def initialize(level) - super(LevelAdjustment::BothTeams) - @level = level.clamp(1, GameData::GrowthRate.max_level) - end - - def getAdjustment(thisTeam,_otherTeam) - ret=[] - for i in 0...thisTeam.length - ret[i]=@level - end - return ret - end -end - - - -class TotalLevelAdjustment < LevelAdjustment - def initialize(minLevel,maxLevel,totalLevel) - super(LevelAdjustment::EnemyTeam) - @minLevel = minLevel.clamp(1, GameData::GrowthRate.max_level) - @maxLevel = maxLevel.clamp(1, GameData::GrowthRate.max_level) - @totalLevel=totalLevel - end - - def getAdjustment(thisTeam,_otherTeam) - ret=[] - total=0 - for i in 0...thisTeam.length - ret[i]=@minLevel - total+=@minLevel - end - loop do - work=false - for i in 0...thisTeam.length - if ret[i]>=@maxLevel || total>=@totalLevel - next - end - ret[i]+=1 - total+=1 - work=true - end - break if !work - end - return ret - end -end - - - -class OpenLevelAdjustment < LevelAdjustment - def initialize(minLevel=1) - super(LevelAdjustment::EnemyTeam) - @minLevel=minLevel - end - - def getAdjustment(thisTeam,otherTeam) - maxLevel=1 - for i in 0...otherTeam.length - level=otherTeam[i].level - maxLevel=level if maxLevel0 - end -end - - - -class SpeciesRestriction - def initialize(*specieslist) - @specieslist=specieslist.clone - end - - def isSpecies?(species,specieslist) - return specieslist.include?(species) - end - - def isValid?(pokemon) - count=0 - if isSpecies?(pokemon.species,@specieslist) - count+=1 - end - return count!=0 - end -end - - - -class BannedSpeciesRestriction - def initialize(*specieslist) - @specieslist=specieslist.clone - end - - def isSpecies?(species,specieslist) - return specieslist.include?(species) - end - - def isValid?(pokemon) - count=0 - if isSpecies?(pokemon.species,@specieslist) - count+=1 - end - return count==0 - end -end - - - -class BannedItemRestriction - def initialize(*itemlist) - @itemlist=itemlist.clone - end - - def isSpecies?(item,itemlist) - return itemlist.include?(item) - end - - def isValid?(pokemon) - count=0 - if pokemon.item && isSpecies?(pokemon.item,@itemlist) - count+=1 - end - return count==0 - end -end - - - -class RestrictedSpeciesRestriction - def initialize(maxValue,*specieslist) - @specieslist=specieslist.clone - @maxValue=maxValue - end - - def isSpecies?(species,specieslist) - return specieslist.include?(species) - end - - def isValid?(team) - count=0 - for i in 0...team.length - if isSpecies?(team[i].species,@specieslist) - count+=1 - end - end - return count<=@maxValue - end -end - - - -class RestrictedSpeciesTeamRestriction < RestrictedSpeciesRestriction - def initialize(*specieslist) - super(4,*specieslist) - end -end - - - -class RestrictedSpeciesSubsetRestriction < RestrictedSpeciesRestriction - def initialize(*specieslist) - super(2,*specieslist) - end -end - - - -class StandardRestriction - def isValid?(pkmn) - return false if !pkmn || pkmn.egg? - # Species with disadvantageous abilities are not banned - pkmn.species_data.abilities.each do |a| - return true if [:TRUANT, :SLOWSTART].include?(a[0]) - end - # Certain named species are not banned - speciesWhitelist = [:DRAGONITE, :SALAMENCE, :TYRANITAR] - return true if speciesWhitelist.include?(pkmn.species) - # Certain named species are banned - speciesBlacklist = [:WYNAUT, :WOBBUFFET] - return false if speciesBlacklist.include?(pkmn.species) - # Species with total base stat 600 or more are banned - bst = 0 - pkmn.baseStats.each_value { |s| bst += s } - return false if bst >= 600 - # Is valid - return true - end -end - - - -module LevelRestriction; end - - - -class MinimumLevelRestriction - attr_reader :level - - def initialize(minLevel) - @level=minLevel - end - - def isValid?(pokemon) - return pokemon.level>=@level - end -end - - - -class MaximumLevelRestriction - attr_reader :level - - def initialize(maxLevel) - @level=maxLevel - end - - def isValid?(pokemon) - return pokemon.level<=@level - end -end - - - -class HeightRestriction - def initialize(maxHeightInMeters) - @level=maxHeightInMeters - end - - def isValid?(pokemon) - return !pbTooTall?(pokemon,@level) - end -end - - - -class WeightRestriction - def initialize(maxWeightInKg) - @level=maxWeightInKg - end - - def isValid?(pokemon) - return !pbTooHeavy?(pokemon,@level) - end -end - - - -class SoulDewClause - def isValid?(pokemon) - return !pokemon.hasItem?(:SOULDEW) - end -end - - - -class ItemsDisallowedClause - def isValid?(pokemon) - return !pokemon.hasItem? - end -end - - - -class NegativeExtendedGameClause - def isValid?(pokemon) - return false if pokemon.isSpecies?(:ARCEUS) - return false if pokemon.hasItem?(:MICLEBERRY) - return false if pokemon.hasItem?(:CUSTAPBERRY) - return false if pokemon.hasItem?(:JABOCABERRY) - return false if pokemon.hasItem?(:ROWAPBERRY) - end -end - - - -class TotalLevelRestriction - attr_reader :level - - def initialize(level) - @level=level - end - - def isValid?(team) - totalLevel=0 - for i in 0...team.length-1 - next if team[i].species==0 - totalLevel+=team[i].level - end - return (totalLevel<=@level) - end - - def errorMessage - return _INTL("The combined levels exceed {1}.",@level) - end -end - - - -class SameSpeciesClause - def isValid?(team) - species=0 - for i in 0...team.length-1 - next if team[i].species==0 - if species==0 - species=team[i].species - else - return false if team[i].species!=species - end - end - return true - end - - def errorMessage - return _INTL("Pokémon can't be the same.") - end -end - - - -class SpeciesClause - def isValid?(team) - for i in 0...team.length-1 - next if team[i].species==0 - for j in i+1...team.length - return false if team[i].species==team[j].species - end - end - return true - end - - def errorMessage - return _INTL("Pokémon can't be the same.") - end -end - - - -$babySpeciesData = {} -$canEvolve = {} - - - -class BabyRestriction - def isValid?(pokemon) - baby=$babySpeciesData[pokemon.species] ? $babySpeciesData[pokemon.species] : - ($babySpeciesData[pokemon.species] = pokemon.species_data.get_baby_species) - return baby==pokemon.species - end -end - - - -class UnevolvedFormRestriction - def isValid?(pokemon) - baby=$babySpeciesData[pokemon.species] ? $babySpeciesData[pokemon.species] : - ($babySpeciesData[pokemon.species] = pokemon.species_data.get_baby_species) - return false if pokemon.species != baby - canEvolve=($canEvolve[pokemon.species]!=nil) ? $canEvolve[pokemon.species] : - ($canEvolve[pokemon.species] = pokemon.species_data.get_evolutions(true).length > 0) - return false if !canEvolve - return true - end -end - - - -class LittleCupRestriction - def isValid?(pokemon) - return false if pokemon.hasItem?(:BERRYJUICE) - return false if pokemon.hasItem?(:DEEPSEATOOTH) - return false if pokemon.hasMove?(:SONICBOOM) - return false if pokemon.hasMove?(:DRAGONRAGE) - return false if pokemon.isSpecies?(:SCYTHER) - return false if pokemon.isSpecies?(:SNEASEL) - return false if pokemon.isSpecies?(:MEDITITE) - return false if pokemon.isSpecies?(:YANMA) - return false if pokemon.isSpecies?(:TANGELA) - return false if pokemon.isSpecies?(:MURKROW) - return true - end -end - - - -class ItemClause - def isValid?(team) - for i in 0...team.length-1 - next if !team[i].hasItem? - for j in i+1...team.length - return false if team[i].item==team[j].item - end - end - return true - end - - def errorMessage - return _INTL("No identical hold items.") - end -end - - - -module NicknameChecker - @@names = {} - - def getName(species) - n = @@names[species] - return n if n - n = GameData::Species.get(species).name - @@names[species] = n.upcase - return n - end - - def check(name, species) - name = name.upcase - return true if name == getName(species) - return false if @@names.values.include?(name) - GameData::Species.each do |species_data| - next if species_data.species == species || species_data.form != 0 - return false if getName(species_data.id) == name - end - return true - end -end - - - -# No two Pokemon can have the same nickname. -# No nickname can be the same as the (real) name of another Pokemon character. -class NicknameClause - def isValid?(team) - for i in 0...team.length-1 - for j in i+1...team.length - return false if team[i].name==team[j].name - return false if !NicknameChecker.check(team[i].name,team[i].species) - end - end - return true - end - - def errorMessage - return _INTL("No identical nicknames.") - end -end - - - -class PokemonRuleSet - def minTeamLength - return [1,self.minLength].max - end - - def maxTeamLength - return [6,self.maxLength].max - end - - def minLength - return @minLength ? @minLength : self.maxLength - end - - def maxLength - return @number<0 ? 6 : @number - end - - def number - return self.maxLength - end - - def initialize(number=0) - @pokemonRules=[] - @teamRules=[] - @subsetRules=[] - @minLength=1 - @number=number - end - - def copy - ret=PokemonRuleSet.new(@number) - for rule in @pokemonRules - ret.addPokemonRule(rule) - end - for rule in @teamRules - ret.addTeamRule(rule) - end - for rule in @subsetRules - ret.addSubsetRule(rule) - end - return ret - end - - # Returns the length of a valid subset of a Pokemon team. - def suggestedNumber - return self.maxLength - end - - # Returns a valid level to assign to each member of a valid Pokemon team. - def suggestedLevel - minLevel=1 - maxLevel=GameData::GrowthRate.max_level - num=self.suggestedNumber - for rule in @pokemonRules - if rule.is_a?(MinimumLevelRestriction) - minLevel=rule.level - elsif rule.is_a?(MaximumLevelRestriction) - maxLevel=rule.level - end - end - totalLevel=maxLevel*num - for rule in @subsetRules - if rule.is_a?(TotalLevelRestriction) - totalLevel=rule.level - end - end - if totalLevel>=maxLevel*num - return [maxLevel,minLevel].max - else - return [(totalLevel/self.suggestedNumber),minLevel].max - end - end - - def setNumberRange(minValue,maxValue) - @minLength=[1,minValue].max - @number=[maxValue,6].min - return self - end - - def setNumber(value) - return setNumberRange(value,value) - end - - def addPokemonRule(rule) - @pokemonRules.push(rule) - return self - end - - # This rule checks - # - the entire team to determine whether a subset of the team meets the rule, or - # - a list of Pokemon whose length is equal to the suggested number. For an - # entire team, the condition must hold for at least one possible subset of - # the team, but not necessarily for the entire team. - # A subset rule is "number-dependent", that is, whether the condition is likely - # to hold depends on the number of Pokemon in the subset. - # Example of a subset rule: - # - The combined level of X Pokemon can't exceed Y. - def addSubsetRule(rule) - @teamRules.push(rule) - return self - end - - # This rule checks either - # - the entire team to determine whether a subset of the team meets the rule, or - # - whether the entire team meets the rule. If the condition holds for the - # entire team, the condition must also hold for any possible subset of the - # team with the suggested number. - # Examples of team rules: - # - No two Pokemon can be the same species. - # - No two Pokemon can hold the same items. - def addTeamRule(rule) - @teamRules.push(rule) - return self - end - - def clearPokemonRules - @pokemonRules.clear - return self - end - - def clearTeamRules - @teamRules.clear - return self - end - - def clearSubsetRules - @subsetRules.clear - return self - end - - def isPokemonValid?(pokemon) - return false if !pokemon - for rule in @pokemonRules - if !rule.isValid?(pokemon) - return false - end - end - return true - end - - def hasRegistrableTeam?(list) - return false if !list || list.lengthself.maxTeamLength - return false - end - teamNumber=[self.maxLength,team.length].min - for pokemon in team - if !isPokemonValid?(pokemon) - return false - end - end - for rule in @teamRules - if !rule.isValid?(team) - return false - end - end - if @subsetRules.length>0 - pbEachCombination(team,teamNumber) { |comb| - isValid=true - for rule in @subsetRules - next if rule.isValid?(comb) - isValid=false - break - end - return true if isValid - } - return false - end - return true - end - - # Returns true if the team's length is greater or equal to the suggested number - # and at least one subset of the team meets the requirements of any team rules - # and subset rules. Not all Pokemon in the team have to be valid. - def hasValidTeam?(team) - return false if !team || team.length0 - pbEachCombination(team,teamNumber) { |comb| return true if isValid?(comb) } - return false - end - return true - end - - # Returns true if the team's length meets the subset length range requirements - # and the team meets the requirements of any team rules and subset rules. Each - # Pokemon in the team must be valid. - def isValid?(team,error=nil) - if team.length1 - return false - elsif team.length>self.maxLength - error.push(_INTL("No more than {1} Pokémon may enter.",self.maxLength)) if error - return false - end - for pokemon in team - if !isPokemonValid?(pokemon) - if pokemon - error.push(_INTL("This team is not allowed.", pokemon.name)) if error - else - error.push(_INTL("{1} is not allowed.", pokemon.name)) if error - end - return false - end - end - for rule in @teamRules - if !rule.isValid?(team) - error.push(rule.errorMessage) if error - return false - end - end - for rule in @subsetRules - if !rule.isValid?(team) - error.push(rule.errorMessage) if error - return false - end - end - return true - end -end - - - -class BattleType - def pbCreateBattle(scene,trainer1,trainer2) - return PokeBattle_Battle.new(scene, - trainer1.party,trainer2.party,trainer1,trainer2) - end -end - - - -class BattleTower < BattleType - def pbCreateBattle(scene,trainer1,trainer2) - return PokeBattle_RecordedBattle.new(scene, - trainer1.party,trainer2.party,trainer1,trainer2) - end -end - - - -class BattlePalace < BattleType - def pbCreateBattle(scene,trainer1,trainer2) - return PokeBattle_RecordedBattlePalace.new(scene, - trainer1.party,trainer2.party,trainer1,trainer2) - end -end - - - -class BattleArena < BattleType - def pbCreateBattle(scene,trainer1,trainer2) - return PokeBattle_RecordedBattleArena.new(scene, - trainer1.party,trainer2.party,trainer1,trainer2) - end -end - - - -class BattleRule - def setRule(battle); end -end - - - -class DoubleBattle < BattleRule - def setRule(battle); battle.setBattleMode("double"); end -end - - - -class SingleBattle < BattleRule - def setRule(battle); battle.setBattleMode("single"); end -end - - - -class SoulDewBattleClause < BattleRule - def setRule(battle); battle.rules["souldewclause"] = true; end -end - - - -class SleepClause < BattleRule - def setRule(battle); battle.rules["sleepclause"] = true; end -end - - - -class FreezeClause < BattleRule - def setRule(battle); battle.rules["freezeclause"] = true; end -end - - - -class EvasionClause < BattleRule - def setRule(battle); battle.rules["evasionclause"] = true; end -end - - - -class OHKOClause < BattleRule - def setRule(battle); battle.rules["ohkoclause"] = true; end -end - - - -class PerishSongClause < BattleRule - def setRule(battle); battle.rules["perishsong"] = true; end -end - - - -class SelfKOClause < BattleRule - def setRule(battle); battle.rules["selfkoclause"] = true; end -end - - - -class SelfdestructClause < BattleRule - def setRule(battle); battle.rules["selfdestructclause"] = true; end -end - - - -class SonicBoomClause < BattleRule - def setRule(battle); battle.rules["sonicboomclause"] = true; end -end - - - -class ModifiedSleepClause < BattleRule - def setRule(battle); battle.rules["modifiedsleepclause"] = true; end -end - - - -class SkillSwapClause < BattleRule - def setRule(battle); battle.rules["skillswapclause"] = true; end -end - - - -class PokemonChallengeRules - attr_reader :ruleset - attr_reader :battletype - attr_reader :levelAdjustment - - def initialize(ruleset=nil) - @ruleset=ruleset ? ruleset : PokemonRuleSet.new - @battletype=BattleTower.new - @levelAdjustment=nil - @battlerules=[] - end - - def copy - ret=PokemonChallengeRules.new(@ruleset.copy) - ret.setBattleType(@battletype) - ret.setLevelAdjustment(@levelAdjustment) - for rule in @battlerules - ret.addBattleRule(rule) - end - return ret - end - - def number - return self.ruleset.number - end - - def setNumber(number) - self.ruleset.setNumber(number) - return self - end - - def setDoubleBattle(value) - if value - self.ruleset.setNumber(4) - self.addBattleRule(DoubleBattle.new) - else - self.ruleset.setNumber(3) - self.addBattleRule(SingleBattle.new) - end - return self - end - - def adjustLevelsBilateral(party1,party2) - if @levelAdjustment && @levelAdjustment.type==LevelAdjustment::BothTeams - return @levelAdjustment.adjustLevels(party1,party2) - else - return nil - end - end - - def unadjustLevelsBilateral(party1,party2,adjusts) - if @levelAdjustment && adjusts && @levelAdjustment.type==LevelAdjustment::BothTeams - @levelAdjustment.unadjustLevels(party1,party2,adjusts) - end - end - - def adjustLevels(party1,party2) - if @levelAdjustment - return @levelAdjustment.adjustLevels(party1,party2) - else - return nil - end - end - - def unadjustLevels(party1,party2,adjusts) - if @levelAdjustment && adjusts - @levelAdjustment.unadjustLevels(party1,party2,adjusts) - end - end - - def addPokemonRule(rule) - self.ruleset.addPokemonRule(rule) - return self - end - - def addLevelRule(minLevel,maxLevel,totalLevel) - self.addPokemonRule(MinimumLevelRestriction.new(minLevel)) - self.addPokemonRule(MaximumLevelRestriction.new(maxLevel)) - self.addSubsetRule(TotalLevelRestriction.new(totalLevel)) - self.setLevelAdjustment(TotalLevelAdjustment.new(minLevel,maxLevel,totalLevel)) - return self - end - - def addSubsetRule(rule) - self.ruleset.addSubsetRule(rule) - return self - end - - def addTeamRule(rule) - self.ruleset.addTeamRule(rule) - return self - end - - def addBattleRule(rule) - @battlerules.push(rule) - return self - end - - def createBattle(scene,trainer1,trainer2) - battle=@battletype.pbCreateBattle(scene,trainer1,trainer2) - for p in @battlerules - p.setRule(battle) - end - return battle - end - - def setRuleset(rule) - @ruleset=rule - return self - end - - def setBattleType(rule) - @battletype=rule - return self - end - - def setLevelAdjustment(rule) - @levelAdjustment=rule - return self - end -end - - - -########################################### -# Generation IV Cups -########################################### -class StandardRules < PokemonRuleSet - attr_reader :number - - def initialize(number,level=nil) - super(number) - addPokemonRule(StandardRestriction.new) - addPokemonRule(SpeciesClause.new) - addPokemonRule(ItemClause.new) - if level - addPokemonRule(MaximumLevelRestriction.new(level)) - end - end -end - - - -class StandardCup < StandardRules - def initialize - super(3,50) - end - - def name - return _INTL("STANDARD Cup") - end -end - - - -class DoubleCup < StandardRules - def initialize - super(4,50) - end - - def name - return _INTL("DOUBLE Cup") - end -end - - - -class FancyCup < PokemonRuleSet - def initialize - super(3) - addPokemonRule(StandardRestriction.new) - addPokemonRule(MaximumLevelRestriction.new(30)) - addSubsetRule(TotalLevelRestriction.new(80)) - addPokemonRule(HeightRestriction.new(2)) - addPokemonRule(WeightRestriction.new(20)) - addPokemonRule(BabyRestriction.new) - addPokemonRule(SpeciesClause.new) - addPokemonRule(ItemClause.new) - end - - def name - return _INTL("FANCY Cup") - end -end - - - -class LittleCup < PokemonRuleSet - def initialize - super(3) - addPokemonRule(StandardRestriction.new) - addPokemonRule(MaximumLevelRestriction.new(5)) - addPokemonRule(BabyRestriction.new) - addPokemonRule(SpeciesClause.new) - addPokemonRule(ItemClause.new) - end - - def name - return _INTL("LITTLE Cup") - end -end - - - -class LightCup < PokemonRuleSet - def initialize - super(3) - addPokemonRule(StandardRestriction.new) - addPokemonRule(MaximumLevelRestriction.new(50)) - addPokemonRule(WeightRestriction.new(99)) - addPokemonRule(BabyRestriction.new) - addPokemonRule(SpeciesClause.new) - addPokemonRule(ItemClause.new) - end - def name - return _INTL("LIGHT Cup") - end -end - - - -########################################### -# Stadium Cups -########################################### -def pbPikaCupRules(double) - ret=PokemonChallengeRules.new - ret.addPokemonRule(StandardRestriction.new) - ret.addLevelRule(15,20,50) - ret.addTeamRule(SpeciesClause.new) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SleepClause.new) - ret.addBattleRule(FreezeClause.new) - ret.addBattleRule(SelfKOClause.new) - ret.setDoubleBattle(double).setNumber(3) - return ret -end - -def pbPokeCupRules(double) - ret=PokemonChallengeRules.new - ret.addPokemonRule(StandardRestriction.new) - ret.addLevelRule(50,55,155) - ret.addTeamRule(SpeciesClause.new) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SleepClause.new) - ret.addBattleRule(FreezeClause.new) - ret.addBattleRule(SelfdestructClause.new) - ret.setDoubleBattle(double).setNumber(3) - return ret -end - -def pbPrimeCupRules(double) - ret=PokemonChallengeRules.new - ret.setLevelAdjustment(OpenLevelAdjustment.new(GameData::GrowthRate.max_level)) - ret.addTeamRule(SpeciesClause.new) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SleepClause.new) - ret.addBattleRule(FreezeClause.new) - ret.addBattleRule(SelfdestructClause.new) - ret.setDoubleBattle(double) - return ret -end - -def pbFancyCupRules(double) - ret=PokemonChallengeRules.new - ret.addPokemonRule(StandardRestriction.new) - ret.addLevelRule(25,30,80) - ret.addPokemonRule(HeightRestriction.new(2)) - ret.addPokemonRule(WeightRestriction.new(20)) - ret.addPokemonRule(BabyRestriction.new) - ret.addTeamRule(SpeciesClause.new) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SleepClause.new) - ret.addBattleRule(FreezeClause.new) - ret.addBattleRule(PerishSongClause.new) - ret.addBattleRule(SelfdestructClause.new) - ret.setDoubleBattle(double).setNumber(3) - return ret -end - -def pbLittleCupRules(double) - ret=PokemonChallengeRules.new - ret.addPokemonRule(StandardRestriction.new) - ret.addPokemonRule(UnevolvedFormRestriction.new) - ret.setLevelAdjustment(EnemyLevelAdjustment.new(5)) - ret.addPokemonRule(MaximumLevelRestriction.new(5)) - ret.addTeamRule(SpeciesClause.new) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SleepClause.new) - ret.addBattleRule(FreezeClause.new) - ret.addBattleRule(SelfdestructClause.new) - ret.addBattleRule(PerishSongClause.new) - ret.addBattleRule(SonicBoomClause.new) - ret.setDoubleBattle(double) - return ret -end - -def pbStrictLittleCupRules(double) - ret=PokemonChallengeRules.new - ret.addPokemonRule(StandardRestriction.new) - ret.addPokemonRule(UnevolvedFormRestriction.new) - ret.setLevelAdjustment(EnemyLevelAdjustment.new(5)) - ret.addPokemonRule(MaximumLevelRestriction.new(5)) - ret.addPokemonRule(LittleCupRestriction.new) - ret.addTeamRule(SpeciesClause.new) - ret.addBattleRule(SleepClause.new) - ret.addBattleRule(EvasionClause.new) - ret.addBattleRule(OHKOClause.new) - ret.addBattleRule(SelfKOClause.new) - ret.setDoubleBattle(double).setNumber(3) - return ret -end - - - -########################################### -# Battle Frontier Rules -########################################### -def pbBattleTowerRules(double,openlevel) - ret=PokemonChallengeRules.new - if openlevel - ret.setLevelAdjustment(OpenLevelAdjustment.new(60)) - else - ret.setLevelAdjustment(CappedLevelAdjustment.new(50)) - end - ret.addPokemonRule(StandardRestriction.new) - ret.addTeamRule(SpeciesClause.new) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SoulDewBattleClause.new) - ret.setDoubleBattle(double) - return ret -end - -def pbBattlePalaceRules(double,openlevel) - return pbBattleTowerRules(double,openlevel).setBattleType(BattlePalace.new) -end - -def pbBattleArenaRules(openlevel) - return pbBattleTowerRules(false,openlevel).setBattleType(BattleArena.new) -end - -def pbBattleFactoryRules(double,openlevel) - ret=PokemonChallengeRules.new - if openlevel - ret.setLevelAdjustment(FixedLevelAdjustment.new(100)) - ret.addPokemonRule(MaximumLevelRestriction.new(100)) - else - ret.setLevelAdjustment(FixedLevelAdjustment.new(50)) - ret.addPokemonRule(MaximumLevelRestriction.new(50)) - end - ret.addTeamRule(SpeciesClause.new) - ret.addPokemonRule(BannedSpeciesRestriction.new(:UNOWN)) - ret.addTeamRule(ItemClause.new) - ret.addBattleRule(SoulDewBattleClause.new) - ret.setDoubleBattle(double).setNumber(0) - return ret -end - - - -=begin -########################################### -# Other Interesting Rulesets -########################################### - -# Official Species Restriction -.addPokemonRule(BannedSpeciesRestriction.new( - :MEWTWO,:MEW, - :LUGIA,:HOOH,:CELEBI, - :KYOGRE,:GROUDON,:RAYQUAZA,:JIRACHI,:DEOXYS, - :DIALGA,:PALKIA,:GIRATINA,:MANAPHY,:PHIONE, - :DARKRAI,:SHAYMIN,:ARCEUS)) -.addBattleRule(SoulDewBattleClause.new) - - - -# New Official Species Restriction -.addPokemonRule(BannedSpeciesRestriction.new( - :MEW, - :CELEBI, - :JIRACHI,:DEOXYS, - :MANAPHY,:PHIONE,:DARKRAI,:SHAYMIN,:ARCEUS)) -.addBattleRule(SoulDewBattleClause.new) - - - -# Pocket Monsters Stadium -PokemonChallengeRules.new -.addPokemonRule(SpeciesRestriction.new( - :VENUSAUR,:CHARIZARD,:BLASTOISE,:BEEDRILL,:FEAROW, - :PIKACHU,:NIDOQUEEN,:NIDOKING,:DUGTRIO,:PRIMEAPE, - :ARCANINE,:ALAKAZAM,:MACHAMP,:GOLEM,:MAGNETON, - :CLOYSTER,:GENGAR,:ONIX,:HYPNO,:ELECTRODE, - :EXEGGUTOR,:CHANSEY,:KANGASKHAN,:STARMIE,:SCYTHER, - :JYNX,:PINSIR,:TAUROS,:GYARADOS,:LAPRAS, - :DITTO,:VAPOREON,:JOLTEON,:FLAREON,:AERODACTYL, - :SNORLAX,:ARTICUNO,:ZAPDOS,:MOLTRES,:DRAGONITE -)) - - - -# 1999 Tournament Rules -PokemonChallengeRules.new -.addTeamRule(SpeciesClause.new) -.addPokemonRule(ItemsDisallowedClause.new) -.addBattleRule(SleepClause.new) -.addBattleRule(FreezeClause.new) -.addBattleRule(SelfdestructClause.new) -.setDoubleBattle(false) -.setLevelRule(1,50,150) -.addPokemonRule(BannedSpeciesRestriction.new( - :VENUSAUR,:DUGTRIO,:ALAKAZAM,:GOLEM,:MAGNETON, - :GENGAR,:HYPNO,:ELECTRODE,:EXEGGUTOR,:CHANSEY, - :KANGASKHAN,:STARMIE,:JYNX,:TAUROS,:GYARADOS, - :LAPRAS,:DITTO,:VAPOREON,:JOLTEON,:SNORLAX, - :ARTICUNO,:ZAPDOS,:DRAGONITE,:MEWTWO,:MEW)) - - - -# 2005 Tournament Rules -PokemonChallengeRules.new -.addPokemonRule(BannedSpeciesRestriction.new( - :DRAGONITE,:MEW,:MEWTWO, - :TYRANITAR,:LUGIA,:CELEBI,:HOOH,:GROUDON,:KYOGRE,:RAYQUAZA, - :JIRACHI,:DEOXYS)) -.setDoubleBattle(true) -.addLevelRule(1,50,200) -.addTeamRule(ItemClause.new) -.addPokemonRule(BannedItemRestriction.new(:SOULDEW,:ENIGMABERRY)) -.addBattleRule(SleepClause.new) -.addBattleRule(FreezeClause.new) -.addBattleRule(SelfdestructClause.new) -.addBattleRule(PerishSongClause.new) - - - -# 2008 Tournament Rules -PokemonChallengeRules.new -.addPokemonRule(BannedSpeciesRestriction.new( - :MEWTWO,:MEW,:TYRANITAR,:LUGIA,:HOOH,:CELEBI, - :GROUDON,:KYOGRE,:RAYQUAZA,:JIRACHI,:DEOXYS, - :PALKIA,:DIALGA,:PHIONE,:MANAPHY,:ROTOM,:SHAYMIN,:DARKRAI)) -.setDoubleBattle(true) -.addLevelRule(1,50,200) -.addTeamRule(NicknameClause.new) -.addTeamRule(ItemClause.new) -.addBattleRule(SoulDewBattleClause.new) - - - -# 2010 Tournament Rules -PokemonChallengeRules.new -.addPokemonRule(BannedSpeciesRestriction.new( - :MEW,:CELEBI,:JIRACHI,:DEOXYS, - :PHIONE,:MANAPHY,:SHAYMIN,:DARKRAI,:ARCEUS)) -.addSubsetRule(RestrictedSpeciesSubsetRestriction.new( - :MEWTWO,:LUGIA,:HOOH, - :GROUDON,:KYOGRE,:RAYQUAZA, - :PALKIA,:DIALGA,:GIRATINA)) -.setDoubleBattle(true) -.addLevelRule(1,100,600) -.setLevelAdjustment(CappedLevelAdjustment.new(50)) -.addTeamRule(NicknameClause.new) -.addTeamRule(ItemClause.new) -.addPokemonRule(SoulDewClause.new) - - - -# Pokemon Colosseum -- Anything Goes -PokemonChallengeRules.new -.addLevelRule(1,100,600) -.addBattleRule(SleepClause.new) -.addBattleRule(FreezeClause.new) -.addBattleRule(SelfdestructClause.new) -.addBattleRule(PerishSongClause.new) - - - -# Pokemon Colosseum -- Max Lv. 50 -PokemonChallengeRules.new -.addLevelRule(1,50,300) -.addTeamRule(SpeciesClause.new) -.addTeamRule(ItemClause.new) -.addBattleRule(SleepClause.new) -.addBattleRule(FreezeClause.new) -.addBattleRule(SelfdestructClause.new) -.addBattleRule(PerishSongClause.new) - - - -# Pokemon Colosseum -- Max Lv. 100 -PokemonChallengeRules.new -.addLevelRule(1,100,600) -.addTeamRule(SpeciesClause.new) -.addTeamRule(ItemClause.new) -.addBattleRule(SleepClause.new) -.addBattleRule(FreezeClause.new) -.addBattleRule(SelfdestructClause.new) -.addBattleRule(PerishSongClause.new) - - - -# Battle Time (includes animations) -If the time runs out, the team with the most Pok�mon left wins. If both teams have -the same number of Pokémon left, total HP remaining breaks the tie. If both HP -totals are identical, the battle is a draw. - -# Command Time -If the player is in the process of switching Pokémon when the time runs out, the -one that can still battle that's closest to the top of the roster is chosen. -Otherwise, the attack on top of the list is chosen. -=end diff --git a/Data/Scripts/018_Alternate battle modes/005_OrgBattle_Generator.rb b/Data/Scripts/018_Alternate battle modes/005_OrgBattle_Generator.rb deleted file mode 100644 index f4c078ed3..000000000 --- a/Data/Scripts/018_Alternate battle modes/005_OrgBattle_Generator.rb +++ /dev/null @@ -1,1318 +0,0 @@ -def pbRandomMove - keys = GameData::Move::DATA.keys - loop do - move_id = keys[rand(keys.length)] - move = GameData::Move.get(move_id) - next if move.id_number > 384 || move.id == :SKETCH || move.id == :STRUGGLE - return move.id - end -end - -def addMove(moves,move,base) - data=GameData::Move.get(move) - count=base+1 - if data.function_code=="000" && data.base_damage<=40 - count=base - end - if [:BUBBLE, :BUBBLEBEAM].include?(data.id) - count=0 - return - end - if data.base_damage<=30 || [:GROWL, :TAILWHIP, :LEER].include?(data.id) - count=base - end - if data.base_damage>=60 || - [:REFLECT, :LIGHTSCREEN, :SAFEGUARD, :SUBSTITUTE, :FAKEOUT].include?(data.id) - count=base+2 - end - if data.base_damage >= 80 && data.type == :NORMAL - count=base+5 - end - if data.base_damage >= 80 && data.type == :NORMAL - count=base+3 - end - if [:PROTECT, :DETECT, :TOXIC, :AERIALACE, :WILLOWISP, :SPORE, :THUNDERWAVE, - :HYPNOSIS, :CONFUSERAY, :ENDURE, :SWORDSDANCE].include?(data.id) - count=base+3 - end - if !moves.include?(move.id) - count.times { moves.push(move.id) } - end -end - -$legalMoves = {} -$legalMovesLevel = 0 -$baseStatTotal = {} -$minimumLevel = {} -$babySpecies = {} -$evolutions = {} -$tmMoves = nil - -def pbGetLegalMoves2(species,maxlevel) - species_data = GameData::Species.get(species) - moves = [] - return moves if !species_data - # Populate available moves array (moves) - species_data.moves.each { |m| addMove(moves, m[1], 2) if m[0] <= maxlevel } - if !$tmMoves - $tmMoves = [] - GameData::Item.each { |i| $tmMoves.push(i.move) if i.is_machine? } - end - species_data.tutor_moves.each { |m| addMove(moves, m, 0) if $tmMoves.include?(m) } - babyspecies = babySpecies(species) - GameData::Species.get(babyspecies).egg_moves.each { |m| addMove(moves, m, 2) } - # - movedatas = [] - for move in moves - movedatas.push([move, GameData::Move.get(move)]) - end - # Delete less powerful moves - deleteAll=proc { |a,item| - while a.include?(item) - a.delete(item) - end - } - for move in moves - md=GameData::Move.get(move) - for move2 in movedatas - if md.function_code=="0A5" && move2[1].function_code=="000" && - md.type==move2[1].type && md.base_damage>=move2[1].base_damage - deleteAll.call(moves,move2[0]) - elsif md.function_code==move2[1].function_code && md.base_damage==0 && - md.accuracy>move2[1].accuracy - # Supersonic vs. Confuse Ray, etc. - deleteAll.call(moves,move2[0]) - elsif md.function_code=="006" && move2[1].function_code=="005" - deleteAll.call(moves,move2[0]) - elsif md.function_code==move2[1].function_code && md.base_damage!=0 && - md.type==move2[1].type && - (md.total_pp==15 || md.total_pp==10 || md.total_pp==move2[1].total_pp) && - (md.base_damage>move2[1].base_damage || - (md.base_damage==move2[1].base_damage && md.accuracy>move2[1].accuracy)) - # Surf, Flamethrower, Thunderbolt, etc. - deleteAll.call(moves,move2[0]) - end - end - end - return moves -end - -def baseStatTotal(species) - $baseStatTotal[species] = pbBaseStatTotal(species) if !$baseStatTotal[species] - return $baseStatTotal[species] -end - -def babySpecies(species) - $babySpecies[species] = GameData::Species.get(species).get_baby_species if !$babySpecies[species] - return $babySpecies[species] -end - -def minimumLevel(move) - $minimumLevel[species] = GameData::Species.get(species).minimum_level if !$minimumLevel[species] - return $minimumLevel[species] -end - -def evolutions(species) - $evolutions[species] = GameData::Species.get(species).get_evolutions(true) if !$evolutions[species] - return $evolutions[species] -end - -=begin -[3/10] -0-266 - 0-500 -[106] -267-372 - 380-500 -[95] -373-467 - 400-555 (nonlegendary) -468-563 - 400-555 (nonlegendary) -564-659 - 400-555 (nonlegendary) -660-755 - 400-555 (nonlegendary) -756-799 - 580-600 [legendary] (compat1==15 or compat2==15, genderbyte=255) -800-849 - 500- -850-881 - 580- -=end - - - -class BaseStatRestriction - def initialize(mn, mx) - @mn = mn - @mx = mx - end - - def isValid?(pkmn) - bst = baseStatTotal(pkmn.species) - return bst >= @mn && bst <= @mx - end -end - - - -class NonlegendaryRestriction - def isValid?(pkmn) - return true if !pkmn.genderless? - return false if pkmn.species_data.egg_groups.include?(:Undiscovered) - return true - end -end - - - -class InverseRestriction - def initialize(r) - @r = r - end - - def isValid?(pkmn) - return !@r.isValid?(pkmn) - end -end - - - -def withRestr(_rule,minbs,maxbs,legendary) - ret=PokemonChallengeRules.new.addPokemonRule(BaseStatRestriction.new(minbs,maxbs)) - if legendary==0 - ret.addPokemonRule(NonlegendaryRestriction.new) - elsif legendary==1 - ret.addPokemonRule(InverseRestriction.new(NonlegendaryRestriction.new)) - end - return ret -end - -# The Pokemon list is already roughly arranged by rank from weakest to strongest -def pbArrangeByTier(pokemonlist,rule) - tiers=[ - withRestr(rule,0,500,0), - withRestr(rule,380,500,0), - withRestr(rule,400,555,0), - withRestr(rule,400,555,0), - withRestr(rule,400,555,0), - withRestr(rule,400,555,0), - withRestr(rule,580,680,1), - withRestr(rule,500,680,0), - withRestr(rule,580,680,2) - ] - tierPokemon=[] - tiers.length.times do - tierPokemon.push([]) - end - for i in 0...pokemonlist.length - next if !rule.ruleset.isPokemonValid?(pokemonlist[i]) - validtiers=[] - for j in 0...tiers.length - tier=tiers[j] - if tier.ruleset.isPokemonValid?(pokemonlist[i]) - validtiers.push(j) - end - end - if validtiers.length>0 - vt=validtiers.length*i/pokemonlist.length - tierPokemon[validtiers[vt]].push(pokemonlist[i]) - end - end - # Now for each tier, sort the Pokemon in that tier - ret=[] - for i in 0...tiers.length - tierPokemon[i].sort! { |a,b| - bstA=baseStatTotal(a.species) - bstB=baseStatTotal(b.species) - if bstA==bstB - a.species<=>b.species - else - bstA<=>bstB - end - } - ret.concat(tierPokemon[i]) - end - return ret -end - -def hasMorePowerfulMove(moves,thismove) - thisdata=GameData::Move.get(thismove) - return false if thisdata.base_damage==0 - for move in moves - next if !move - moveData = GameData::Move.get(move) - if moveData.type==thisdata.type && moveData.base_damage>thisdata.base_damage - return true - end - end - return false -end - -def pbRandomPokemonFromRule(rule,trainer) - pkmn=nil - i=0 - iteration=-1 - loop do - iteration+=1 - species=nil - level=rule.ruleset.suggestedLevel - keys = GameData::Species::DATA.keys - loop do - loop do - species = keys[rand(keys.length)] - break if GameData::Species.get(species).form == 0 - end - r=rand(20) - bst=baseStatTotal(species) - next if level400 - next if r<10 && babySpecies(species)!=species - end - next if r<10 && babySpecies(species)==species - next if r<7 && evolutions(species).length>0 - break - end - ev = [] - GameData::Stat.each_main { |s| ev.push(s.id) if rand(100) < 50 } - nature = nil - keys = GameData::Nature::DATA.keys - loop do - nature = keys[rand(keys.length)] - nature_data = GameData::Nature.get(nature) - if [:LAX, :GENTLE].include?(nature_data.id) || nature_data.stat_changes.length == 0 - next if rand(20) < 19 - else - raised_emphasis = false - lowered_emphasis = false - nature_data.stat_changes.each do |change| - next if !ev.include?(change[0]) - raised_emphasis = true if change[1] > 0 - lowered_emphasis = true if change[1] < 0 - end - next if rand(10) < 6 && !raised_emphasis - next if rand(10) < 9 && lowered_emphasis - end - break - end - item = nil - $legalMoves={} if level!=$legalMovesLevel - $legalMoves[species]=pbGetLegalMoves2(species,level) if !$legalMoves[species] - itemlist=[ - :ORANBERRY,:SITRUSBERRY,:ADAMANTORB,:BABIRIBERRY, - :BLACKSLUDGE,:BRIGHTPOWDER,:CHESTOBERRY,:CHOICEBAND, - :CHOICESCARF,:CHOICESPECS,:CHOPLEBERRY,:DAMPROCK, - :DEEPSEATOOTH,:EXPERTBELT,:FLAMEORB,:FOCUSSASH, - :FOCUSBAND,:HEATROCK,:LEFTOVERS,:LIFEORB,:LIGHTBALL, - :LIGHTCLAY,:LUMBERRY,:OCCABERRY,:PETAYABERRY,:SALACBERRY, - :SCOPELENS,:SHEDSHELL,:SHELLBELL,:SHUCABERRY,:LIECHIBERRY, - :SILKSCARF,:THICKCLUB,:TOXICORB,:WIDELENS,:YACHEBERRY, - :HABANBERRY,:SOULDEW,:PASSHOBERRY,:QUICKCLAW,:WHITEHERB - ] - # Most used: Leftovers, Life Orb, Choice Band, Choice Scarf, Focus Sash - loop do - if rand(40)==0 - item = :LEFTOVERS - break - end - item = itemlist[rand(itemlist.length)] - next if !item - case item - when :LIGHTBALL - next if species != :PIKACHU - when :SHEDSHELL - next if species != :FORRETRESS && species != :SKARMORY - when :SOULDEW - next if species != :LATIOS && species != :LATIAS - when :FOCUSSASH - next if baseStatTotal(species)>450 && rand(10)<8 - when :ADAMANTORB - next if species != :DIALGA - when :PASSHOBERRY - next if species != :STEELIX - when :BABIRIBERRY - next if species != :TYRANITAR - when :HABANBERRY - next if species != :GARCHOMP - when :OCCABERRY - next if species != :METAGROSS - when :CHOPLEBERRY - next if species != :UMBREON - when :YACHEBERRY - next if species != :TORTERRA && species != :GLISCOR && species != :DRAGONAIR - when :SHUCABERRY - next if species != :HEATRAN - when :DEEPSEATOOTH - next if species != :CLAMPERL - when :THICKCLUB - next if species != :CUBONE && species != :MAROWAK - when :LIECHIBERRY - ev.push(:ATTACK) if !ev.include?(:ATTACK) && rand(100) < 50 - when :SALACBERRY - ev.push(:SPEED) if !ev.include?(:SPEED) && rand(100) < 50 - when :PETAYABERRY - ev.push(:SPECIAL_ATTACK) if !ev.include?(:SPECIAL_ATTACK) && rand(100) < 50 - end - break - end - if level < 10 && GameData::Item.exists?(:ORANBERRY) - item = :ORANBERRY if rand(40) == 0 || item == :SITRUSBERRY - elsif level > 20 && GameData::Item.exists?(:SITRUSBERRY) - item = :SITRUSBERRY if rand(40) == 0 || item == :ORANBERRY - end - moves=$legalMoves[species] - sketch=false - if moves[0] == :SKETCH - sketch=true - for i in 0...Pokemon::MAX_MOVES - moves[i]=pbRandomMove - end - end - next if moves.length==0 - if (moves|[]).length=1 - hasNormal=true if d.type == :NORMAL - hasPhysical=true if d.category==0 - hasSpecial=true if d.category==1 - end - end - if !hasPhysical && ev.include?(:ATTACK) - # No physical attack, but emphasizes Attack - next if rand(10)<8 - end - if !hasSpecial && ev.include?(:SPECIAL_ATTACK) - # No special attack, but emphasizes Special Attack - next if rand(10)<8 - end - r=rand(10) - next if r>6 && totalbasedamage>180 - next if r>8 && totalbasedamage>140 - next if totalbasedamage==0 && rand(20)!=0 - ############ - # Moves accepted - if hasPhysical && !hasSpecial - ev.push(:ATTACK) if rand(10)<8 - ev.delete(:SPECIAL_ATTACK) if rand(10)<8 - end - if !hasPhysical && hasSpecial - ev.delete(:ATTACK) if rand(10)<8 - ev.push(:SPECIAL_ATTACK) if rand(10)<8 - end - item = :LEFTOVERS if !hasNormal && item == :SILKSCARF - moves=newmoves - break - end - end - if item == :LIGHTCLAY && !moves.any? { |m| m == :LIGHTSCREEN || m = :REFLECT } - item = :LEFTOVERS - end - if item == :BLACKSLUDGE - type1 = GameData::Species.get(species).type1 - type2 = GameData::Species.get(species).type2 || type1 - item = :LEFTOVERS if type1 != :POISON && type2 != :POISON - end - if item == :HEATROCK && !moves.any? { |m| m == :SUNNYDAY } - item = :LEFTOVERS - end - if item == :DAMPROCK && !moves.any? { |m| m == :RAINDANCE } - item = :LEFTOVERS - end - if moves.any? { |m| m == :REST } - item = :LUMBERRY if rand(3)==0 - item = :CHESTOBERRY if rand(4)==0 - end - pk = PBPokemon.new(species, item, nature, moves[0], moves[1], moves[2], moves[3], ev) - pkmn = pk.createPokemon(level, 31, trainer) - i += 1 - break if rule.ruleset.isPokemonValid?(pkmn) - end - return pkmn -end - - - -class SingleMatch - attr_reader :opponentRating - attr_reader :opponentDeviation - attr_reader :score - attr_reader :kValue - - def initialize(opponentRating,opponentDev,score,kValue=16) - @opponentRating=opponentRating - @opponentDeviation=opponentDev - @score=score # -1=draw, 0=lose, 1=win - @kValue=kValue - end -end - - - -class MatchHistory - include Enumerable - - def each - @matches.each { |item| yield item } - end - - def length - @matches.length - end - - def [](i) - @matches[i] - end - - def initialize(thisPlayer) - @matches=[] - @thisPlayer=thisPlayer - end - - def addMatch(otherPlayer,result) - # 1=I won; 0=Other player won; -1: Draw - @matches.push(SingleMatch.new( - otherPlayer.rating,otherPlayer.deviation,result)) - end - - def updateAndClear() - @thisPlayer.update(@matches) - @matches.clear - end -end - - - -class PlayerRatingElo - attr_reader :rating - K_VALUE = 16 - - def initialize - @rating=1600.0 - @deviation=0 - @volatility=0 - @estimatedRating=nil - end - - def winChancePercent - return @estimatedRating if @estimatedRating - x=(1+10.0**((@rating-1600.0)/400.0)) - @estimatedRating=(x==0 ? 1.0 : 1.0/x) - return @estimatedRating - end - - def update(matches) - if matches.length == 0 - return - end - stake=0 - matches.length.times do - score=(match.score==-1) ? 0.5 : match.score - e=(1+10.0**((@rating-match.opponentRating)/400.0)) - stake+=match.kValue*(score-e) - end - @rating+=stake - end -end - - - -class PlayerRating - attr_reader :volatility - attr_reader :deviation - attr_reader :rating - - def initialize - @rating=1500.0 - @deviation=350.0 - @volatility=0.9 - @estimatedRating=nil - end - - def winChancePercent - return @estimatedRating if @estimatedRating - if (@deviation > 100) - # http://www.smogon.com/forums/showthread.php?t=55764 - otherRating=1500.0 - otherDeviation=350.0 - s=Math.sqrt(100000.0+@deviation*@deviation+otherDeviation*otherDeviation) - g=10.0**((otherRating-@rating)*0.79/s) - @estimatedRating=(1.0/(1.0+g))*100.0 # Percent chance that I win against opponent - else - # GLIXARE method - rds = @deviation * @deviation; - sqr = Math.sqrt(15.905694331435 * (rds + 221781.21786254)); - inner = (1500.0 - @rating) * Math::PI / sqr; - @estimatedRating=(10000.0 / (1.0 + (10.0**inner)) + 0.5) / 100.0; - end - return @estimatedRating - end - - def update(matches,system=1.2) - volatility = volatility2 - deviation = deviation2 - rating = rating2; - if matches.length == 0 - setDeviation2(Math.sqrt(deviation * deviation + volatility * volatility)) - return - end - g=[] - e=[] - score=[] - for i in 0...matches.length - match = matches[i] - g[i] = getGFactor(match.opponentDeviation) - e[i] = getEFactor(rating,match.opponentRating, g[i]) - score[i] = match.score - end - # Estimated variance - variance = 0.0 - for i in 0...matches.length - variance += g[i]*g[i]*e[i]*(1-e[i]) - end - variance=1.0/variance - # Improvement sum - sum = 0.0 - for i in 0...matches.length - v = score[i] - if (v != -1) - sum += g[i]*(v.to_f-e[i]) - end - end - volatility = getUpdatedVolatility(volatility,deviation,variance,sum,system) - # Update deviation - t = deviation * deviation + volatility * volatility - deviation = 1.0 / Math.sqrt(1.0 / t + 1.0 / variance) - # Update rating - rating = rating + deviation * deviation * sum - setRating2(rating) - setDeviation2(deviation) - setVolatility2(volatility) - end - - private - - attr_writer :volatility - - def rating2 - return (@rating-1500.0)/173.7178 - end - - def deviation2 - return (@deviation)/173.7178 - end - - def getGFactor(deviation) - # deviation is not yet in glicko2 - deviation/=173.7178 - return 1.0 / Math.sqrt(1.0 + (3.0*deviation*deviation) / (Math::PI*Math::PI)) - end - - def getEFactor(rating,opponentRating, g) - # rating is already in glicko2 - # opponentRating is not yet in glicko2 - opponentRating=(opponentRating-1500.0)/173.7178 - return 1.0 / (1.0 + Math.exp(-g * (rating - opponentRating))); - end - - alias volatility2 volatility - - def setVolatility2(value) - @volatility=value - end - - def setRating2(value) - @estimatedRating=nil - @rating=(value*173.7178)+1500.0 - end - - def setDeviation2(value) - @estimatedRating=nil - @deviation=(value*173.7178) - end - - def getUpdatedVolatility(volatility, deviation, variance,improvementSum, system) - improvement = improvementSum * variance - a = Math.log(volatility * volatility) - squSystem = system * system - squDeviation = deviation * deviation - squVariance = variance + variance - squDevplusVar = squDeviation + variance - x0 = a - 100.times { # Up to 100 iterations to avoid potentially infinite loops - e = Math.exp(x0) - d = squDevplusVar + e - squD = d * d - i = improvement / d - h1 = -(x0 - a) / squSystem - 0.5 * e * i * i - h2 = -1.0 / squSystem - 0.5 * e * squDevplusVar / squD - h2 += 0.5 * squVariance * e * (squDevplusVar - e) / (squD * d) - x1 = x0 - x0 -= h1 / h2 - break if ((x1 - x0).abs < 0.000001) - } - return Math.exp(x0 / 2.0) - end -end - - - - -class RuledTeam - def rating - @rating.winChancePercent - end - - def ratingRaw - [@rating.rating,@rating.deviation,@rating.volatility,@rating.winChancePercent] - end - - def ratingData - @rating - end - - def totalGames - (@totalGames||0)+self.games - end - - def updateRating - @totalGames=0 if !@totalGames - oldgames=self.games - @history.updateAndClear() - newgames=self.games - @totalGames+=(oldgames-newgames) - end - - def compare(other) - @rating.compare(other.ratingData) - end - - def addMatch(other,score) - @history.addMatch(other.ratingData,score) - end - - def games - @history.length - end - - attr_accessor :team - - def initialize(party,rule) - count=rule.ruleset.suggestedNumber - @team=[] - retnum=[] - loop do - for i in 0...count - retnum[i]=rand(party.length) - @team[i]=party[retnum[i]] - party.delete_at(retnum[i]) - end - break if rule.ruleset.isValid?(@team) - end - @totalGames=0 - @rating=PlayerRating.new - @history=MatchHistory.new(@rating) - end - - def [](i) - @team[i] - end - - def toStr - return "["+@rating.to_i.to_s+","+@games.to_i.to_s+"]" - end - - def length - return @team.length - end - - def load(party) - ret=[] - for i in 0...team.length - ret.push(party[team[i]]) - end - return ret - end -end - - - -def pbDecideWinnerEffectiveness(move, otype1, otype2, ability, scores) - data = GameData::Move.get(move) - return 0 if data.base_damage == 0 - atype = data.type - typemod = Effectiveness::NORMAL_EFFECTIVE_ONE ** 2 - if ability != :LEVITATE || data.type != :GROUND - mod1 = Effectiveness.calculate_one(atype, otype1) - mod2 = (otype1 == otype2) ? Effectiveness::NORMAL_EFFECTIVE_ONE : Effectiveness.calculate_one(atype, otype2) - if ability == :WONDERGUARD - mod1 = Effectiveness::NORMAL_EFFECTIVE_ONE if mod1 <= Effectiveness::NORMAL_EFFECTIVE_ONE - mod2 = Effectiveness::NORMAL_EFFECTIVE_ONE if mod2 <= Effectiveness::NORMAL_EFFECTIVE_ONE - end - typemod = mod1 * mod2 - end - return scores[0] if typemod == 0 # Ineffective - return scores[1] if typemod == 1 # Doubly not very effective - return scores[2] if typemod == 2 # Not very effective - return scores[3] if typemod == 4 # Normal effective - return scores[4] if typemod == 8 # Super effective - return scores[5] if typemod == 16 # Doubly super effective - return 0 -end - -def pbDecideWinnerScore(party0,party1,rating) - score=0 - types1=[] - types2=[] - abilities=[] - for j in 0...party1.length - types1.push(party1[j].type1) - types2.push(party1[j].type2) - abilities.push(party1[j].ability_id) - end - for i in 0...party0.length - for move in party0[i].moves - next if !move - for j in 0...party1.length - score+=pbDecideWinnerEffectiveness(move.id, - types1[j],types2[j],abilities[j],[-16,-8,0,4,12,20]) - end - end - basestatsum=baseStatTotal(party0[i].species) - score+=basestatsum/10 - score+=10 if party0[i].item # Not in Battle Dome ranking - end - score+=rating+rand(32) - return score -end - -def pbDecideWinner(party0,party1,rating0,rating1) - rating0=(rating0*15.0/100).round - rating1=(rating1*15.0/100).round - score0=pbDecideWinnerScore(party0,party1,rating0) - score1=pbDecideWinnerScore(party1,party0,rating1) - if score0==score1 - return 5 if rating0==rating1 - return (rating0>rating1) ? 1 : 2 - else - return (score0>score1) ? 1 : 2 - end -end - -def pbRuledBattle(team1,team2,rule) - decision=0 - if rand(100)!=0 - party1=[] - party2=[] - team1.length.times { |i| party1.push(team1[i]) } - team2.length.times { |i| party2.push(team2[i]) } - decision=pbDecideWinner(party1,party2,team1.rating,team2.rating) - else - level=rule.ruleset.suggestedLevel - trainer1=NPCTrainer.new("PLAYER1",1) - trainer2=NPCTrainer.new("PLAYER2",1) - items1=[] - items2=[] - team1.each_with_index do |p,i| - next if !p - if p.level!=level - p.level=level - p.calc_stats - end - items1[i]=p.item_id - trainer1.party.push(p) - end - team2.each_with_index do |p,i| - next if !p - if p.level!=level - p.level=level - p.calc_stats - end - items2[i]=p.item_id - trainer2.party.push(p) - end - scene=PokeBattle_DebugSceneNoLogging.new - battle=rule.createBattle(scene,trainer1,trainer2) - battle.debug=true - battle.controlPlayer=true - battle.internalBattle=false - decision=battle.pbStartBattle - #p [items1,items2] - team1.each_with_index do |p,i| - next if !p - p.heal - p.item = items1[i] - end - team2.each_with_index do |p,i| - next if !p - p.heal - p.item = items2[i] - end - end - if decision==1 # Team 1 wins - team1.addMatch(team2,1) - team2.addMatch(team1,0) - elsif decision==2 # Team 2 wins - team1.addMatch(team2,0) - team2.addMatch(team1,1) - else - team1.addMatch(team2,-1) - team2.addMatch(team1,-1) - end -end - -def getTypes(species) - species_data = GameData::Species.get(species) - type1 = species_data.type1 - type2 = species_data.type2 - return (type1 == type2) ? [type1] : [type1, type2] -end - -def pbTrainerInfo(pokemonlist,trfile,rules) - bttrainers=pbGetBTTrainers(trfile) - btpokemon=pbGetBTPokemon(trfile) - if bttrainers.length==0 - for i in 0...200 - yield(nil) if block_given? && i%50==0 - trainerid=0 - if GameData::TrainerType.exists?(:YOUNGSTER) && rand(30) == 0 - trainerid = :YOUNGSTER - else - tr_typekeys = GameData::TrainerType::DATA.keys - loop do - tr_type = tr_typekeys[rand(tr_typekeys.length)] - tr_type_data = GameData::TrainerType.get(tr_type) - next if tr_type_data.base_money >= 100 - trainerid = tr_type_data.id - end - end - gender = GameData::TrainerType.get(trainerid).gender - randomName=getRandomNameEx(gender,nil,0,12) - tr=[trainerid,randomName,_INTL("Here I come!"), - _INTL("Yes, I won!"),_INTL("Man, I lost!"),[]] - bttrainers.push(tr) - end - bttrainers.sort! { |a, b| - money1 = GameData::TrainerType.get(a[0]).base_money - money2 = GameData::TrainerType.get(b[0]).base_money - (money1 == money2) ? a[0].to_s <=> b[0].to_s : money1 <=> money2 - } - end - yield(nil) if block_given? - suggestedLevel=rules.ruleset.suggestedLevel - rulesetTeam=rules.ruleset.copy.clearPokemonRules - pkmntypes=[] - validities=[] - for pkmn in pokemonlist - pkmn.level=suggestedLevel if pkmn.level!=suggestedLevel - pkmntypes.push(getTypes(pkmn.species)) - validities.push(rules.ruleset.isPokemonValid?(pkmn)) - end - newbttrainers=[] - for btt in 0...bttrainers.length - yield(nil) if block_given? && btt%50==0 - trainerdata=bttrainers[btt] - pokemonnumbers=trainerdata[5] || [] - species=[] - types={} - #p trainerdata[1] - GameData::Type.each { |t| types[t.id] = 0 } - for pn in pokemonnumbers - pkmn=btpokemon[pn] - species.push(pkmn.species) - t=getTypes(pkmn.species) - t.each { |typ| types[typ] += 1 } - end - species|=[] # remove duplicates - count=0 - GameData::Type.each do |t| - if types[t.id] >= 5 - types[t.id] /= 4 - types[t.id] = 10 if types[t.id] > 10 - else - types[t.id] = 0 - end - count += types[t.id] - end - types[:NORMAL] = 1 if count == 0 - if pokemonnumbers.length==0 - GameData::Type.each { |t| types[t.id] = 1 } - end - numbers=[] - if pokemonlist - numbersPokemon=[] - # p species - for index in 0...pokemonlist.length - pkmn=pokemonlist[index] - next if !validities[index] - absDiff=((index*8/pokemonlist.length)-(btt*8/bttrainers.length)).abs - if species.include?(pkmn.species) - weight=[32,12,5,2,1,0,0,0][[absDiff,7].min] - if rand(40)0 && !numbers.include?(index) - numbers.push(index) - numbersPokemon.push(pokemonlist[index]) - break - end - } - end - break if numbers.length >= Settings::MAX_PARTY_SIZE && rules.ruleset.hasValidTeam?(numbersPokemon) - end - if numbers.length < Settings::MAX_PARTY_SIZE || !rules.ruleset.hasValidTeam?(numbersPokemon) - while numbers.length=10 - ret.delete_at(firstIndex) - end - ret.push(pk) - end - end - return ret -end - -def pbReplenishBattlePokemon(party,rule) - while party.length<20 - pkmn=pbRandomPokemonFromRule(rule,nil) - found=false - for pk in party - if isBattlePokemonDuplicate(pkmn,pk) - found=true; break - end - end - party.push(pkmn) if !found - end -end - -def pbGenerateChallenge(rule,tag) - oldrule=rule - yield(_INTL("Preparing to generate teams")) - rule=rule.copy.setNumber(2) - yield(nil) - party=load_data(tag+".rxdata") rescue [] - teams=load_data(tag+"teams.rxdata") rescue [] - if teams.length<10 - btpokemon=pbGetBTPokemon(tag) - if btpokemon && btpokemon.length!=0 - suggestedLevel=rule.ruleset.suggestedLevel - for pk in btpokemon - pkmn=pk.createPokemon(suggestedLevel,31,nil) - party.push(pkmn) if rule.ruleset.isPokemonValid?(pkmn) - end - end - end - yield(nil) - party=pbRemoveDuplicates(party) - yield(nil) - maxteams=600 - cutoffrating=65 - toolowrating=40 - iterations=11 - iterations.times do |iter| - save_data(party,tag+".rxdata") - yield(_INTL("Generating teams ({1} of {2})",iter+1,iterations)) - i=0 - while i=80 - teams[i]=RuledTeam.new(party,rule) - elsif teams[i].length<2 - teams[i]=RuledTeam.new(party,rule) - elsif i>=maxteams - teams[i]=nil - teams.compact! - elsif teams[i].totalGames>=250 - # retire - for j in 0...teams[i].length - party.push(teams[i][j]) - end - teams[i]=RuledTeam.new(party,rule) - elsif teams[i].ratinga.rating } - yield(_INTL("Simulating battles ({1} of {2})",iter+1,iterations)) - i=0; loop do - changed=false - teams.length.times { |j| - yield(nil) - other=j;5.times do - other=rand(teams.length) - next if other==j - end - next if other==j - changed=true - pbRuledBattle(teams[j],teams[other],rule) - } - # i+=1;break if i>=5 - i+=1 - gameCount=0 - for team in teams - gameCount+=team.games - end - #p [gameCount,teams.length,gameCount/teams.length] - yield(nil) - if (gameCount/teams.length)>=12 - #p "Iterations: #{i}" - for team in teams - games=team.games - team.updateRating - #p [games,team.totalGames,team.ratingRaw] if $INTERNAL - end - #p [gameCount,teams.length,gameCount/teams.length] - break - end - end - teams.sort! { |a,b| b.rating<=>a.rating } - save_data(teams,tag+"teams.rxdata") - end - party=[] - yield(nil) - teams.sort! { |a,b| a.rating<=>b.rating } - for team in teams - next if team.rating<=cutoffrating - for i in 0...team.length - party.push(team[i]) - end - end - rule=oldrule - yield(nil) - party=pbRemoveDuplicates(party) - yield(_INTL("Writing results")) - party=pbArrangeByTier(party,rule) - yield(nil) - pbTrainerInfo(party,tag,rule) { yield(nil) } - yield(nil) -end - -def pbWriteCup(id,rules) - return if !$DEBUG - trlists=(load_data("Data/trainer_lists.dat") rescue []) - list=[] - for i in 0...trlists.length - tr=trlists[i] - if tr[5] - list.push("*"+(tr[3].sub(/\.txt$/,""))) - else - list.push((tr[3].sub(/\.txt$/,""))) - end - end - cmd=0 - if trlists.length!=0 - cmd=pbMessage(_INTL("Generate Pokémon teams for this challenge?"), - [_INTL("NO"),_INTL("YES, USE EXISTING"),_INTL("YES, USE NEW")],1) - else - cmd=pbMessage(_INTL("Generate Pokémon teams for this challenge?"), - [_INTL("YES"),_INTL("NO")],2) - if cmd==0 - cmd=2 - elsif cmd==1 - cmd=0 - end - end - return if cmd==0 # No - if cmd==1 # Yes, use existing - cmd=pbMessage(_INTL("Choose a challenge."),list,-1) - if cmd>=0 - pbMessage(_INTL("This challenge will use the Pokémon list from {1}.",list[cmd])) - for i in 0...trlists.length - tr=trlists[i] - while !tr[5] && tr[2].include?(id) - tr[2].delete(id) - end - end - trlists[cmd][2].push(id) if !trlists[cmd][5] - save_data(trlists,"Data/trainer_lists.dat") - Graphics.update - Compiler.write_trainer_lists - end - return - # Yes, use new - elsif cmd==2 && !pbConfirmMessage(_INTL("This may take a long time. Are you sure?")) - return - end - mw=pbCreateMessageWindow - t=Time.now - pbGenerateChallenge(rules,id) { |message| - if (Time.now-t)>=5 - Graphics.update; t=Time.now - end - if message - pbMessageDisplay(mw,message,false) - Graphics.update; t=Time.now - end - } - pbDisposeMessageWindow(mw) - pbMessage(_INTL("Team generation complete.")) -end