Rearranged OrgBattle scripts

This commit is contained in:
Maruno17
2021-05-07 18:23:20 +01:00
parent 654be6c1de
commit 23996a4781
18 changed files with 3729 additions and 3823 deletions

View File

@@ -14,7 +14,7 @@ class Game_Character
attr_accessor :character_hue attr_accessor :character_hue
attr_reader :opacity attr_reader :opacity
attr_reader :blend_type attr_reader :blend_type
attr_reader :direction attr_accessor :direction
attr_accessor :pattern attr_accessor :pattern
attr_accessor :pattern_surf attr_accessor :pattern_surf
attr_accessor :lock_pattern attr_accessor :lock_pattern

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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<ivgroups[0]) ? ivs[0] : ivs[1]
party.push(rndpoke.createPokemon(rule.ruleset.suggestedLevel,indvalue,nil))
end
break if rule.ruleset.isValid?(party)
end
return party
end
def pbGenerateBattleTrainer(trainerid,rule)
bttrainers=pbGetBTTrainers(pbBattleChallenge.currentChallenge)
trainerdata=bttrainers[trainerid]
opponent=NPCTrainer.new(
pbGetMessageFromHash(MessageTypes::TrainerNames,trainerdata[1]),
trainerdata[0])
btpokemon=pbGetBTPokemon(pbBattleChallenge.currentChallenge)
# Individual Values
indvalues=31
indvalues=21 if trainerid<220
indvalues=18 if trainerid<200
indvalues=15 if trainerid<180
indvalues=12 if trainerid<160
indvalues=9 if trainerid<140
indvalues=6 if trainerid<120
indvalues=3 if trainerid<100
pokemonnumbers=trainerdata[5]
#p trainerdata
if pokemonnumbers.length<rule.ruleset.suggestedNumber
for n in pokemonnumbers
rndpoke=btpokemon[n]
pkmn=rndpoke.createPokemon(rule.ruleset.suggestedLevel,indvalues,opponent)
opponent.party.push(pkmn)
end
return opponent
end
loop do
opponent.party.clear
while opponent.party.length<rule.ruleset.suggestedNumber
rnd=pokemonnumbers[rand(pokemonnumbers.length)]
rndpoke=btpokemon[rnd]
pkmn=rndpoke.createPokemon(
rule.ruleset.suggestedLevel,indvalues,opponent)
opponent.party.push(pkmn)
end
break if rule.ruleset.isValid?(opponent.party)
end
return opponent
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
def pbIsBanned?(pokemon)
return StandardSpeciesRestriction.new.isValid?(pokemon)
end

File diff suppressed because it is too large Load Diff