From 308937a9f4a432da517da41a91f9f5934bfe5c92 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Sat, 30 Oct 2021 21:56:34 +0100 Subject: [PATCH] Refactored Day Care code --- .../002_Save data/005_Game_SaveConversions.rb | 31 + .../012_Overworld/002_Overworld_Metadata.rb | 8 +- .../012_Overworld/007_Overworld_DayCare.rb | 921 ++++++++++-------- .../002_ShadowPokemon_Other.rb | 11 +- .../003_Debug_MenuExtraCode.rb | 284 +++--- .../004_Compiler_MapsAndEvents.rb | 11 +- 6 files changed, 722 insertions(+), 544 deletions(-) diff --git a/Data/Scripts/002_Save data/005_Game_SaveConversions.rb b/Data/Scripts/002_Save data/005_Game_SaveConversions.rb index cc5560239..e6214de29 100644 --- a/Data/Scripts/002_Save data/005_Game_SaveConversions.rb +++ b/Data/Scripts/002_Save data/005_Game_SaveConversions.rb @@ -194,3 +194,34 @@ SaveData.register_conversion(:v20_rename_bag_variables) do end end end + +SaveData.register_conversion(:v20_refactor_day_care_variables) do + essentials_version 20 + display_title 'Refactoring Day Care variables' + to_value :global_metadata do |global| + global.instance_eval do + @day_care = DayCare.new if @day_care.nil? + if !@daycare.nil? + @daycare.each do |old_slot| + if !old_slot[0] + old_slot[0] = Pokemon.new(:MANAPHY, 50) + old_slot[1] = 4 + end + next if !old_slot[0] + @day_care.slots.each do |slot| + next if slot.filled? + slot.instance_eval do + @pokemon = old_slot[0] + @initial_level = old_slot[1] + end + end + end + @day_care.egg_generated = ((@daycareEgg.is_a?(Numeric) && @daycareEgg > 0) || @daycareEgg == true) + @day_care.step_counter = @daycareEggSteps + @daycare = nil + @daycareEgg = nil + @daycareEggSteps = nil + end + end + end +end diff --git a/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb b/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb index 4969e2b70..0829fa51e 100644 --- a/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb +++ b/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb @@ -23,9 +23,7 @@ class PokemonGlobalMetadata attr_accessor :pokedexIndex # Last species viewed per Dex attr_accessor :pokedexMode # Search mode # Day Care - attr_accessor :daycare - attr_accessor :daycareEgg - attr_accessor :daycareEggSteps + attr_accessor :day_care # Special battle modes attr_accessor :safariState attr_accessor :bugContestState @@ -79,9 +77,7 @@ class PokemonGlobalMetadata @pokedexIndex[i] = 0 end # Day Care - @daycare = [[nil,0],[nil,0]] - @daycareEgg = false - @daycareEggSteps = 0 + @day_care = DayCare.new # Special battle modes @safariState = nil @bugContestState = nil diff --git a/Data/Scripts/012_Overworld/007_Overworld_DayCare.rb b/Data/Scripts/012_Overworld/007_Overworld_DayCare.rb index df0244ccb..2bca2ff74 100644 --- a/Data/Scripts/012_Overworld/007_Overworld_DayCare.rb +++ b/Data/Scripts/012_Overworld/007_Overworld_DayCare.rb @@ -1,403 +1,562 @@ +class DayCare + #============================================================================= + # Code that generates an egg based on two given Pokémon. + #============================================================================= + module EggGenerator + module_function + + def generate(mother, father) + # Determine which Pokémon is the mother and which is the father + # Ensure mother is female, if the pair contains a female + # Ensure father is male, if the pair contains a male + # Ensure father is genderless, if the pair is a genderless with Ditto + if mother.male? || father.female? || mother.genderless? + mother, father = father, mother + end + mother_data = [mother, mother.species_data.egg_groups.include?(:Ditto)] + father_data = [father, father.species_data.egg_groups.include?(:Ditto)] + # Determine which parent the egg's species is based from + species_parent = (mother_data[1]) ? father : mother + # Determine the egg's species + baby_species = determine_egg_species(species_parent.species, mother, father) + mother_data.push(mother.species_data.breeding_can_produce?(baby_species)) + father_data.push(father.species_data.breeding_can_produce?(baby_species)) + # Generate egg + egg = generate_basic_egg(baby_species) + # Inherit properties from parent(s) + inherit_form(egg, species_parent, mother_data, father_data) + inherit_nature(egg, mother, father) + inherit_ability(egg, mother_data, father_data) + inherit_moves(egg, mother_data, father_data) + inherit_IVs(egg, mother, father) + inherit_poke_ball(egg, mother_data, father_data) + # Calculate other properties of the egg + set_shininess(egg, mother, father) # Masuda method and Shiny Charm + set_pokerus(egg) + # Recalculate egg's stats + egg.calc_stats + return egg + end + + def determine_egg_species(parent_species, mother, father) + ret = GameData::Species.get(parent_species).get_baby_species(true, mother.item_id, father.item_id) + # Check for alternate offspring (i.e. Nidoran M/F, Volbeat/Illumise, Manaphy/Phione) + offspring = GameData::Species.get(ret).offspring + ret = offspring.sample if offspring.length > 0 + return ret + end + + def generate_basic_egg(species) + egg = Pokemon.new(species, Settings::EGG_LEVEL) + egg.name = _INTL("Egg") + egg.steps_to_hatch = egg.species_data.hatch_steps + egg.obtain_text = _INTL("Day-Care Couple") + egg.happiness = 120 + egg.form = 0 if species == :SINISTEA + return egg + end + + def inherit_form(egg, species_parent, mother, father) + # mother = [mother, mother_ditto, mother_in_family] + # father = [father, father_ditto, father_in_family] + # Inherit form from the parent that determined the egg's species + if species_parent.species_data.has_flag?("InheritFormFromMother") + egg.form = species_parent.form + end + # Inherit form from a parent holding an Ever Stone + [mother, father].each do |parent| + next if !parent[2] # Parent isn't a related species to the egg + next if !parent[0].species_data.has_flag?("InheritFormWithEverStone") + next if !parent[0].hasItem?(:EVERSTONE) + egg.form = parent[0].form + break + end + end + + def get_moves_to_inherit(egg, mother, father) + # mother = [mother, mother_ditto, mother_in_family] + # father = [father, father_ditto, father_in_family] + move_father = (father[1]) ? mother[0] : father[0] + move_mother = (father[1]) ? father[0] : mother[0] + moves = [] + # Get level-up moves known by both parents + egg.getMoveList. each do |move| + next if move[0] <= egg.level # Could already know this move by default + next if !mother[0].hasMove?(move[1]) || !father[0].hasMove?(move[1]) + moves.push(move[1]) + end + # Inherit Machine moves from father (or non-Ditto genderless parent) + if Settings::BREEDING_CAN_INHERIT_MACHINE_MOVES && !move_father.female? + GameData::Item.each do |i| + move = i.move + next if !move + next if !move_father.hasMove?(move) || !egg.compatible_with_move?(move) + moves.push(move) + end + end + # Inherit egg moves from each parent + if !move_father.female? + egg.species_data.egg_moves.each do |move| + moves.push(move) if move_father.hasMove?(move) + end + end + if Settings::BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER && move_mother.female? + egg.species_data.egg_moves.each do |move| + moves.push(move) if move_mother.hasMove?(move)) + end + end + # Learn Volt Tackle if a parent has a Light Ball and is in the Pichu family + if egg.species == :PICHU && GameData::Move.exists?(:VOLTTACKLE) + if (father[2] && father[0].hasItem?(:LIGHTBALL)) || + (mother[2] && mother[0].hasItem?(:LIGHTBALL)) + moves.push(:VOLTTACKLE) + end + end + return moves + end + + def inherit_moves(egg, mother, father) + moves = get_moves_to_inherit(egg, mother, father) + # Remove duplicates (keeping the latest ones) + moves = moves.reverse + moves |= [] # remove duplicates + moves = moves.reverse + # Learn moves + first_move_index = moves.length - Pokemon::MAX_MOVES + first_move_index = 0 if first_move_index < 0 + (first_move_index...moves.length).each { |i| egg.learn_move(moves[i]) } + end + + def inherit_nature(egg, mother, father) + new_natures = [] + new_natures.push(mother.nature) if mother.hasItem?(:EVERSTONE) + new_natures.push(father.nature) if father.hasItem?(:EVERSTONE) + return if new_natures.empty? + egg.nature = new_natures.sample + end + + # If a Pokémon is bred with a Ditto, that Pokémon can pass down its Hidden + # Ability (80% chance). If neither Pokémon are Ditto, then the mother can + # pass down its ability (80% chance if Hidden, 60% chance if not). + # NOTE: This is how ability inheritance works in Gen 6+. Gen 5 is more + # restrictive, and even works differently between BW and B2W2, and I + # don't think that is worth adding in. Gen 4 and lower don't have + # ability inheritance at all, and again, I'm not bothering to add that + # in. + def inherit_ability(egg, mother, father) + # mother = [mother, mother_ditto, mother_in_family] + # father = [father, father_ditto, father_in_family] + parent = (mother[1]) ? father[0] : mother[0] # The female or non-Ditto parent + if parent.hasHiddenAbility? + egg.ability_index = parent.ability_index if rand(100) < 60 + elsif !mother[1] && !father[1] # If neither parent is a Ditto + if rand(100) < 80 + egg.ability_index = mother[0].ability_index + else + egg.ability_index = (mother[0].ability_index + 1) % 2 + end + end + end + + # NOTE: Destiny Bond's effect is only in Gen 6+, but I don't think it's + # worth excluding it if the mechanics generation is 5 or lower. + def inherit_IVs(egg, mother, father) + # Get all stats + stats = [] + GameData::Stat.each_main { |s| stats.push(s) } + # Get the number of stats to inherit + inherit_count = 3 + inherit_count = 5 if mother.hasItem?(:DESTINYKNOT) || father.hasItem?(:DESTINYKNOT) + # Inherit IV because of Power items (if both parents have a Power item, + # then only a random one of them is inherited) + power_items = [ + [:POWERWEIGHT, :HP], + [:POWERBRACER, :ATTACK], + [:POWERBELT, :DEFENSE], + [:POWERLENS, :SPECIAL_ATTACK], + [:POWERBAND, :SPECIAL_DEFENSE], + [:POWERANKLET, :SPEED] + ] + power_stats = [] + [mother, father].each do |parent| + power_items.each do |item| + next if !parent.hasItem?(item[0]) + power_stats.push(item[1], parent.iv[item[1]]) + break + end + end + if power_stats.length > 0 + power_stat = power_stats.sample + egg.iv[power_stat[0]] = power_stat[1] + stats.delete(power_stat[0]) # Don't try to inherit this stat's IV again + inherit_count -= 1 + end + # Inherit the rest of the IVs + chosen_stats = stats.sample(inherit_count) + chosen_stats.each { |stat| egg.iv[stat] = [mother, father].sample.iv[stat] } + end + + # Poké Balls can only be inherited from parents that are related to the + # egg's species. + # NOTE: This is how Poké Ball inheritance works in Gen 7+. Gens 5 and lower + # don't have Poké Ball inheritance at all. In Gen 6, only a female + # parent can pass down its Poké Ball. I don't think it's worth adding + # in these variations on the mechanic. + # NOTE: The official games treat Nidoran M/F and Volbeat/Illumise as + # unrelated for the purpose of this mechanic. Essentials treats them + # as related and allows them to pass down their Poké Balls. + def inherit_poke_ball(egg, mother, father) + # mother = [mother, mother_ditto, mother_in_family] + # father = [father, father_ditto, father_in_family] + balls = [] + [mother, father].each do |parent| + balls.push(parent[0].poke_ball) if parent[2] + end + balls.delete(:MASTERBALL) # Can't inherit this Ball + balls.delete(:CHERISHBALL) # Can't inherit this Ball + egg.poke_ball = balls.sample if !balls.empty? + end + + # NOTE: There is a bug in Gen 8 that skips the original generation of an + # egg's personal ID if the Masuda Method/Shiny Charm can cause any + # rerolls. Essentials doesn't have this bug, meaning eggs are slightly + # more likely to be shiny (in Gen 8+ mechanics) than in Gen 8 itself. + def set_shininess(egg, mother, father) + shiny_retries = 0 + if father.owner.language != mother.owner.language + shiny_retries += (Settings::MECHANICS_GENERATION >= 8) ? 6 : 5 + end + shiny_retries += 2 if $bag.has?(:SHINYCHARM) + return if shiny_retries == 0 + shiny_retries.times do + break if egg.shiny? + egg.shiny = nil # Make it recalculate shininess + egg.personalID = rand(2**16) | rand(2**16) << 16 + end + end + + def set_pokerus(egg) + egg.givePokerus if rand(65536) < Settings::POKERUS_CHANCE + end + end + + #============================================================================= + # A slot in the Day Care, which can contain a Pokémon. + #============================================================================= + class DayCareSlot + attr_reader :pokemon + + def initialize + reset + end + + def reset + @pokemon = nil + @initial_level = 0 + end + + def deposit(pkmn) + @pokemon = pkmn + @pokemon.heal + @pokemon.form = 0 if @pokemon.isSpecies?(:SHAYMIN) + @initial_level = pkmn.level + end + + def filled? + return !@pokemon.nil? + end + + def pokemon_name + return (filled?) ? @pokemon.name : "" + end + + def level_gain + return (filled?) ? @pokemon.level - @initial_level : 0 + end + + def cost + return (level_gain + 1) * 100 + end + + def choice_text + return nil if !filled? + if @pokemon.male? + return choices.push(_INTL("{1} (♂, Lv.{2})", @pokemon.name, @pokemon.level)) + elsif @pokemon.female? + return choices.push(_INTL("{1} (♀, Lv.{2})", @pokemon.name, @pokemon.level)) + end + return choices.push(_INTL("{1} (Lv.{2})", @pokemon.name, @pokemon.level)) + end + + def add_exp(amount = 1) + return if !filled? + max_exp = @pokemon.growth_rate.maximum_exp + return if @pokemon.exp >= max_exp + old_level = @pokemon.level + @pokemon.exp += amount + return if @pokemon.level == old_level + @pokemon.calc_stats + move_list = @pokemon.getMoveList + move_list.each { |move| @pokemon.learn_move(move[1]) if move[0] == @pokemon.level } + end + end + + #============================================================================= + + attr_reader :slots + attr_accessor :egg_generated + attr_accessor :step_counter + attr_accessor :gain_exp + + MAX_SLOTS = 2 + + def initialize + @slots = [] + MAX_SLOTS.times { @slots.push(DayCareSlot.new) } + reset_egg_counters + @gain_exp = true + end + + def [](index) + return @slots[index] + end + + def reset_egg_counters + @egg_generated = false + @step_counter = 0 + end + + def count + return @slots.select { |slot| slot.filled? }.length + end + + def get_compatibility + return compatibility + end + + def generate_egg + return nil if self.count != 2 + pkmn1, pkmn2 = pokemon_pair + return EggGenerator.generate(pkmn1, pkmn2) + end + + def update_on_step_taken + # Make an egg available at the Day Care + @step_counter += 1 + if @step_counter >= 256 + @step_counter = 0 + if !@egg_generated && count == 2 + compat = compatibility + egg_chance = [0, 20, 50, 70][compat] + egg_chance = [0, 40, 80, 88][compat] if $bag.has?(:OVALCHARM) + @egg_generated = true if rand(100) < egg_chance + end + end + # Day Care Pokémon gain Exp/moves + if @gain_exp + @slots.each { |slot| slot.add_exp } + end + end + + #============================================================================= + + def self.count + return $PokemonGlobal.day_care.count + end + + def self.egg_generated? + return $PokemonGlobal.day_care.egg_generated + end + + def self.reset_egg_counters + $PokemonGlobal.day_care.reset_egg_counters + end + + def self.get_details(index, name_var, cost_var) + day_care = $PokemonGlobal.day_care + $game_variables[name_var] = day_care[index].pokemon_name if name_var > 0 + $game_variables[cost_var] = day_care[index].cost if cost_var > 0 + end + + def self.get_level_gain(index, name_var, level_var) + day_care = $PokemonGlobal.day_care + $game_variables[name_var] = day_care[index].pokemon_name if name_var > 0 + $game_variables[level_var] = day_care[index].level_gain if level_var > 0 + end + + def self.get_compatibility(compat_var) + $game_variables[compat_var] = $PokemonGlobal.day_care.get_compatibility if compat_var > 0 + end + + def self.deposit(party_index) + day_care = $PokemonGlobal.day_care + pkmn = $player.party[party_index] + raise _INTL("No Pokémon at index {1} in party.", party_index) if pkmn.nil? + day_care.slots.each do |slot| + next if slot.filled? + slot.deposit(pkmn) + $player.party.delete_at(party_index) + day_care.reset_egg_counters + return + end + raise _INTL("No room to deposit a Pokémon.") + end + + def self.withdraw(index) + day_care = $PokemonGlobal.day_care + slot = day_care[index] + if !slot.filled? + raise _INTL("No Pokémon found in slot {1}.", index) + elsif $player.party_full? + raise _INTL("No room in party for Pokémon.") + end + $player.party.push(slot.pokemon) + slot.reset + day_care.reset_egg_counters + end + + def self.choose(text, choice_var) + day_care = $PokemonGlobal.day_care + case day_care.count + when 0 + raise _INTL("No Pokémon found in Day Care to choose from.") + when 1 + day_care.slots.each_with_index { |slot, i| $game_variables[choice_var] = i if slot.filled? } + else + commands = [] + indices = [] + day_care.slots.each_with_index do |slot, i| + text = slot.choice_text + next if !text + commands.push(text) + indices.push(i) + end + commands.push(_INTL("CANCEL")) + command = pbMessage(text, commands, commands.length) + $game_variables[choice_var] = (command == commands.length - 1) ? -1 : indices[command] + end + end + + def self.collect_egg + day_care = $PokemonGlobal.day_care + egg = day_care.generate_egg + raise _INTL("Couldn't generate the egg.") if egg.nil? + raise _INTL("No room in party for egg.") if $player.party_full? + $player.party.push(egg) + day_care.reset_egg_counters + end + + #============================================================================= + + private + + def pokemon_pair + pkmn1 = nil + pkmn2 = nil + @slots.each do |slot| + next if !slot.filled? + if pkmn1.nil? + pkmn1 = slot.pokemon + else + pkmn2 = slot.pokemon + end + end + raise _INTL("Couldn't find 2 deposited Pokémon.") if pkmn2.nil? + return pkmn1, pkmn2 + end + + def pokemon_in_ditto_egg_group?(pkmn) + return pkmn.species_data.egg_groups.include?(:Ditto) + end + + def compatible_gender?(pkmn1, pkmn2) + return true if pkmn1.female? && pkmn2.male? + return true if pkmn1.male? && pkmn2.female? + ditto1 = pokemon_in_ditto_egg_group?(pkmn1) + ditto2 = pokemon_in_ditto_egg_group?(pkmn2) + return true if ditto1 && !ditto2 + return true if ditto2 && !ditto1 + return false + end + + def compatibility + return 0 if self.count != 2 + # Find the Pokémon whose compatibility is being calculated + pkmn1, pkmn2 = pokemon_pair + # Shadow Pokémon cannot breed + return 0 if pkmn1.shadowPokemon? || pkmn2.shadowPokemon? + # Pokémon in the Undiscovered egg group cannot breed + egg_groups1 = pkmn1.species_data.egg_groups + egg_groups2 = pkmn2.species_data.egg_groups + return 0 if egg_groups1.include?(:Undiscovered) || + egg_groups2.include?(:Undiscovered) + # Pokémon that don't share an egg group (and neither is in the Ditto group) + # cannot breed + return 0 if !egg_groups1.include?(:Ditto) && + !egg_groups2.include?(:Ditto) && + (egg_groups1 & egg_groups2).length == 0 + # Pokémon with incompatible genders cannot breed + return 0 if !compatible_gender?(pkmn1, pkmn2) + # Pokémon can breed; calculate a compatibility factor + ret = 1 + ret += 1 if pkmn1.species == pkmn2.species + ret += 1 if pkmn1.owner.id != pkmn2.owner.id + return ret + end +end + #=============================================================================== -# Query information about Pokémon in the Day Care. +# With each step taken, add Exp to Pokémon in the Day Care and try to generate +# an egg. +#=============================================================================== +Events.onStepTaken += proc { |_sender,_e| + $PokemonGlobal.day_care.update_on_step_taken +} + +#=============================================================================== +# Deprecated methods #=============================================================================== -# Returns the number of Pokémon in the Day Care. def pbDayCareDeposited - ret = 0 - for i in 0...2 - ret += 1 if $PokemonGlobal.daycare[i][0] - end - return ret + Deprecation.warn_method('pbDayCareDeposited', 'v21', 'DayCare.count') + return DayCare.count end -# Get name/cost info of a particular Pokémon in the Day Care. -def pbDayCareGetDeposited(index,nameVariable,costVariable) - pkmn = $PokemonGlobal.daycare[index][0] - return false if !pkmn - cost = pbDayCareGetCost(index) - $game_variables[nameVariable] = pkmn.name if nameVariable>=0 - $game_variables[costVariable] = cost if costVariable>=0 +def pbDayCareGetDeposited(index, name_var, cost_var) + Deprecation.warn_method('pbDayCareGetDeposited', 'v21', 'DayCare.get_details(index, name_var, cost_var)') + DayCare.get_details(index, name_var, cost_var) end -# Get name/levels gained info of a particular Pokémon in the Day Care. -def pbDayCareGetLevelGain(index,nameVariable,levelVariable) - pkmn = $PokemonGlobal.daycare[index][0] - return false if !pkmn - $game_variables[nameVariable] = pkmn.name - $game_variables[levelVariable] = pkmn.level-$PokemonGlobal.daycare[index][1] - return true +def pbDayCareGetLevelGain(index, name_var, level_var) + Deprecation.warn_method('pbDayCareGetLevelGain', 'v21', 'DayCare.get_level_gain(index, name_var, level_var)') + DayCare.get_level_gain(index, name_var, level_var) end -def pbDayCareGetCost(index) - pkmn = $PokemonGlobal.daycare[index][0] - return 0 if !pkmn - cost = pkmn.level-$PokemonGlobal.daycare[index][1]+1 - cost *= 100 - return cost -end - -# Returns whether an egg is waiting to be collected. -def pbEggGenerated? - return false if pbDayCareDeposited!=2 - return $PokemonGlobal.daycareEgg==1 -end - - - -#=============================================================================== -# Manipulate Pokémon in the Day Care. -#=============================================================================== -def pbDayCareDeposit(index) - for i in 0...2 - next if $PokemonGlobal.daycare[i][0] - pkmn = $player.party[index] - pkmn.heal - pkmn.form = 0 if pkmn.isSpecies?(:SHAYMIN) - $PokemonGlobal.daycare[i][0] = pkmn - $PokemonGlobal.daycare[i][1] = pkmn.level - $player.party.delete_at(index) - $PokemonGlobal.daycareEgg = 0 - $PokemonGlobal.daycareEggSteps = 0 - return - end - raise _INTL("No room to deposit a Pokémon") +def pbDayCareDeposit(party_index) + Deprecation.warn_method('pbDayCareDeposit', 'v21', 'DayCare.deposit(party_index)') + DayCare.deposit(party_index) end def pbDayCareWithdraw(index) - if !$PokemonGlobal.daycare[index][0] - raise _INTL("There's no Pokémon here...") - elsif $player.party_full? - raise _INTL("Can't store the Pokémon...") - else - $player.party[$player.party.length] = $PokemonGlobal.daycare[index][0] - $PokemonGlobal.daycare[index][0] = nil - $PokemonGlobal.daycare[index][1] = 0 - $PokemonGlobal.daycareEgg = 0 - end + Deprecation.warn_method('pbDayCareWithdraw', 'v21', 'DayCare.withdraw(index)') + DayCare.withdraw(index) end -def pbDayCareChoose(text,variable) - count = pbDayCareDeposited - if count==0 - raise _INTL("There's no Pokémon here...") - elsif count==1 - $game_variables[variable] = ($PokemonGlobal.daycare[0][0]) ? 0 : 1 - else - choices = [] - for i in 0...2 - pokemon = $PokemonGlobal.daycare[i][0] - if pokemon.male? - choices.push(_ISPRINTF("{1:s} (♂, Lv.{2:d})",pokemon.name,pokemon.level)) - elsif pokemon.female? - choices.push(_ISPRINTF("{1:s} (♀, Lv.{2:d})",pokemon.name,pokemon.level)) - else - choices.push(_ISPRINTF("{1:s} (Lv.{2:d})",pokemon.name,pokemon.level)) - end - end - choices.push(_INTL("CANCEL")) - command = pbMessage(text,choices,choices.length) - $game_variables[variable] = (command==2) ? -1 : command - end +def pbDayCareChoose(text, choice_var) + Deprecation.warn_method('pbDayCareChoose', 'v21', 'DayCare.choose(text, choice_var)') + DayCare.choose(text, choice_var) end - - -#=============================================================================== -# Check compatibility of Pokémon in the Day Care. -#=============================================================================== -def pbIsDitto?(pkmn) - return pkmn.species_data.egg_groups.include?(:Ditto) +def pbDayCareGetCompatibility(compat_var) + Deprecation.warn_method('pbDayCareGetCompatibility', 'v21', 'DayCare.get_compatibility(compat_var)') + DayCare.get_compatibility(compat_var) end -def pbDayCareCompatibleGender(pkmn1, pkmn2) - return true if pkmn1.female? && pkmn2.male? - return true if pkmn1.male? && pkmn2.female? - ditto1 = pbIsDitto?(pkmn1) - ditto2 = pbIsDitto?(pkmn2) - return true if ditto1 && !ditto2 - return true if ditto2 && !ditto1 - return false +def pbEggGenerated? + Deprecation.warn_method('pbEggGenerated?', 'v21', 'DayCare.egg_generated?') + return DayCare.egg_generated? end -def pbDayCareGetCompat - return 0 if pbDayCareDeposited != 2 - pkmn1 = $PokemonGlobal.daycare[0][0] - pkmn2 = $PokemonGlobal.daycare[1][0] - # Shadow Pokémon cannot breed - return 0 if pkmn1.shadowPokemon? || pkmn2.shadowPokemon? - # Pokémon in the Undiscovered egg group cannot breed - egg_groups1 = pkmn1.species_data.egg_groups - egg_groups2 = pkmn2.species_data.egg_groups - return 0 if egg_groups1.include?(:Undiscovered) || - egg_groups2.include?(:Undiscovered) - # Pokémon that don't share an egg group (and neither is in the Ditto group) - # cannot breed - return 0 if !egg_groups1.include?(:Ditto) && - !egg_groups2.include?(:Ditto) && - (egg_groups1 & egg_groups2).length == 0 - # Pokémon with incompatible genders cannot breed - return 0 if !pbDayCareCompatibleGender(pkmn1, pkmn2) - # Pokémon can breed; calculate a compatibility factor - ret = 1 - ret += 1 if pkmn1.species == pkmn2.species - ret += 1 if pkmn1.owner.id != pkmn2.owner.id - return ret -end - -def pbDayCareGetCompatibility(variable) - $game_variables[variable] = pbDayCareGetCompat -end - - - -#=============================================================================== -# Generate an Egg based on Pokémon in the Day Care. -#=============================================================================== def pbDayCareGenerateEgg - return if pbDayCareDeposited != 2 - raise _INTL("Can't store the egg.") if $player.party_full? - pkmn0 = $PokemonGlobal.daycare[0][0] - pkmn1 = $PokemonGlobal.daycare[1][0] - mother = nil - father = nil - babyspecies = nil - ditto0 = pbIsDitto?(pkmn0) - ditto1 = pbIsDitto?(pkmn1) - if pkmn0.female? || ditto0 - mother = pkmn0 - father = pkmn1 - babyspecies = (ditto0) ? father.species : mother.species - else - mother = pkmn1 - father = pkmn0 - babyspecies = (ditto1) ? father.species : mother.species - end - # Determine the egg's species - babyspecies = GameData::Species.get(babyspecies).get_baby_species(true, mother.item_id, father.item_id) - case babyspecies - when :MANAPHY - babyspecies = :PHIONE if GameData::Species.exists?(:PHIONE) - when :NIDORANfE, :NIDORANmA - if GameData::Species.exists?(:NIDORANfE) && GameData::Species.exists?(:NIDORANmA) - babyspecies = [:NIDORANfE, :NIDORANmA][rand(2)] - end - when :VOLBEAT, :ILLUMISE - if GameData::Species.exists?(:VOLBEAT) && GameData::Species.exists?(:ILLUMISE) - babyspecies = [:VOLBEAT, :ILLUMISE][rand(2)] - end - end - # Generate egg - egg = Pokemon.new(babyspecies, Settings::EGG_LEVEL) - # Randomise personal ID - pid = rand(65536) - pid |= (rand(65536)<<16) - egg.personalID = pid - # Inherit form from mother - parent = (ditto0 || (!pkmn0.female? && ditto1)) ? father : mother - if parent.species_data.has_flag?("InheritFormFromMother") - egg.form = parent.form - end - # Inherit form from parent holding an Ever Stone - [mother, father].each do |parent| - next if !parent.species_data.get_baby_species(true, mother.item_id, father.item_id) == babyspecies - next if !parent.species_data.has_flag?("InheritFormWithEverStone") - next if !parent.hasItem?(:EVERSTONE) - egg.form = parent.form - break - end - egg.form = 0 if babyspecies == :SINISTEA - # Inheriting Moves - moves = [] - othermoves = [] - movefather = father - movemother = mother - if pbIsDitto?(movefather) && !mother.female? - movefather = mother - movemother = father - end - # Initial Moves - initialmoves = egg.getMoveList - for k in initialmoves - if k[0] <= Settings::EGG_LEVEL - moves.push(k[1]) - elsif mother.hasMove?(k[1]) && father.hasMove?(k[1]) - othermoves.push(k[1]) - end - end - # Inheriting Natural Moves - for move in othermoves - moves.push(move) - end - # Inheriting Machine Moves - if Settings::BREEDING_CAN_INHERIT_MACHINE_MOVES - GameData::Item.each do |i| - atk = i.move - next if !atk - next if !egg.compatible_with_move?(atk) - next if !movefather.hasMove?(atk) - moves.push(atk) - end - end - # Inheriting Egg Moves - babyEggMoves = egg.species_data.egg_moves - if movefather.male? - babyEggMoves.each { |m| moves.push(m) if movefather.hasMove?(m) } - end - if Settings::BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER - babyEggMoves.each { |m| moves.push(m) if movemother.hasMove?(m) } - end - # Volt Tackle - lightball = false - if (father.isSpecies?(:PIKACHU) || father.isSpecies?(:RAICHU)) && - father.hasItem?(:LIGHTBALL) - lightball = true - end - if (mother.isSpecies?(:PIKACHU) || mother.isSpecies?(:RAICHU)) && - mother.hasItem?(:LIGHTBALL) - lightball = true - end - if lightball && babyspecies == :PICHU && GameData::Move.exists?(:VOLTTACKLE) - moves.push(:VOLTTACKLE) - end - moves = moves.reverse - moves |= [] # remove duplicates - moves = moves.reverse - # Assembling move list - first_move_index = moves.length - Pokemon::MAX_MOVES - first_move_index = 0 if first_move_index < 0 - finalmoves = [] - for i in first_move_index...moves.length - finalmoves.push(Pokemon::Move.new(moves[i])) - end - # Inheriting Individual Values - ivs = {} - GameData::Stat.each_main { |s| ivs[s.id] = rand(Pokemon::IV_STAT_LIMIT + 1) } - ivinherit = [] - for i in 0...2 - parent = [mother,father][i] - ivinherit[i] = :HP if parent.hasItem?(:POWERWEIGHT) - ivinherit[i] = :ATTACK if parent.hasItem?(:POWERBRACER) - ivinherit[i] = :DEFENSE if parent.hasItem?(:POWERBELT) - ivinherit[i] = :SPECIAL_ATTACK if parent.hasItem?(:POWERLENS) - ivinherit[i] = :SPECIAL_DEFENSE if parent.hasItem?(:POWERBAND) - ivinherit[i] = :SPEED if parent.hasItem?(:POWERANKLET) - end - num = 0 - r = rand(2) - 2.times do - if ivinherit[r]!=nil - parent = [mother,father][r] - ivs[ivinherit[r]] = parent.iv[ivinherit[r]] - num += 1 - break - end - r = (r+1)%2 - end - limit = (mother.hasItem?(:DESTINYKNOT) || father.hasItem?(:DESTINYKNOT)) ? 5 : 3 - loop do - freestats = [] - GameData::Stat.each_main { |s| freestats.push(s.id) if !ivinherit.include?(s.id) } - break if freestats.length==0 - r = freestats[rand(freestats.length)] - parent = [mother,father][rand(2)] - ivs[r] = parent.iv[r] - ivinherit.push(r) - num += 1 - break if num>=limit - end - # Inheriting nature - new_natures = [] - new_natures.push(mother.nature) if mother.hasItem?(:EVERSTONE) - new_natures.push(father.nature) if father.hasItem?(:EVERSTONE) - if new_natures.length > 0 - new_nature = (new_natures.length == 1) ? new_natures[0] : new_natures[rand(new_natures.length)] - egg.nature = new_nature - end - # Masuda method and Shiny Charm - shinyretries = 0 - shinyretries += 5 if father.owner.language != mother.owner.language - shinyretries += 2 if $bag.has?(:SHINYCHARM) - if shinyretries>0 - shinyretries.times do - break if egg.shiny? - egg.shiny = nil # Make it recalculate shininess - egg.personalID = rand(2**16) | rand(2**16) << 16 - end - end - # Inheriting ability from the mother - if !ditto0 || !ditto1 - parent = (ditto0) ? father : mother # The non-Ditto - if parent.hasHiddenAbility? - egg.ability_index = parent.ability_index if rand(100) < 60 - elsif !ditto0 && !ditto1 - if rand(100) < 80 - egg.ability_index = mother.ability_index - else - egg.ability_index = (mother.ability_index + 1) % 2 - end - end - end - # Inheriting Poké Ball from the mother (or father if it's same species as mother) - if !ditto0 || !ditto1 - possible_balls = [] - if mother.species == father.species - possible_balls.push(mother.poke_ball) - possible_balls.push(father.poke_ball) - else - possible_balls.push(pkmn0.poke_ball) if pkmn0.female? || ditto1 - possible_balls.push(pkmn1.poke_ball) if pkmn1.female? || ditto0 - end - possible_balls.delete(:MASTERBALL) # Can't inherit this Ball - possible_balls.delete(:CHERISHBALL) # Can't inherit this Ball - if possible_balls.length > 0 - egg.poke_ball = possible_balls[0] - egg.poke_ball = possible_balls[rand(possible_balls.length)] if possible_balls.length > 1 - end - end - # Set all stats - egg.happiness = 120 - egg.iv = ivs - egg.moves = finalmoves - egg.calc_stats - egg.obtain_text = _INTL("Day-Care Couple") - egg.name = _INTL("Egg") - egg.steps_to_hatch = egg.species_data.hatch_steps - egg.givePokerus if rand(65536) < Settings::POKERUS_CHANCE - # Add egg to party - $player.party[$player.party.length] = egg + Deprecation.warn_method('pbDayCareGenerateEgg', 'v21', 'DayCare.collect_egg') + DayCare.collect_egg end - - - -#=============================================================================== -# Code that happens every step the player takes. -#=============================================================================== -Events.onStepTaken += proc { |_sender,_e| - # Make an egg available at the Day Care - deposited = pbDayCareDeposited - if deposited==2 && $PokemonGlobal.daycareEgg==0 - $PokemonGlobal.daycareEggSteps = 0 if !$PokemonGlobal.daycareEggSteps - $PokemonGlobal.daycareEggSteps += 1 - if $PokemonGlobal.daycareEggSteps==256 - $PokemonGlobal.daycareEggSteps = 0 - compatval = [0, 20, 50, 70][pbDayCareGetCompat] - compatval = [0, 40, 80, 88][pbDayCareGetCompat] if $bag.has?(:OVALCHARM) - $PokemonGlobal.daycareEgg = 1 if rand(100)=maxexp - oldlevel = pkmn.level - pkmn.exp += 1 # Gain Exp - next if pkmn.level==oldlevel - pkmn.calc_stats - movelist = pkmn.getMoveList - for i in movelist - pkmn.learn_move(i[1]) if i[0]==pkmn.level # Learned a new move - end - end -} diff --git a/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb b/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb index 00f16c34e..2ee3dbb76 100644 --- a/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb +++ b/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb @@ -430,11 +430,10 @@ Events.onStepTaken += proc { if ($PokemonGlobal.purifyChamber rescue nil) $PokemonGlobal.purifyChamber.update end - for i in 0...2 - pkmn = $PokemonGlobal.daycare[i][0] - next if !pkmn - stage = pkmn.heartStage - pkmn.adjustHeart(-1) - pkmn.update_shadow_moves if pkmn.heartStage != stage + $PokemonGlobal.day_care.slots.each do |slot| + next if !slot.filled? || !slot.pokemon.shadowPokemon? + old_stage = slot.pokemon.heartStage + slot.pokemon.adjustHeart(-1) + slot.pokemon.update_shadow_moves if slot.pokemon.heartStage != old_stage end } diff --git a/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb b/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb index e907f91c9..1656be3d5 100644 --- a/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb +++ b/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb @@ -201,165 +201,149 @@ end # Debug Day Care screen #=============================================================================== def pbDebugDayCare - commands = [_INTL("Withdraw Pokémon 1"), - _INTL("Withdraw Pokémon 2"), - _INTL("Deposit Pokémon"), - _INTL("Generate egg"), - _INTL("Collect egg")] - viewport = Viewport.new(0,0,Graphics.width,Graphics.height) - viewport.z = 99999 - sprites = {} - addBackgroundPlane(sprites,"background","hatchbg",viewport) - sprites["overlay"] = BitmapSprite.new(Graphics.width,Graphics.height,viewport) - pbSetSystemFont(sprites["overlay"].bitmap) - sprites["cmdwindow"] = Window_CommandPokemonEx.new(commands) - cmdwindow = sprites["cmdwindow"] - cmdwindow.x = 0 - cmdwindow.y = Graphics.height-128 - cmdwindow.width = Graphics.width - cmdwindow.height = 128 - cmdwindow.viewport = viewport - cmdwindow.columns = 2 - base = Color.new(248,248,248) - shadow = Color.new(104,104,104) - refresh = true + day_care = $PokemonGlobal.day_care + cmd_window = Window_CommandPokemonEx.newEmpty(0, 0, Graphics.width, Graphics.height) + commands = [] + cmd = 0 + compat = 0 + need_refresh = true loop do - if refresh - if pbEggGenerated? - commands[3] = _INTL("Discard egg") - else - commands[3] = _INTL("Generate egg") - end - cmdwindow.commands = commands - sprites["overlay"].bitmap.clear - textpos = [] - for i in 0...2 - textpos.push([_INTL("Pokémon {1}",i+1),Graphics.width/4+i*Graphics.width/2,2,2,base,shadow]) - end - for i in 0...2 - next if !$PokemonGlobal.daycare[i][0] - y = 34 - pkmn = $PokemonGlobal.daycare[i][0] - initlevel = $PokemonGlobal.daycare[i][1] - leveldiff = pkmn.level-initlevel - textpos.push(["#{pkmn.name} (#{pkmn.speciesName})",8+i*Graphics.width/2,y,0,base,shadow]) - y += 32 - if pkmn.male? - textpos.push([_INTL("Male ♂"),8+i*Graphics.width/2,y,0,Color.new(128,192,248),shadow]) - elsif pkmn.female? - textpos.push([_INTL("Female ♀"),8+i*Graphics.width/2,y,0,Color.new(248,96,96),shadow]) - else - textpos.push([_INTL("Genderless"),8+i*Graphics.width/2,y,0,base,shadow]) - end - y += 32 - if initlevel>=GameData::GrowthRate.max_level - textpos.push(["Lv. #{initlevel} (max)",8+i*Graphics.width/2,y,0,base,shadow]) - elsif leveldiff>0 - textpos.push(["Lv. #{initlevel} -> #{pkmn.level} (+#{leveldiff})", - 8+i*Graphics.width/2,y,0,base,shadow]) - else - textpos.push(["Lv. #{initlevel} (no change)",8+i*Graphics.width/2,y,0,base,shadow]) - end - y += 32 - if pkmn.level=0 - pbDayCareDeposit(pbGet(1)) - refresh = true + if need_refresh + commands.clear + day_care.slots.each_with_index do |slot, i| + if slot.filled? + pkmn = slot.pokemon + msg = _INTL("{1} ({2})", pkmn.name, pkmn.speciesName) + if pkmn.male? + msg += ", ♂" + elsif pkmn.female? + msg += ", ♀" end - end - when 3 # Generate/discard egg - if pbEggGenerated? - pbPlayDecisionSE - $PokemonGlobal.daycareEgg = 0 - $PokemonGlobal.daycareEggSteps = 0 - refresh = true - else - if pbDayCareDeposited!=2 || pbDayCareGetCompat==0 - pbPlayBuzzerSE + if slot.level_gain > 0 + msg += ", " + _INTL("Lv.{1} (+{2})", pkmn.level, slot.level_gain) else - pbPlayDecisionSE - $PokemonGlobal.daycareEgg = 1 - refresh = true + msg += ", " + _INTL("Lv.{1}", pkmn.level) end - end - when 4 # Collect egg - if $PokemonGlobal.daycareEgg!=1 - pbPlayBuzzerSE - elsif $player.party_full? - pbPlayBuzzerSE - pbMessage(_INTL("Party is full, can't collect the egg.")) + commands.push(_INTL("[Slot {1}] {2}", i, msg)) else - pbPlayDecisionSE - pbDayCareGenerateEgg - $PokemonGlobal.daycareEgg = 0 - $PokemonGlobal.daycareEggSteps = 0 - pbMessage(_INTL("Collected the {1} egg.", $player.last_party.speciesName)) - refresh = true + commands.push(_INTL("[Slot {1}] Empty", i)) end end + compat = $PokemonGlobal.day_care.get_compatibility + if day_care.egg_generated + commands.push(_INTL("[Egg available]")) + elsif compat > 0 + commands.push(_INTL("[Can produce egg]")) + else + commands.push(_INTL("[Cannot breed]")) + end + commands.push(_INTL("[Steps to next cycle: {1}]", 256 - day_care.step_counter)) + cmd_window.commands = commands + need_refresh = false + end + cmd = pbCommands2(cmd_window, commands, -1, cmd, true) + break if cmd < 0 + if cmd == commands.length - 2 # Egg + compat = $PokemonGlobal.day_care.get_compatibility + if compat == 0 + pbMessage(_INTL("Pokémon cannot breed.")) + else + msg = _INTL("Pokémon can breed (compatibility = {1}).", compat) + # Show compatibility + if day_care.egg_generated + case pbMessage("\\ts[]" + msg, [ + _INTL("Collect egg"), _INTL("Clear egg"), _INTL("Cancel")], 3) + when 0 # Collect egg + if $player.party_full? + pbMessage(_INTL("Party is full, can't collect the egg.")) + else + DayCare.collect_egg + pbMessage(_INTL("Collected the {1} egg.", $player.last_party.speciesName)) + need_refresh = true + end + when 1 # Clear egg + day_care.egg_generated = false + need_refresh = true + end + else + case pbMessage("\\ts[]" + msg, [_INTL("Make egg available"), _INTL("Cancel")], 2) + when 0 # Make egg available + day_care.egg_generated = true + need_refresh = true + end + end + end + elsif cmd == commands.length - 1 # Steps to next cycle + case pbMessage("\\ts[]" + _INTL("Change number of steps to next cycle?"), [ + _INTL("Set to 1"), _INTL("Set to 256"), _INTL("Set to other value"), _INTL("Cancel")], 4) + when 0 # Set to 1 + day_care.step_counter = 255 + need_refresh = true + when 1 # Set to 256 + day_care.step_counter = 0 + need_refresh = true + when 2 # Set to other value + params = ChooseNumberParams.new + params.setDefaultValue(day_care.step_counter) + params.setRange(1, 256) + new_counter = pbMessageChooseNumber(_INTL("Set steps until next cycle (1-256)."), params) + if new_counter != 256 - day_care.step_counter + day_care.step_counter = 256 - new_counter + need_refresh = true + end + end + else # Slot + slot = day_care[cmd] + if slot.filled? + pkmn = slot.pokemon + msg = _INTL("Cost: ${1}", slot.cost) + if pkmn.level < GameData::GrowthRate.max_level + end_exp = pkmn.growth_rate.minimum_exp_for_level(pkmn.level + 1) + msg += "\\n" + _INTL("Steps to next level: {1}", end_exp - pkmn.exp) + end + # Show level change and cost + case pbMessage("\\ts[]" + msg, [ + _INTL("Summary"), _INTL("Withdraw"), _INTL("Cancel")], 3) + when 0 # Summary + pbFadeOutIn { + scene = PokemonSummary_Scene.new + screen = PokemonSummaryScreen.new(scene, false) + screen.pbStartScreen([pkmn], 0) + need_refresh = true + } + when 1 # Withdraw + if $player.party_full? + pbMessage(_INTL("Party is full, can't withdraw Pokémon.")) + else + $player.party.push(pkmn) + slot.reset + day_care.reset_egg_counters + need_refresh = true + end + end + else + case pbMessage("\\ts[]" + _INTL("This slot is empty."), [ + _INTL("Deposit"), _INTL("Cancel")], 2) + when 0 # Deposit + if $player.party.empty? + pbMessage(_INTL("Party is empty, can't deposit Pokémon.")) + else + pbChooseNonEggPokemon(1, 3) + party_index = pbGet(1) + if party_index >= 0 + pkmn = $player.party[party_index] + slot.deposit(pkmn) + $player.party.delete_at(party_index) + day_care.reset_egg_counters + need_refresh = true + end + end + end + end + end end - pbDisposeSpriteHash(sprites) - viewport.dispose + cmd_window.dispose end diff --git a/Data/Scripts/021_Compiler/004_Compiler_MapsAndEvents.rb b/Data/Scripts/021_Compiler/004_Compiler_MapsAndEvents.rb index fa89327b8..4c31f121a 100644 --- a/Data/Scripts/021_Compiler/004_Compiler_MapsAndEvents.rb +++ b/Data/Scripts/021_Compiler/004_Compiler_MapsAndEvents.rb @@ -20,7 +20,16 @@ module Compiler ['$Trainer', '$player'], ['$SaveVersion', '$save_engine_version'], ['$game_version', '$save_game_version'], - ['$MapFactory', '$map_factory'] + ['$MapFactory', '$map_factory'], + ['pbDayCareDeposited', 'DayCare.count'], + ['pbDayCareGetDeposited', 'DayCare.get_details'], + ['pbDayCareGetLevelGain', 'DayCare.get_level_gain'], + ['pbDayCareDeposit', 'DayCare.deposit'], + ['pbDayCareWithdraw', 'DayCare.withdraw'], + ['pbDayCareChoose', 'DayCare.choose'], + ['pbDayCareGetCompatibility', 'DayCare.get_compatibility'], + ['pbEggGenerated?', 'DayCare.egg_generated?'], + ['pbDayCareGenerateEgg', 'DayCare.collect_egg'] ] module_function