#=============================================================================== # #=============================================================================== module GameData class Species attr_reader :id attr_reader :species attr_reader :form attr_reader :real_name attr_reader :real_form_name attr_reader :real_category attr_reader :real_pokedex_entry attr_reader :pokedex_form attr_reader :types attr_reader :base_stats attr_reader :evs attr_reader :base_exp attr_reader :growth_rate attr_reader :gender_ratio attr_reader :catch_rate attr_reader :happiness attr_reader :moves attr_reader :tutor_moves attr_reader :egg_moves attr_reader :abilities attr_reader :hidden_abilities attr_reader :wild_item_common attr_reader :wild_item_uncommon attr_reader :wild_item_rare attr_reader :egg_groups attr_reader :hatch_steps attr_reader :incense attr_reader :offspring attr_reader :evolutions attr_reader :height attr_reader :weight attr_reader :color attr_reader :shape attr_reader :habitat attr_reader :generation attr_reader :flags attr_reader :mega_stone attr_reader :mega_move attr_reader :unmega_form attr_reader :mega_message attr_reader :pbs_file_suffix DATA = {} DATA_FILENAME = "species.dat" PBS_BASE_FILENAME = ["pokemon", "pokemon_forms"] extend ClassMethodsSymbols include InstanceMethods def self.schema(compiling_forms = false) ret = {} if compiling_forms ret["SectionName"] = [:id, "ev", :Species] else ret["SectionName"] = [:id, "m"] ret["Name"] = [:real_name, "s"] end ret["FormName"] = [:real_form_name, "q"] if compiling_forms ret["PokedexForm"] = [:pokedex_form, "u"] ret["MegaStone"] = [:mega_stone, "e", :Item] ret["MegaMove"] = [:mega_move, "e", :Move] ret["UnmegaForm"] = [:unmega_form, "u"] ret["MegaMessage"] = [:mega_message, "u"] end ret["Types"] = [:types, "*e", :Type] ret["BaseStats"] = [:base_stats, "vvvvvv"] if !compiling_forms ret["GenderRatio"] = [:gender_ratio, "e", :GenderRatio] ret["GrowthRate"] = [:growth_rate, "e", :GrowthRate] end ret["BaseExp"] = [:base_exp, "v"] ret["EVs"] = [:evs, "*ev", :Stat] ret["CatchRate"] = [:catch_rate, "u"] ret["Happiness"] = [:happiness, "u"] ret["Abilities"] = [:abilities, "*e", :Ability] ret["HiddenAbilities"] = [:hidden_abilities, "*e", :Ability] ret["Moves"] = [:moves, "*ue", nil, :Move] ret["TutorMoves"] = [:tutor_moves, "*e", :Move] ret["EggMoves"] = [:egg_moves, "*e", :Move] ret["EggGroups"] = [:egg_groups, "*e", :EggGroup] ret["HatchSteps"] = [:hatch_steps, "v"] if compiling_forms ret["Offspring"] = [:offspring, "*e", :Species] else ret["Incense"] = [:incense, "e", :Item] ret["Offspring"] = [:offspring, "*s"] end ret["Height"] = [:height, "f"] ret["Weight"] = [:weight, "f"] ret["Color"] = [:color, "e", :BodyColor] ret["Shape"] = [:shape, "e", :BodyShape] ret["Habitat"] = [:habitat, "e", :Habitat] ret["Category"] = [:real_category, "s"] ret["Pokedex"] = [:real_pokedex_entry, "q"] ret["Generation"] = [:generation, "i"] ret["Flags"] = [:flags, "*s"] ret["WildItemCommon"] = [:wild_item_common, "*e", :Item] ret["WildItemUncommon"] = [:wild_item_uncommon, "*e", :Item] ret["WildItemRare"] = [:wild_item_rare, "*e", :Item] if compiling_forms ret["Evolutions"] = [:evolutions, "*ees", :Species, :Evolution] ret["Evolution"] = [:evolutions, "^eeS", :Species, :Evolution] else ret["Evolutions"] = [:evolutions, "*ses", nil, :Evolution] ret["Evolution"] = [:evolutions, "^seS", nil, :Evolution] end return ret end def self.editor_properties return [ ["ID", ReadOnlyProperty, _INTL("The ID of the Pokémon.")], ["Name", LimitStringProperty.new(Pokemon::MAX_NAME_SIZE), _INTL("Name of the Pokémon.")], ["FormName", StringProperty, _INTL("Name of this form of the Pokémon.")], ["Types", GameDataPoolProperty.new(:Type, false), _INTL("The Pokémon's type(s).")], ["BaseStats", BaseStatsProperty, _INTL("Base stats of the Pokémon.")], ["GenderRatio", GameDataProperty.new(:GenderRatio), _INTL("Proportion of males to females for this species.")], ["GrowthRate", GameDataProperty.new(:GrowthRate), _INTL("Pokémon's growth rate.")], ["BaseExp", LimitProperty.new(9999), _INTL("Base experience earned when this species is defeated.")], ["EVs", EffortValuesProperty, _INTL("Effort Value points earned when this species is defeated.")], ["CatchRate", LimitProperty.new(255), _INTL("Catch rate of this species (0-255).")], ["Happiness", LimitProperty.new(255), _INTL("Base happiness of this species (0-255).")], ["Abilities", AbilitiesProperty.new, _INTL("Abilities which the Pokémon can have (max. 2).")], ["HiddenAbilities", AbilitiesProperty.new, _INTL("Secret abilities which the Pokémon can have.")], ["Moves", LevelUpMovesProperty, _INTL("Moves which the Pokémon learns while levelling up.")], ["TutorMoves", EggMovesProperty.new, _INTL("Moves which the Pokémon can be taught by TM/HM/Move Tutor.")], ["EggMoves", EggMovesProperty.new, _INTL("Moves which the Pokémon can learn via breeding.")], ["EggGroups", EggGroupsProperty.new, _INTL("Egg groups that the Pokémon belongs to for breeding purposes.")], ["HatchSteps", LimitProperty.new(99_999), _INTL("Number of steps until an egg of this species hatches.")], ["Incense", ItemProperty, _INTL("Item needed to be held by a parent to produce an egg of this species.")], ["Offspring", GameDataPoolProperty.new(:Species), _INTL("All possible species that an egg can be when breeding for an egg of this species (if blank, the egg can only be this species).")], ["Height", NonzeroLimitProperty.new(999), _INTL("Height of the Pokémon in 0.1 metres (e.g. 42 = 4.2m).")], ["Weight", NonzeroLimitProperty.new(9999), _INTL("Weight of the Pokémon in 0.1 kilograms (e.g. 42 = 4.2kg).")], ["Color", GameDataProperty.new(:BodyColor), _INTL("Pokémon's body color.")], ["Shape", GameDataProperty.new(:BodyShape), _INTL("Body shape of this species.")], ["Habitat", GameDataProperty.new(:Habitat), _INTL("The habitat of this species.")], ["Category", StringProperty, _INTL("Kind of Pokémon species.")], ["Pokedex", StringProperty, _INTL("Description of the Pokémon as displayed in the Pokédex.")], ["Generation", LimitProperty.new(99_999), _INTL("The number of the generation the Pokémon debuted in.")], ["Flags", StringListProperty, _INTL("Words/phrases that distinguish this species from others.")], ["WildItemCommon", GameDataPoolProperty.new(:Item), _INTL("Item(s) commonly held by wild Pokémon of this species.")], ["WildItemUncommon", GameDataPoolProperty.new(:Item), _INTL("Item(s) uncommonly held by wild Pokémon of this species.")], ["WildItemRare", GameDataPoolProperty.new(:Item), _INTL("Item(s) rarely held by wild Pokémon of this species.")], ["Evolutions", EvolutionsProperty.new, _INTL("Evolution paths of this species.")] ] end # @param species [Symbol, self, String] # @param form [Integer] # @return [self, nil] def self.get_species_form(species, form) return nil if !species || !form validate species => [Symbol, self, String] validate form => Integer species = species.species if species.is_a?(self) species = species.to_sym if species.is_a?(String) trial = sprintf("%s_%d", species, form).to_sym species_form = (DATA[trial].nil?) ? species : trial return (DATA.has_key?(species_form)) ? DATA[species_form] : nil end def self.each_species DATA.each_value { |species| yield species if species.form == 0 } end def self.each_form_for_species(species) DATA.each_value { |species| yield species if species.species == species } end def self.species_count ret = 0 self.each_species { |species| ret += 1 } return ret end #--------------------------------------------------------------------------- def initialize(hash) @id = hash[:id] @species = hash[:species] || @id @form = hash[:form] || 0 @real_name = hash[:real_name] || "Unnamed" @real_form_name = hash[:real_form_name] @real_category = hash[:real_category] || "???" @real_pokedex_entry = hash[:real_pokedex_entry] || "???" @pokedex_form = hash[:pokedex_form] || @form @types = hash[:types] || [:NORMAL] @base_stats = hash[:base_stats] || {} @evs = hash[:evs] || {} GameData::Stat.each_main do |s| @base_stats[s.id] = 1 if !@base_stats[s.id] || @base_stats[s.id] <= 0 @evs[s.id] = 0 if !@evs[s.id] || @evs[s.id] < 0 end @base_exp = hash[:base_exp] || 100 @growth_rate = hash[:growth_rate] || :Medium @gender_ratio = hash[:gender_ratio] || :Female50Percent @catch_rate = hash[:catch_rate] || 255 @happiness = hash[:happiness] || 70 @moves = hash[:moves] || [] @tutor_moves = hash[:tutor_moves] || [] @egg_moves = hash[:egg_moves] || [] @abilities = hash[:abilities] || [] @hidden_abilities = hash[:hidden_abilities] || [] @wild_item_common = hash[:wild_item_common] || [] @wild_item_uncommon = hash[:wild_item_uncommon] || [] @wild_item_rare = hash[:wild_item_rare] || [] @egg_groups = hash[:egg_groups] || [:Undiscovered] @hatch_steps = hash[:hatch_steps] || 1 @incense = hash[:incense] @offspring = hash[:offspring] || [] @evolutions = hash[:evolutions] || [] @height = hash[:height] || 1 @weight = hash[:weight] || 1 @color = hash[:color] || :Red @shape = hash[:shape] || :Head @habitat = hash[:habitat] || :None @generation = hash[:generation] || 0 @flags = hash[:flags] || [] @mega_stone = hash[:mega_stone] @mega_move = hash[:mega_move] @unmega_form = hash[:unmega_form] || 0 @mega_message = hash[:mega_message] || 0 @pbs_file_suffix = hash[:pbs_file_suffix] || "" end # @return [String] the translated name of this species def name return pbGetMessageFromHash(MessageTypes::SPECIES_NAMES, @real_name) end # @return [String] the translated name of this form of this species def form_name return pbGetMessageFromHash(MessageTypes::SPECIES_FORM_NAMES, @real_form_name) end # @return [String] the translated Pokédex category of this species def category return pbGetMessageFromHash(MessageTypes::SPECIES_CATEGORIES, @real_category) end # @return [String] the translated Pokédex entry of this species def pokedex_entry return pbGetMessageFromHash(MessageTypes::POKEDEX_ENTRIES, @real_pokedex_entry) end def default_form @flags.each do |flag| return $~[1].to_i if flag[/^DefaultForm_(\d+)$/i] end return -1 end def base_form default = default_form return (default >= 0) ? default : @form end def single_gendered? return GameData::GenderRatio.get(@gender_ratio).single_gendered? end def base_stat_total return @base_stats.values.sum end def has_flag?(flag) return @flags.any? { |f| f.downcase == flag.downcase } end def apply_metrics_to_sprite(sprite, index, shadow = false) metrics_data = GameData::SpeciesMetrics.get_species_form(@species, @form) metrics_data.apply_metrics_to_sprite(sprite, index, shadow) end def shows_shadow? metrics_data = GameData::SpeciesMetrics.get_species_form(@species, @form) return metrics_data.shows_shadow? end def get_evolutions(exclude_invalid = false) ret = [] @evolutions.each do |evo| next if evo[3] # Is the prevolution next if evo[1] == :None && exclude_invalid ret.push([evo[0], evo[1], evo[2]]) # [Species, method, parameter] end return ret end def get_family_evolutions(exclude_invalid = true) evos = get_evolutions(exclude_invalid) evos = evos.sort { |a, b| GameData::Species.keys.index(a[0]) <=> GameData::Species.keys.index(b[0]) } ret = [] evos.each do |evo| ret.push([@species].concat(evo)) # [Prevo species, evo species, method, parameter] evo_array = GameData::Species.get(evo[0]).get_family_evolutions(exclude_invalid) ret.concat(evo_array) if evo_array && evo_array.length > 0 end return ret end def get_previous_species return @species if @evolutions.length == 0 @evolutions.each { |evo| return evo[0] if evo[3] } # Is the prevolution return @species end def get_baby_species(check_items = false, item1 = nil, item2 = nil) ret = @species return ret if @evolutions.length == 0 @evolutions.each do |evo| next if !evo[3] # Check only the prevolution if check_items incense = GameData::Species.get(evo[0]).incense ret = evo[0] if !incense || item1 == incense || item2 == incense else ret = evo[0] # Species of prevolution end break end ret = GameData::Species.get(ret).get_baby_species(check_items, item1, item2) if ret != @species return ret end # Returns an array of all the species in this species' evolution family. def get_family_species sp = get_baby_species evos = GameData::Species.get(sp).get_family_evolutions(false) return [sp] if evos.length == 0 return [sp].concat(evos.map { |e| e[1] }).uniq end # This takes into account whether other_species is evolved. def breeding_can_produce?(other_species) other_family = GameData::Species.get(other_species).get_family_species if @offspring.length > 0 return (other_family & @offspring).length > 0 end return other_family.include?(@species) end # If this species doesn't have egg moves, looks at prevolutions one at a # time and returns theirs instead. def get_egg_moves return @egg_moves if !@egg_moves.empty? prevo = get_previous_species return GameData::Species.get_species_form(prevo, @form).get_egg_moves if prevo != @species return @egg_moves end def family_evolutions_have_method?(check_method, check_param = nil) sp = get_baby_species evos = GameData::Species.get(sp).get_family_evolutions return false if evos.length == 0 evos.each do |evo| if check_method.is_a?(Array) next if !check_method.include?(evo[2]) elsif evo[2] != check_method next end return true if check_param.nil? || evo[3] == check_param end return false end # Used by the Moon Ball when checking if a Pokémon's evolution family # includes an evolution that uses the Moon Stone. def family_item_evolutions_use_item?(check_item = nil) sp = get_baby_species evos = GameData::Species.get(sp).get_family_evolutions return false if !evos || evos.length == 0 evos.each do |evo| next if GameData::Evolution.get(evo[2]).use_item_proc.nil? return true if check_item.nil? || evo[3] == check_item end return false end def minimum_level return 1 if @evolutions.length == 0 @evolutions.each do |evo| next if !evo[3] # Check only the prevolution prevo_data = GameData::Species.get_species_form(evo[0], base_form) return 1 if !prevo_data.incense.nil? prevo_min_level = prevo_data.minimum_level evo_method_data = GameData::Evolution.get(evo[1]) return prevo_min_level if evo_method_data.level_up_proc.nil? && evo_method_data.battle_level_up_proc.nil? && evo_method_data.id != :Shedinja any_level_up = evo_method_data.any_level_up return (any_level_up) ? prevo_min_level + 1 : evo[2] end return 1 end alias __orig__get_property_for_PBS get_property_for_PBS unless method_defined?(:__orig__get_property_for_PBS) def get_property_for_PBS(key, writing_form = false) key = "SectionName" if key == "ID" ret = nil if self.class.schema(writing_form).include?(key) ret = self.send(self.class.schema(writing_form)[key][0]) ret = nil if ret == false || (ret.is_a?(Array) && ret.length == 0) end case key when "SectionName" ret = [@species, @form] if writing_form when "FormName" ret = nil if nil_or_empty?(ret) when "PokedexForm" ret = nil if ret == @form when "UnmegaForm", "MegaMessage", "Generation" ret = nil if ret == 0 when "BaseStats" new_ret = [] GameData::Stat.each_main do |s| new_ret[s.pbs_order] = ret[s.id] if s.pbs_order >= 0 end ret = new_ret when "EVs" new_ret = [] GameData::Stat.each_main do |s| new_ret.push([s.id, ret[s.id]]) if ret[s.id] > 0 && s.pbs_order >= 0 end ret = new_ret when "Height", "Weight" ret = ret.to_f / 10 when "Habitat" ret = nil if ret == :None when "Evolutions" ret = nil # Want to use "Evolution" instead when "Evolution" if ret ret = ret.reject { |evo| evo[3] } # Remove prevolutions ret.each do |evo| param_type = GameData::Evolution.get(evo[1]).parameter if !param_type.nil? if param_type.is_a?(Symbol) && !GameData.const_defined?(param_type) evo[2] = getConstantName(param_type, evo[2]) else evo[2] = evo[2].to_s end end end ret.each_with_index { |evo, i| ret[i] = evo[0, 3] } ret = nil if ret.length == 0 end end if writing_form && !ret.nil? base_form = GameData::Species.get(@species) if !["WildItemCommon", "WildItemUncommon", "WildItemRare"].include?(key) || (base_form.wild_item_common == @wild_item_common && base_form.wild_item_uncommon == @wild_item_uncommon && base_form.wild_item_rare == @wild_item_rare) ret = nil if base_form.get_property_for_PBS(key) == ret end end return ret end end end