Files
infinitefusion-e18/Data/Scripts/012_Overworld/007_Overworld_DayCare.rb

622 lines
22 KiB
Ruby

#===============================================================================
# NOTE: In Gen 7+, the Day Care is replaced by the Pokémon Nursery, which works
# in much the same way except deposited Pokémon no longer gain Exp because
# of the player walking around and, in Gen 8+, deposited Pokémon are able
# to learn egg moves from each other if they are the same species. In
# Essentials, this code can be used for both facilities, and these
# mechanics differences are set by some Settings.
# NOTE: The Day Care has a different price than the Pokémon Nursery. For the Day
# Care, you are charged when you withdraw a deposited Pokémon and you pay
# an amount based on how many levels it gained. For the Nursery, you pay
# $500 up-front when you deposit a Pokémon. This difference will appear in
# the Day Care Lady's event, not in these scripts.
#===============================================================================
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
# Set regional form
new_form = MultipleForms.call("getFormOnEggCreation", egg)
egg.form = new_form if new_form
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) &&
((father[2] && father[0].hasItem?(:LIGHTBALL)) ||
(mother[2] && mother[0].hasItem?(:LIGHTBALL)))
moves.push(:VOLTTACKLE)
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
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
if Settings::MECHANICS_GENERATION >= 6
inherit_count = 5 if mother.hasItem?(:DESTINYKNOT) || father.hasItem?(:DESTINYKNOT)
end
# 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(65_536) < 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 _INTL("{1} (♂, Lv.{2})", @pokemon.name, @pokemon.level)
elsif @pokemon.female?
return _INTL("{1} (♀, Lv.{2})", @pokemon.name, @pokemon.level)
end
return _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
attr_accessor :share_egg_moves # For deposited Pokémon of the same species
MAX_SLOTS = 2
def initialize
@slots = []
MAX_SLOTS.times { @slots.push(DayCareSlot.new) }
reset_egg_counters
@gain_exp = Settings::DAY_CARE_POKEMON_GAIN_EXP_FROM_WALKING
@share_egg_moves = Settings::DAY_CARE_POKEMON_CAN_SHARE_EGG_MOVES
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 share_egg_move
return if count != 2
pkmn1, pkmn2 = pokemon_pair
return if pkmn1.species != pkmn2.species
egg_moves1 = pkmn1.species_data.get_egg_moves
egg_moves2 = pkmn2.species_data.get_egg_moves
known_moves1 = []
known_moves2 = []
if pkmn2.numMoves < Pokemon::MAX_MOVES
pkmn1.moves.each { |m| known_moves1.push(m.id) if egg_moves2.include?(m.id) && !pkmn2.hasMove?(m.id) }
end
if pkmn1.numMoves < Pokemon::MAX_MOVES
pkmn2.moves.each { |m| known_moves2.push(m.id) if egg_moves1.include?(m.id) && !pkmn1.hasMove?(m.id) }
end
if !known_moves1.empty?
if known_moves2.empty?
pkmn2.learn_move(known_moves1[0])
else
learner = [[pkmn1, known_moves2[0]], [pkmn2, known_moves1[0]]].sample
learner[0].learn_move(learner[1])
end
elsif !known_moves2.empty?
pkmn1.learn_move(known_moves2[0])
end
end
def update_on_step_taken
@step_counter += 1
if @step_counter >= 256
@step_counter = 0
# Make an egg available at the Day Care
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
# Have one deposited Pokémon learn an egg move from the other
# NOTE: I don't know what the chance of this happening is.
share_egg_move if @share_egg_moves && rand(100) < 50
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)
$stats.day_care_deposits += 1
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
$stats.day_care_levels_gained += slot.level_gain
$player.party.push(slot.pokemon)
slot.reset
day_care.reset_egg_counters
end
def self.choose(message, 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|
choice_text = slot.choice_text
next if !choice_text
commands.push(choice_text)
indices.push(i)
end
commands.push(_INTL("CANCEL"))
command = pbMessage(message, 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
#===============================================================================
# With each step taken, add Exp to Pokémon in the Day Care and try to generate
# an egg.
#===============================================================================
EventHandlers.add(:on_player_step_taken, :update_day_care,
proc {
$PokemonGlobal.day_care.update_on_step_taken
}
)
#===============================================================================
# Deprecated methods
#===============================================================================
# @deprecated This method is slated to be removed in v21.
def pbDayCareDeposited
Deprecation.warn_method("pbDayCareDeposited", "v21", "DayCare.count")
return DayCare.count
end
# @deprecated This method is slated to be removed in v21.
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
# @deprecated This method is slated to be removed in v21.
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
# @deprecated This method is slated to be removed in v21.
def pbDayCareDeposit(party_index)
Deprecation.warn_method("pbDayCareDeposit", "v21", "DayCare.deposit(party_index)")
DayCare.deposit(party_index)
end
# @deprecated This method is slated to be removed in v21.
def pbDayCareWithdraw(index)
Deprecation.warn_method("pbDayCareWithdraw", "v21", "DayCare.withdraw(index)")
DayCare.withdraw(index)
end
# @deprecated This method is slated to be removed in v21.
def pbDayCareChoose(text, choice_var)
Deprecation.warn_method("pbDayCareChoose", "v21", "DayCare.choose(text, choice_var)")
DayCare.choose(text, choice_var)
end
# @deprecated This method is slated to be removed in v21.
def pbDayCareGetCompatibility(compat_var)
Deprecation.warn_method("pbDayCareGetCompatibility", "v21", "DayCare.get_compatibility(compat_var)")
DayCare.get_compatibility(compat_var)
end
# @deprecated This method is slated to be removed in v21.
def pbEggGenerated?
Deprecation.warn_method("pbEggGenerated?", "v21", "DayCare.egg_generated?")
return DayCare.egg_generated?
end
# @deprecated This method is slated to be removed in v21.
def pbDayCareGenerateEgg
Deprecation.warn_method("pbDayCareGenerateEgg", "v21", "DayCare.collect_egg")
DayCare.collect_egg
end