#=============================================================================== # #=============================================================================== module Compiler @@categories[:pbs_files] = { :should_compile => proc { |compiling| next should_compile_pbs_files? }, :header_text => proc { next _INTL("Compiling PBS files") }, :skipped_text => proc { next _INTL("Not compiled") }, :compile => proc { # Delete old data files in preparation for recompiling get_all_pbs_data_filenames_to_compile.each do |filename| begin File.delete("Data/#{filename[0]}") if FileTest.exist?("Data/#{filename[0]}") rescue SystemCallError end end compile_pbs_files } } module_function def get_all_pbs_data_filenames_to_compile ret = GameData.get_all_data_filenames ret += [ # Extra .dat files for data that isn't a GameData class ["map_connections.dat", true], ["regional_dexes.dat", true], ["trainer_lists.dat", true] ] return ret end def get_all_pbs_files_to_compile # Get the GameData classes and their respective base PBS filenames ret = GameData.get_all_pbs_base_filenames ret.merge!({ :BattleFacility => "battle_facility_lists", :Connection => "map_connections", :RegionalDex => "regional_dexes" }) ret.each { |key, val| ret[key] = [val] } # [base_filename, ["PBS/file.txt", etc.]] # Look through all PBS files and match them to a GameData class based on # their base filenames text_files_keys = ret.keys.sort! { |a, b| ret[b][0].length <=> ret[a][0].length } Dir.chdir("PBS/") do Dir.glob("*.txt") do |f| base_name = File.basename(f, ".txt") text_files_keys.each do |key| next if base_name != ret[key][0] && !f.start_with?(ret[key][0] + "_") ret[key][1] ||= [] ret[key][1].push("PBS/" + f) break end end end return ret end def should_compile_pbs_files? # If no PBS folder exists, create one and fill it, then recompile if !FileTest.directory?("PBS") Dir.mkdir("PBS") rescue nil GameData.load_all write_all_pbs_files return true end # Get all data files and PBS files to be checked for their last modified times data_files = get_all_pbs_data_filenames_to_compile text_files = get_all_pbs_files_to_compile # Check data files for their latest modify time latest_data_write_time = 0 data_files.each do |filename| # filename = [string, boolean (whether mandatory)] if FileTest.exist?("Data/" + filename[0]) begin File.open("Data/#{filename[0]}") do |file| latest_data_write_time = [latest_data_write_time, file.mtime.to_i].max end rescue SystemCallError return true end elsif filename[1] return true end end # Check PBS files for their latest modify time latest_text_edit_time = 0 text_files.each_value do |value| next if !value || !value[1].is_a?(Array) value[1].each do |filepath| begin File.open(filepath) { |file| latest_text_edit_time = [latest_text_edit_time, file.mtime.to_i].max } rescue SystemCallError end end end # Decide to compile if a PBS file was edited more recently than any .dat files return (latest_text_edit_time >= latest_data_write_time) end def compile_pbs_files text_files = get_all_pbs_files_to_compile modify_pbs_file_contents_before_compiling compile_town_map(*text_files[:TownMap][1]) compile_connections(*text_files[:Connection][1]) compile_types(*text_files[:Type][1]) compile_abilities(*text_files[:Ability][1]) compile_moves(*text_files[:Move][1]) # Depends on Type compile_items(*text_files[:Item][1]) # Depends on Move compile_berry_plants(*text_files[:BerryPlant][1]) # Depends on Item compile_pokemon(*text_files[:Species][1]) # Depends on Move, Item, Type, Ability compile_pokemon_forms(*text_files[:Species1][1]) # Depends on Species, Move, Item, Type, Ability compile_pokemon_metrics(*text_files[:SpeciesMetrics][1]) # Depends on Species compile_shadow_pokemon(*text_files[:ShadowPokemon][1]) # Depends on Species compile_regional_dexes(*text_files[:RegionalDex][1]) # Depends on Species compile_ribbons(*text_files[:Ribbon][1]) compile_encounters(*text_files[:Encounter][1]) # Depends on Species compile_trainer_types(*text_files[:TrainerType][1]) compile_trainers(*text_files[:Trainer][1]) # Depends on Species, Item, Move compile_trainer_lists # Depends on TrainerType compile_metadata(*text_files[:Metadata][1]) # Depends on TrainerType compile_map_metadata(*text_files[:MapMetadata][1]) compile_dungeon_tilesets(*text_files[:DungeonTileset][1]) compile_dungeon_parameters(*text_files[:DungeonParameters][1]) compile_phone(*text_files[:PhoneMessage][1]) # Depends on TrainerType end #----------------------------------------------------------------------------- # Generic methods used when compiling PBS files. #----------------------------------------------------------------------------- def compile_pbs_file_message_start(filename) # The `` around the file's name turns it cyan Console.echo_li(_INTL("Compiling PBS file `{1}`...", filename.split("/").last)) end def process_pbs_file_message_end Console.echo_done(true) Graphics.update end def compile_PBS_file_generic(game_data, *paths) if game_data.const_defined?(:OPTIONAL) && game_data::OPTIONAL return if paths.none? { |p| FileTest.exist?(p) } end game_data::DATA.clear schema = game_data.schema # Read from PBS file(s) paths.each do |path| compile_pbs_file_message_start(path) base_filename = game_data::PBS_BASE_FILENAME base_filename = base_filename[0] if base_filename.is_a?(Array) # For Species file_suffix = File.basename(path, ".txt")[base_filename.length + 1, path.length] || "" File.open(path, "rb") do |f| FileLineData.file = path # For error reporting # Read a whole section's lines at once, then run through this code. # contents is a hash containing all the XXX=YYY lines in that section, where # the keys are the XXX and the values are the YYY (as unprocessed strings). idx = 0 pbEachFileSection(f, schema) do |contents, section_name| echo "." if idx % 100 == 0 Graphics.update if idx % 500 == 0 idx += 1 data_hash = { :id => section_name.to_sym, :pbs_file_suffix => file_suffix } # Go through schema hash of compilable data and compile this section schema.each_key do |key| FileLineData.setSection(section_name, key, contents[key]) # For error reporting if key == "SectionName" data_hash[schema[key][0]] = get_csv_record(section_name, schema[key]) next end # Skip empty properties next if contents[key].nil? # Compile value for key if schema[key][1][0] == "^" contents[key].each do |val| value = get_csv_record(val, schema[key]) value = nil if value.is_a?(Array) && value.empty? data_hash[schema[key][0]] ||= [] data_hash[schema[key][0]].push(value) end data_hash[schema[key][0]].compact! else value = get_csv_record(contents[key], schema[key]) value = nil if value.is_a?(Array) && value.empty? data_hash[schema[key][0]] = value end end # Validate and modify the compiled data yield false, data_hash if block_given? if game_data.exists?(data_hash[:id]) raise _INTL("Section name '{1}' is used twice.", data_hash[:id]) + "\n" + FileLineData.linereport end # Add section's data to records game_data.register(data_hash) end end process_pbs_file_message_end end yield true, nil if block_given? # Save all data game_data.save end #----------------------------------------------------------------------------- # Compile Town Map data. #----------------------------------------------------------------------------- def compile_town_map(*paths) compile_PBS_file_generic(GameData::TownMap, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_town_maps : validate_compiled_town_map(hash) end end def validate_compiled_town_map(hash) end def validate_all_compiled_town_maps # Get town map names and descriptions for translating region_names = [] point_names = [] interest_names = [] GameData::TownMap.each do |town_map| region_names[town_map.id] = town_map.real_name town_map.point.each do |point| point_names.push(point[2]) interest_names.push(point[3]) end end point_names.uniq! interest_names.uniq! MessageTypes.setMessagesAsHash(MessageTypes::REGION_NAMES, region_names) MessageTypes.setMessagesAsHash(MessageTypes::REGION_LOCATION_NAMES, point_names) MessageTypes.setMessagesAsHash(MessageTypes::REGION_LOCATION_DESCRIPTIONS, interest_names) end #----------------------------------------------------------------------------- # Compile map connections. #----------------------------------------------------------------------------- def compile_connections(*paths) hashenum = { "N" => "N", "North" => "N", "E" => "E", "East" => "E", "S" => "S", "South" => "S", "W" => "W", "West" => "W" } schema = [nil, "iyiiyi", nil, hashenum, nil, nil, hashenum] records = [] paths.each do |path| compile_pbs_file_message_start(path) pbCompilerEachPreppedLine(path) do |line, lineno| FileLineData.setLine(line, lineno) record = get_csv_record(line, schema) if !pbRgssExists?(sprintf("Data/Map%03d.rxdata", record[0])) print _INTL("Warning: Map {1}, as mentioned in the map connection data, was not found.", record[0]) + "\n" + FileLineData.linereport elsif !pbRgssExists?(sprintf("Data/Map%03d.rxdata", record[3])) print _INTL("Warning: Map {1}, as mentioned in the map connection data, was not found.", record[3]) + "\n" + FileLineData.linereport end case record[1] when "N" raise _INTL("North side of first map must connect with south side of second map.") + "\n" + FileLineData.linereport if record[4] != "S" when "S" raise _INTL("South side of first map must connect with north side of second map.") + "\n" + FileLineData.linereport if record[4] != "N" when "E" raise _INTL("East side of first map must connect with west side of second map.") + "\n" + FileLineData.linereport if record[4] != "W" when "W" raise _INTL("West side of first map must connect with east side of second map.") + "\n" + FileLineData.linereport if record[4] != "E" end records.push(record) end process_pbs_file_message_end end save_data(records, "Data/map_connections.dat") end #----------------------------------------------------------------------------- # Compile type data. #----------------------------------------------------------------------------- def compile_types(*paths) compile_PBS_file_generic(GameData::Type, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_types : validate_compiled_type(hash) end end def validate_compiled_type(hash) # Remove duplicate weaknesses/resistances/immunities hash[:weaknesses].uniq! if hash[:weaknesses].is_a?(Array) hash[:resistances].uniq! if hash[:resistances].is_a?(Array) hash[:immunities].uniq! if hash[:immunities].is_a?(Array) end def validate_all_compiled_types type_names = [] GameData::Type.each do |type| # Ensure all weaknesses/resistances/immunities are valid types type.weaknesses.each do |other_type| next if GameData::Type.exists?(other_type) raise _INTL("'{1}' is not a defined type (type {2}, Weaknesses).", other_type.to_s, type.id) end type.resistances.each do |other_type| next if GameData::Type.exists?(other_type) raise _INTL("'{1}' is not a defined type (type {2}, Resistances).", other_type.to_s, type.id) end type.immunities.each do |other_type| next if GameData::Type.exists?(other_type) raise _INTL("'{1}' is not a defined type (type {2}, Immunities).", other_type.to_s, type.id) end # Get type names for translating type_names.push(type.real_name) end MessageTypes.setMessagesAsHash(MessageTypes::TYPE_NAMES, type_names) end #----------------------------------------------------------------------------- # Compile ability data. #----------------------------------------------------------------------------- def compile_abilities(*paths) compile_PBS_file_generic(GameData::Ability, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_abilities : validate_compiled_ability(hash) end end def validate_compiled_ability(hash) end def validate_all_compiled_abilities # Get abilty names/descriptions for translating ability_names = [] ability_descriptions = [] GameData::Ability.each do |ability| ability_names.push(ability.real_name) ability_descriptions.push(ability.real_description) end MessageTypes.setMessagesAsHash(MessageTypes::ABILITY_NAMES, ability_names) MessageTypes.setMessagesAsHash(MessageTypes::ABILITY_DESCRIPTIONS, ability_descriptions) end #----------------------------------------------------------------------------- # Compile move data. #----------------------------------------------------------------------------- def compile_moves(*paths) compile_PBS_file_generic(GameData::Move, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_moves : validate_compiled_move(hash) end end def validate_compiled_move(hash) if (hash[:category] || 2) == 2 && (hash[:power] || 0) != 0 raise _INTL("Move {1} is defined as a Status move with a non-zero base damage.", hash[:real_name]) + "\n" + FileLineData.linereport elsif (hash[:category] || 2) != 2 && (hash[:power] || 0) == 0 print _INTL("Warning: Move {1} is defined as Physical or Special but has a base damage of 0. Changing it to a Status move.", hash[:real_name]) + "\n" + FileLineData.linereport hash[:category] = 2 end end def validate_all_compiled_moves # Get move names/descriptions for translating move_names = [] move_descriptions = [] GameData::Move.each do |move| move_names.push(move.real_name) move_descriptions.push(move.real_description) end MessageTypes.setMessagesAsHash(MessageTypes::MOVE_NAMES, move_names) MessageTypes.setMessagesAsHash(MessageTypes::MOVE_DESCRIPTIONS, move_descriptions) end #----------------------------------------------------------------------------- # Compile item data. #----------------------------------------------------------------------------- def compile_items(*paths) compile_PBS_file_generic(GameData::Item, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_items : validate_compiled_item(hash) end end def validate_compiled_item(hash) end def validate_all_compiled_items # Get item names/descriptions for translating item_names = [] item_names_plural = [] item_portion_names = [] item_portion_names_plural = [] item_descriptions = [] GameData::Item.each do |item| item_names.push(item.real_name) item_names_plural.push(item.real_name_plural) item_portion_names.push(item.real_portion_name) item_portion_names_plural.push(item.real_portion_name_plural) item_descriptions.push(item.real_description) end MessageTypes.setMessagesAsHash(MessageTypes::ITEM_NAMES, item_names) MessageTypes.setMessagesAsHash(MessageTypes::ITEM_NAME_PLURALS, item_names_plural) MessageTypes.setMessagesAsHash(MessageTypes::ITEM_PORTION_NAMES, item_portion_names) MessageTypes.setMessagesAsHash(MessageTypes::ITEM_PORTION_NAME_PLURALS, item_portion_names_plural) MessageTypes.setMessagesAsHash(MessageTypes::ITEM_DESCRIPTIONS, item_descriptions) end #----------------------------------------------------------------------------- # Compile berry plant data. #----------------------------------------------------------------------------- def compile_berry_plants(*paths) compile_PBS_file_generic(GameData::BerryPlant, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_berry_plants : validate_compiled_berry_plant(hash) end end def validate_compiled_berry_plant(hash) end def validate_all_compiled_berry_plants end #----------------------------------------------------------------------------- # Compile Pokémon data. #----------------------------------------------------------------------------- def compile_pokemon(*paths) compile_PBS_file_generic(GameData::Species, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_pokemon : validate_compiled_pokemon(hash) end end # NOTE: This method is also called by def validate_compiled_pokemon_form # below, and since a form's hash can contain very little data, don't # assume any data exists. def validate_compiled_pokemon(hash) # Convert base stats array to a hash if hash[:base_stats].is_a?(Array) new_stats = {} GameData::Stat.each_main do |s| new_stats[s.id] = (hash[:base_stats][s.pbs_order] || 1) if s.pbs_order >= 0 end hash[:base_stats] = new_stats end # Convert EVs array to a hash if hash[:evs].is_a?(Array) new_evs = {} hash[:evs].each { |val| new_evs[val[0]] = val[1] } GameData::Stat.each_main { |s| new_evs[s.id] ||= 0 } hash[:evs] = new_evs end # Convert height and weight to integer values of tenths of a unit hash[:height] = [(hash[:height] * 10).round, 1].max if hash[:height] hash[:weight] = [(hash[:weight] * 10).round, 1].max if hash[:weight] # Ensure evolutions have a parameter if they need one (don't need to ensure # the parameter makes sense; that happens below) if hash[:evolutions] hash[:evolutions].each do |evo| FileLineData.setSection(hash[:id].to_s, "Evolution", "Evolution = #{evo[0]},#{evo[1]}") # For error reporting param_type = GameData::Evolution.get(evo[1]).parameter next if evo[2] || param_type.nil? raise _INTL("Evolution method {1} requires a parameter, but none was given.", evo[1]) + "\n" + FileLineData.linereport end end # Record all evolutions as not being prevolutions if hash[:evolutions].is_a?(Array) hash[:evolutions].each { |evo| evo[3] = false } end # Remove duplicate types if hash[:types].is_a?(Array) hash[:types].uniq! hash[:types].compact! end end def validate_all_compiled_pokemon # Enumerate all offspring species (this couldn't be done earlier) GameData::Species.each do |species| FileLineData.setSection(species.id.to_s, "Offspring", nil) # For error reporting offspring = species.offspring offspring.each_with_index do |sp, i| offspring[i] = cast_csv_value(sp, "e", :Species) end end # Enumerate all evolution species and parameters (this couldn't be done earlier) GameData::Species.each do |species| FileLineData.setSection(species.id.to_s, "Evolutions", nil) # For error reporting species.evolutions.each do |evo| evo[0] = cast_csv_value(evo[0], "e", :Species) param_type = GameData::Evolution.get(evo[1]).parameter if param_type.nil? evo[2] = nil elsif param_type == Integer evo[2] = cast_csv_value(evo[2], "u") elsif param_type != String evo[2] = cast_csv_value(evo[2], "e", param_type) end end end # Add prevolution "evolution" entry for all evolved species all_evos = {} GameData::Species.each do |species| # Build a hash of prevolutions for each species species.evolutions.each do |evo| all_evos[evo[0]] = [species.species, evo[1], evo[2], true] if !all_evos[evo[0]] end end GameData::Species.each do |species| # Distribute prevolutions species.evolutions.push(all_evos[species.species].clone) if all_evos[species.species] end # Get species names/descriptions for translating species_names = [] species_form_names = [] species_categories = [] species_pokedex_entries = [] GameData::Species.each do |species| species_names.push(species.real_name) species_form_names.push(species.real_form_name) species_categories.push(species.real_category) species_pokedex_entries.push(species.real_pokedex_entry) end MessageTypes.setMessagesAsHash(MessageTypes::SPECIES_NAMES, species_names) MessageTypes.setMessagesAsHash(MessageTypes::SPECIES_FORM_NAMES, species_form_names) MessageTypes.setMessagesAsHash(MessageTypes::SPECIES_CATEGORIES, species_categories) MessageTypes.setMessagesAsHash(MessageTypes::POKEDEX_ENTRIES, species_pokedex_entries) end #----------------------------------------------------------------------------- # Compile Pokémon forms data. # NOTE: Doesn't use compile_PBS_file_generic because it needs its own schema # and shouldn't clear GameData::Species at the start. #----------------------------------------------------------------------------- def compile_pokemon_forms(*paths) schema = GameData::Species.schema(true) # Read from PBS file(s) paths.each do |path| compile_pbs_file_message_start(path) file_suffix = File.basename(path, ".txt")[GameData::Species::PBS_BASE_FILENAME[1].length + 1, path.length] || "" File.open(path, "rb") do |f| FileLineData.file = path # For error reporting # Read a whole section's lines at once, then run through this code. # contents is a hash containing all the XXX=YYY lines in that section, where # the keys are the XXX and the values are the YYY (as unprocessed strings). idx = 0 pbEachFileSection(f, schema) do |contents, section_name| echo "." if idx % 100 == 0 Graphics.update if idx % 500 == 0 idx += 1 data_hash = { :id => section_name.to_sym, :pbs_file_suffix => file_suffix } # Go through schema hash of compilable data and compile this section schema.each_key do |key| FileLineData.setSection(section_name, key, contents[key]) # For error reporting if key == "SectionName" data_hash[schema[key][0]] = get_csv_record(section_name, schema[key]) next end # Skip empty properties next if contents[key].nil? # Compile value for key if schema[key][1][0] == "^" contents[key].each do |val| value = get_csv_record(val, schema[key]) value = nil if value.is_a?(Array) && value.empty? data_hash[schema[key][0]] ||= [] data_hash[schema[key][0]].push(value) end data_hash[schema[key][0]].compact! else value = get_csv_record(contents[key], schema[key]) value = nil if value.is_a?(Array) && value.empty? data_hash[schema[key][0]] = value end end # Validate and modify the compiled data validate_compiled_pokemon_form(data_hash) if GameData::Species.exists?(data_hash[:id]) raise _INTL("Section name '{1}' is used twice.", data_hash[:id]) + "\n" + FileLineData.linereport end # Add section's data to records GameData::Species.register(data_hash) end end process_pbs_file_message_end end validate_all_compiled_pokemon_forms # Save all data GameData::Species.save end def validate_compiled_pokemon_form(hash) # Split species and form into their own values, generate compound ID from them hash[:species] = hash[:id][0] hash[:form] = hash[:id][1] hash[:id] = sprintf("%s_%d", hash[:species].to_s, hash[:form]).to_sym if !GameData::Species.exists?(hash[:species]) raise _INTL("Undefined species ID '{1}'.", hash[:species]) + "\n" + FileLineData.linereport elsif GameData::Species.exists?(hash[:id]) raise _INTL("Form {1} for species ID '{2}' is defined twice.", hash[:form], hash[:species]) + "\n" + FileLineData.linereport end # Perform the same validations on this form as for a regular species validate_compiled_pokemon(hash) # Inherit undefined properties from base species base_data = GameData::Species.get(hash[:species]) [:real_name, :real_category, :real_pokedex_entry, :base_exp, :growth_rate, :gender_ratio, :catch_rate, :happiness, :hatch_steps, :incense, :height, :weight, :color, :shape, :habitat, :generation].each do |property| hash[property] = base_data.send(property) if hash[property].nil? end [:types, :base_stats, :evs, :tutor_moves, :egg_moves, :abilities, :hidden_abilities, :egg_groups, :offspring, :flags].each do |property| hash[property] = base_data.send(property).clone if hash[property].nil? end if !hash[:moves].is_a?(Array) || hash[:moves].length == 0 hash[:moves] ||= [] base_data.moves.each { |m| hash[:moves].push(m.clone) } end if !hash[:evolutions].is_a?(Array) || hash[:evolutions].length == 0 hash[:evolutions] ||= [] base_data.evolutions.each { |e| hash[:evolutions].push(e.clone) } end if hash[:wild_item_common].nil? && hash[:wild_item_uncommon].nil? && hash[:wild_item_rare].nil? hash[:wild_item_common] = base_data.wild_item_common.clone hash[:wild_item_uncommon] = base_data.wild_item_uncommon.clone hash[:wild_item_rare] = base_data.wild_item_rare.clone end end def validate_all_compiled_pokemon_forms # Enumerate all evolution parameters (this couldn't be done earlier) GameData::Species.each do |species| FileLineData.setSection(species.id.to_s, "Evolutions", nil) # For error reporting species.evolutions.each do |evo| param_type = GameData::Evolution.get(evo[1]).parameter if param_type.nil? evo[2] = nil elsif param_type == Integer evo[2] = cast_csv_value(evo[2], "u") if evo[2].is_a?(String) elsif param_type != String evo[2] = cast_csv_value(evo[2], "e", param_type) if evo[2].is_a?(String) end end end # Add prevolution "evolution" entry for all evolved species all_evos = {} GameData::Species.each do |species| # Build a hash of prevolutions for each species species.evolutions.each do |evo| next if evo[3] all_evos[evo[0]] = [species.species, evo[1], evo[2], true] if !all_evos[evo[0]] if species.form > 0 all_evos[[evo[0], species.form]] = [species.species, evo[1], evo[2], true] if !all_evos[[evo[0], species.form]] end end end GameData::Species.each do |species| # Distribute prevolutions prevo_data = all_evos[[species.species, species.base_form]] || all_evos[species.species] next if !prevo_data # Record what species evolves from species.evolutions.delete_if { |evo| evo[3] } species.evolutions.push(prevo_data.clone) # Record that the prevolution can evolve into species prevo = GameData::Species.get(prevo_data[0]) if prevo.evolutions.none? { |evo| !evo[3] && evo[0] == species.species } prevo.evolutions.push([species.species, :None, nil]) end end # Get species names/descriptions for translating species_form_names = [] species_categories = [] species_pokedex_entries = [] GameData::Species.each do |species| next if species.form == 0 species_form_names.push(species.real_form_name) species_categories.push(species.real_category) species_pokedex_entries.push(species.real_pokedex_entry) end MessageTypes.addMessagesAsHash(MessageTypes::SPECIES_FORM_NAMES, species_form_names) MessageTypes.addMessagesAsHash(MessageTypes::SPECIES_CATEGORIES, species_categories) MessageTypes.addMessagesAsHash(MessageTypes::POKEDEX_ENTRIES, species_pokedex_entries) end #----------------------------------------------------------------------------- # Compile Pokémon metrics data. #----------------------------------------------------------------------------- def compile_pokemon_metrics(*paths) compile_PBS_file_generic(GameData::SpeciesMetrics, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_pokemon_metrics : validate_compiled_pokemon_metrics(hash) end end def validate_compiled_pokemon_metrics(hash) # Split species and form into their own values, generate compound ID from them if hash[:id].is_a?(Array) hash[:species] = hash[:id][0] hash[:form] = hash[:id][1] || 0 if hash[:form] == 0 hash[:id] = hash[:species] else hash[:id] = sprintf("%s_%d", hash[:species].to_s, hash[:form]).to_sym end end end def validate_all_compiled_pokemon_metrics end #----------------------------------------------------------------------------- # Compile Shadow Pokémon data. #----------------------------------------------------------------------------- def compile_shadow_pokemon(*paths) compile_PBS_file_generic(GameData::ShadowPokemon, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_shadow_pokemon : validate_compiled_shadow_pokemon(hash) end end def validate_compiled_shadow_pokemon(hash) # Split species and form into their own values, generate compound ID from them if hash[:id].is_a?(Array) hash[:species] = hash[:id][0] hash[:form] = hash[:id][1] || 0 if hash[:form] == 0 hash[:id] = hash[:species] else hash[:id] = sprintf("%s_%d", hash[:species].to_s, hash[:form]).to_sym end end end def validate_all_compiled_shadow_pokemon end #----------------------------------------------------------------------------- # Compile Regional Dexes. #----------------------------------------------------------------------------- def compile_regional_dexes(*paths) dex_lists = [] paths.each do |path| compile_pbs_file_message_start(path) section = nil pbCompilerEachPreppedLine(path) do |line, line_no| Graphics.update if line_no % 200 == 0 if line[/^\s*\[\s*(\d+)\s*\]\s*$/] section = $~[1].to_i if dex_lists[section] raise _INTL("Dex list number {1} is defined at least twice.", section) + "\n" + FileLineData.linereport end dex_lists[section] = [] else raise _INTL("Expected a section at the beginning of the file.") + "\n" + FileLineData.linereport if !section species_list = line.split(",") species_list.each do |species| next if !species || species.empty? s = parseSpecies(species) dex_lists[section].push(s) end end end process_pbs_file_message_end end # Check for duplicate species in a Regional Dex dex_lists.each_with_index do |list, index| unique_list = list.uniq next if list == unique_list list.each_with_index do |s, i| next if unique_list[i] == s raise _INTL("Dex list number {1} has species {2} listed twice.", index, s) + "\n" + FileLineData.linereport end end # Save all data save_data(dex_lists, "Data/regional_dexes.dat") end #----------------------------------------------------------------------------- # Compile ribbon data. #----------------------------------------------------------------------------- def compile_ribbons(*paths) compile_PBS_file_generic(GameData::Ribbon, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_ribbons : validate_compiled_ribbon(hash) end end def validate_compiled_ribbon(hash) end def validate_all_compiled_ribbons # Get ribbon names/descriptions for translating ribbon_names = [] ribbon_descriptions = [] GameData::Ribbon.each do |ribbon| ribbon_names.push(ribbon.real_name) ribbon_descriptions.push(ribbon.real_description) end MessageTypes.setMessagesAsHash(MessageTypes::RIBBON_NAMES, ribbon_names) MessageTypes.setMessagesAsHash(MessageTypes::RIBBON_DESCRIPTIONS, ribbon_descriptions) end #----------------------------------------------------------------------------- # Compile wild encounter data. #----------------------------------------------------------------------------- def compile_encounters(*paths) GameData::Encounter::DATA.clear max_level = GameData::GrowthRate.max_level paths.each do |path| compile_pbs_file_message_start(path) file_suffix = File.basename(path, ".txt")[GameData::Encounter::PBS_BASE_FILENAME.length + 1, path.length] || "" encounter_hash = nil step_chances = nil current_type = nil idx = 0 pbCompilerEachPreppedLine(path) do |line, line_no| echo "." if idx % 100 == 0 idx += 1 Graphics.update if idx % 500 == 0 next if line.length == 0 if current_type && line[/^\d+,/] # Species line values = line.split(",").collect! { |v| v.strip } if !values || values.length < 3 raise _INTL("Expected a species entry line for encounter type {1} for map {2}.", GameData::EncounterType.get(current_type).real_name, encounter_hash[:map]) + "\n" + FileLineData.linereport end values = get_csv_record(line, [nil, "vevV", nil, :Species]) values[3] = values[2] if !values[3] if values[2] > max_level raise _INTL("Level number {1} is not valid (max. {2}).", values[2], max_level) + "\n" + FileLineData.linereport elsif values[3] > max_level raise _INTL("Level number {1} is not valid (max. {2}).", values[3], max_level) + "\n" + FileLineData.linereport elsif values[2] > values[3] raise _INTL("Minimum level is greater than maximum level.") + "\n" + FileLineData.linereport end encounter_hash[:types][current_type].push(values) elsif line[/^\[\s*(.+)\s*\]$/] # Map ID line values = $~[1].split(",").collect! { |v| v.strip.to_i } values[1] = 0 if !values[1] map_number = values[0] map_version = values[1] # Add map encounter's data to records if encounter_hash encounter_hash[:types].each_value do |slots| next if !slots || slots.length == 0 slots.each_with_index do |slot, i| next if !slot slots.each_with_index do |other_slot, j| next if i == j || !other_slot next if slot[1] != other_slot[1] || slot[2] != other_slot[2] || slot[3] != other_slot[3] slot[0] += other_slot[0] slots[j] = nil end end slots.compact! slots.sort! { |a, b| (a[0] == b[0]) ? a[1].to_s <=> b[1].to_s : b[0] <=> a[0] } end GameData::Encounter.register(encounter_hash) end # Raise an error if a map/version combo is used twice key = sprintf("%s_%d", map_number, map_version).to_sym if GameData::Encounter::DATA[key] raise _INTL("Encounters for map '{1}' are defined twice.", map_number) + "\n" + FileLineData.linereport end step_chances = {} # Construct encounter hash encounter_hash = { :id => key, :map => map_number, :version => map_version, :step_chances => step_chances, :types => {}, :pbs_file_suffix => file_suffix } current_type = nil elsif !encounter_hash # File began with something other than a map ID line raise _INTL("Expected a map number, got \"{1}\" instead.", line) + "\n" + FileLineData.linereport else # Check if line is an encounter method name or not values = line.split(",").collect! { |v| v.strip } current_type = (values[0] && !values[0].empty?) ? values[0].to_sym : nil if current_type && GameData::EncounterType.exists?(current_type) # Start of a new encounter method step_chances[current_type] = values[1].to_i if values[1] && !values[1].empty? step_chances[current_type] ||= GameData::EncounterType.get(current_type).trigger_chance encounter_hash[:types][current_type] = [] else raise _INTL("Undefined encounter type \"{1}\" for map '{2}'.", line, encounter_hash[:map]) + "\n" + FileLineData.linereport end end end # Add last map's encounter data to records if encounter_hash encounter_hash[:types].each_value do |slots| next if !slots || slots.length == 0 slots.each_with_index do |slot, i| next if !slot slots.each_with_index do |other_slot, j| next if i == j || !other_slot next if slot[1] != other_slot[1] || slot[2] != other_slot[2] || slot[3] != other_slot[3] slot[0] += other_slot[0] slots[j] = nil end end slots.compact! slots.sort! { |a, b| (a[0] == b[0]) ? a[1].to_s <=> b[1].to_s : b[0] <=> a[0] } end GameData::Encounter.register(encounter_hash) end process_pbs_file_message_end end # Save all data GameData::Encounter.save end #----------------------------------------------------------------------------- # Compile trainer type data. #----------------------------------------------------------------------------- def compile_trainer_types(*paths) compile_PBS_file_generic(GameData::TrainerType, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_trainer_types : validate_compiled_trainer_type(hash) end end def validate_compiled_trainer_type(hash) # Ensure valid Poké Ball if hash[:poke_ball] if !GameData::Item.get(hash[:poke_ball]).is_poke_ball? raise _INTL("Value '{1}' isn't a defined Poké Ball.", hash[:poke_ball]) + "\n" + FileLineData.linereport end end end def validate_all_compiled_trainer_types # Get trainer type names for translating trainer_type_names = [] GameData::TrainerType.each do |tr_type| trainer_type_names.push(tr_type.real_name) end MessageTypes.setMessagesAsHash(MessageTypes::TRAINER_TYPE_NAMES, trainer_type_names) end #----------------------------------------------------------------------------- # Compile individual trainer data. #----------------------------------------------------------------------------- def compile_trainers(*paths) GameData::Trainer::DATA.clear schema = GameData::Trainer.schema sub_schema = GameData::Trainer.sub_schema idx = 0 # Read from PBS file(s) paths.each do |path| compile_pbs_file_message_start(path) file_suffix = File.basename(path, ".txt")[GameData::Trainer::PBS_BASE_FILENAME.length + 1, path.length] || "" data_hash = nil current_pkmn = nil section_name = nil section_line = nil # Read each line of trainers.txt at a time and compile it as a trainer property pbCompilerEachPreppedLine(path) do |line, line_no| echo "." if idx % 100 == 0 idx += 1 Graphics.update if idx % 500 == 0 FileLineData.setSection(section_name, nil, section_line) if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [trainer_type, name] or [trainer_type, name, version] section_name = $~[1] section_line = line if data_hash validate_compiled_trainer(data_hash) GameData::Trainer.register(data_hash) end FileLineData.setSection(section_name, nil, section_line) # Construct data hash data_hash = { :pbs_file_suffix => file_suffix } data_hash[schema["SectionName"][0]] = get_csv_record(section_name.clone, schema["SectionName"]) data_hash[schema["Pokemon"][0]] = [] current_pkmn = nil elsif line[/^\s*(\w+)\s*=\s*(.*)$/] # XXX=YYY lines if !data_hash raise _INTL("Expected a section at the beginning of the file.") + "\n" + FileLineData.linereport end key = $~[1] if schema[key] # Property of the trainer property_value = get_csv_record($~[2], schema[key]) if key == "Pokemon" current_pkmn = { :species => property_value[0], :level => property_value[1] } data_hash[schema[key][0]].push(current_pkmn) else data_hash[schema[key][0]] = property_value end elsif sub_schema[key] # Property of a Pokémon if !current_pkmn raise _INTL("Property \"{1}\" is Pokémon-specific, but a Pokémon hasn't been defined yet.", key) + "\n" + FileLineData.linereport end current_pkmn[sub_schema[key][0]] = get_csv_record($~[2], sub_schema[key]) end end end # Add last trainer's data to records if data_hash FileLineData.setSection(section_name, nil, section_line) validate_compiled_trainer(data_hash) GameData::Trainer.register(data_hash) end process_pbs_file_message_end end validate_all_compiled_trainers # Save all data GameData::Trainer.save end def validate_compiled_trainer(hash) # Split trainer type, name and version into their own values, generate compound ID from them hash[:id][2] ||= 0 hash[:trainer_type] = hash[:id][0] hash[:real_name] = hash[:id][1] hash[:version] = hash[:id][2] # Ensure the trainer has at least one Pokémon if hash[:pokemon].empty? raise _INTL("Trainer with ID '{1}' has no Pokémon.", hash[:id]) + "\n" + FileLineData.linereport end max_level = GameData::GrowthRate.max_level hash[:pokemon].each do |pkmn| # Ensure valid level if pkmn[:level] > max_level raise _INTL("Invalid Pokémon level {1} (must be 1-{2}).", pkmn[:level], max_level) + "\n" + FileLineData.linereport end # Ensure valid name length if pkmn[:real_name] && pkmn[:real_name].length > Pokemon::MAX_NAME_SIZE raise _INTL("Invalid Pokémon nickname: {1} (must be 1-{2} characters).", pkmn[:real_name], Pokemon::MAX_NAME_SIZE) + "\n" + FileLineData.linereport end # Ensure no duplicate moves pkmn[:moves].uniq! if pkmn[:moves] # Ensure valid IVs, convert IVs to hash format if pkmn[:iv] iv_hash = {} GameData::Stat.each_main do |s| next if s.pbs_order < 0 iv_hash[s.id] = pkmn[:iv][s.pbs_order] || pkmn[:iv][0] if iv_hash[s.id] > Pokemon::IV_STAT_LIMIT raise _INTL("Invalid IV: {1} (must be 0-{2}).", iv_hash[s.id], Pokemon::IV_STAT_LIMIT) + "\n" + FileLineData.linereport end end pkmn[:iv] = iv_hash end # Ensure valid EVs, convert EVs to hash format if pkmn[:ev] ev_hash = {} ev_total = 0 GameData::Stat.each_main do |s| next if s.pbs_order < 0 ev_hash[s.id] = pkmn[:ev][s.pbs_order] || pkmn[:ev][0] ev_total += ev_hash[s.id] if ev_hash[s.id] > Pokemon::EV_STAT_LIMIT raise _INTL("Invalid EV: {1} (must be 0-{2}).", ev_hash[s.id], Pokemon::EV_STAT_LIMIT) + "\n" + FileLineData.linereport end end pkmn[:ev] = ev_hash if ev_total > Pokemon::EV_LIMIT raise _INTL("Invalid EV set (must sum to {1} or less).", Pokemon::EV_LIMIT) + "\n" + FileLineData.linereport end end # Ensure valid happiness if pkmn[:happiness] if pkmn[:happiness] > 255 raise _INTL("Bad happiness: {1} (must be 0-255).", pkmn[:happiness]) + "\n" + FileLineData.linereport end end # Ensure valid Poké Ball if pkmn[:poke_ball] if !GameData::Item.get(pkmn[:poke_ball]).is_poke_ball? raise _INTL("Value '{1}' isn't a defined Poké Ball.", pkmn[:poke_ball]) + "\n" + FileLineData.linereport end end end end def validate_all_compiled_trainers # Get trainer names and lose texts for translating trainer_names = [] lose_texts = [] pokemon_nicknames = [] GameData::Trainer.each do |trainer| trainer_names.push(trainer.real_name) lose_texts.push(trainer.real_lose_text) trainer.pokemon.each do |pkmn| pokemon_nicknames.push(pkmn[:real_name]) if !nil_or_empty?(pkmn[:real_name]) end end MessageTypes.setMessagesAsHash(MessageTypes::TRAINER_NAMES, trainer_names) MessageTypes.setMessagesAsHash(MessageTypes::TRAINER_SPEECHES_LOSE, lose_texts) MessageTypes.setMessagesAsHash(MessageTypes::POKEMON_NICKNAMES, pokemon_nicknames) end #----------------------------------------------------------------------------- # Compile Battle Tower and other Cups trainers/Pokémon. #----------------------------------------------------------------------------- def compile_trainer_lists(path = "PBS/battle_facility_lists.txt") compile_pbs_file_message_start(path) btTrainersRequiredTypes = { "Trainers" => [0, "s"], "Pokemon" => [1, "s"], "Challenges" => [2, "*s"] } if !FileTest.exist?(path) File.open(path, "wb") do |f| f.write(0xEF.chr) f.write(0xBB.chr) f.write(0xBF.chr) f.write("[DefaultTrainerList]\r\n") f.write("Trainers = battle_tower_trainers.txt\r\n") f.write("Pokemon = battle_tower_pokemon.txt\r\n") end end sections = [] MessageTypes.setMessagesAsHash(MessageTypes::FRONTIER_INTRO_SPEECHES, []) MessageTypes.setMessagesAsHash(MessageTypes::FRONTIER_END_SPEECHES_WIN, []) MessageTypes.setMessagesAsHash(MessageTypes::FRONTIER_END_SPEECHES_LOSE, []) File.open(path, "rb") do |f| FileLineData.file = path idx = 0 pbEachFileSection(f) do |section, name| echo "." idx += 1 Graphics.update next if name != "DefaultTrainerList" && name != "TrainerList" rsection = [] section.each_key do |key| FileLineData.setSection(name, key, section[key]) schema = btTrainersRequiredTypes[key] next if key == "Challenges" && name == "DefaultTrainerList" next if !schema record = get_csv_record(section[key], schema) rsection[schema[0]] = record end if !rsection[0] raise _INTL("No trainer data file given in section {1}.", name) + "\n" + FileLineData.linereport end if !rsection[1] raise _INTL("No trainer data file given in section {1}.", name) + "\n" + FileLineData.linereport end rsection[3] = rsection[0] rsection[4] = rsection[1] rsection[5] = (name == "DefaultTrainerList") if FileTest.exist?("PBS/" + rsection[0]) rsection[0] = compile_battle_tower_trainers("PBS/" + rsection[0]) else rsection[0] = [] end if FileTest.exist?("PBS/" + rsection[1]) filename = "PBS/" + rsection[1] rsection[1] = [] pbCompilerEachCommentedLine(filename) do |line, _lineno| rsection[1].push(PBPokemon.fromInspected(line)) end else rsection[1] = [] end rsection[2] = [] if !rsection[2] while rsection[2].include?("") rsection[2].delete("") end rsection[2].compact! sections.push(rsection) end end save_data(sections, "Data/trainer_lists.dat") process_pbs_file_message_end end def compile_battle_tower_trainers(filename) sections = [] requiredtypes = { "Type" => [0, "e", :TrainerType], "Name" => [1, "s"], "BeginSpeech" => [2, "s"], "EndSpeechWin" => [3, "s"], "EndSpeechLose" => [4, "s"], "PokemonNos" => [5, "*u"] } trainernames = [] beginspeech = [] endspeechwin = [] endspeechlose = [] if FileTest.exist?(filename) File.open(filename, "rb") do |f| FileLineData.file = filename pbEachFileSection(f) do |section, name| rsection = [] section.each_key do |key| FileLineData.setSection(name, key, section[key]) schema = requiredtypes[key] next if !schema record = get_csv_record(section[key], schema) rsection[schema[0]] = record end trainernames.push(rsection[1]) beginspeech.push(rsection[2]) endspeechwin.push(rsection[3]) endspeechlose.push(rsection[4]) sections.push(rsection) end end end MessageTypes.addMessagesAsHash(MessageTypes::TRAINER_NAMES, trainernames) MessageTypes.addMessagesAsHash(MessageTypes::FRONTIER_INTRO_SPEECHES, beginspeech) MessageTypes.addMessagesAsHash(MessageTypes::FRONTIER_END_SPEECHES_WIN, endspeechwin) MessageTypes.addMessagesAsHash(MessageTypes::FRONTIER_END_SPEECHES_LOSE, endspeechlose) return sections end #----------------------------------------------------------------------------- # Compile metadata. # NOTE: Doesn't use compile_PBS_file_generic because it contains data for two # different GameData classes. #----------------------------------------------------------------------------- def compile_metadata(*paths) GameData::Metadata::DATA.clear GameData::PlayerMetadata::DATA.clear global_schema = GameData::Metadata.schema player_schema = GameData::PlayerMetadata.schema paths.each do |path| compile_pbs_file_message_start(path) file_suffix = File.basename(path, ".txt")[GameData::Metadata::PBS_BASE_FILENAME.length + 1, path.length] || "" # Read from PBS file File.open(path, "rb") do |f| FileLineData.file = path # For error reporting # Read a whole section's lines at once, then run through this code. # contents is a hash containing all the XXX=YYY lines in that section, where # the keys are the XXX and the values are the YYY (as unprocessed strings). idx = 0 pbEachFileSection(f) do |contents, section_name| echo "." if idx % 100 == 0 Graphics.update if idx % 500 == 0 idx += 1 schema = (section_name.to_i == 0) ? global_schema : player_schema data_hash = { :id => section_name.to_sym, :pbs_file_suffix => file_suffix } # Go through schema hash of compilable data and compile this section schema.each_key do |key| FileLineData.setSection(section_name, key, contents[key]) # For error reporting if key == "SectionName" data_hash[schema[key][0]] = get_csv_record(section_name, schema[key]) next end # Skip empty properties next if contents[key].nil? # Compile value for key if schema[key][1][0] == "^" contents[key].each do |val| value = get_csv_record(val, schema[key]) value = nil if value.is_a?(Array) && value.empty? data_hash[schema[key][0]] ||= [] data_hash[schema[key][0]].push(value) end data_hash[schema[key][0]].compact! else value = get_csv_record(contents[key], schema[key]) value = nil if value.is_a?(Array) && value.empty? data_hash[schema[key][0]] = value end end # Validate and modify the compiled data if data_hash[:id] == 0 validate_compiled_global_metadata(data_hash) if GameData::Metadata.exists?(data_hash[:id]) raise _INTL("Global metadata ID '{1}' is used twice.", data_hash[:id]) + "\n" + FileLineData.linereport end else validate_compiled_player_metadata(data_hash) if GameData::PlayerMetadata.exists?(data_hash[:id]) raise _INTL("Player metadata ID '{1}' is used twice.", data_hash[:id]) + "\n" + FileLineData.linereport end end # Add section's data to records if data_hash[:id] == 0 GameData::Metadata.register(data_hash) else GameData::PlayerMetadata.register(data_hash) end end end process_pbs_file_message_end end validate_all_compiled_metadata # Save all data GameData::Metadata.save GameData::PlayerMetadata.save end def validate_compiled_global_metadata(hash) if hash[:home].nil? raise _INTL("The entry 'Home' is required in metadata.txt section 0.") + "\n" + FileLineData.linereport end end def validate_compiled_player_metadata(hash) end # Should be used to check both global metadata and player character metadata. def validate_all_compiled_metadata # Ensure global metadata is defined if !GameData::Metadata.exists?(0) raise _INTL("Global metadata is not defined in metadata.txt but should be.") + "\n" + FileLineData.linereport end # Ensure player character 1's metadata is defined if !GameData::PlayerMetadata.exists?(1) raise _INTL("Metadata for player character 1 is not defined in metadata.txt but should be.") + "\n" + FileLineData.linereport end # Get storage creator's name for translating storage_creator = [GameData::Metadata.get.real_storage_creator] MessageTypes.setMessagesAsHash(MessageTypes::STORAGE_CREATOR_NAME, storage_creator) end #----------------------------------------------------------------------------- # Compile map metadata. #----------------------------------------------------------------------------- def compile_map_metadata(*paths) compile_PBS_file_generic(GameData::MapMetadata, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_map_metadata : validate_compiled_map_metadata(hash) end end def validate_compiled_map_metadata(hash) # Give the map its RMXP map name if it doesn't define its own if nil_or_empty?(hash[:real_name]) hash[:real_name] = pbLoadMapInfos[hash[:id]].name end end def validate_all_compiled_map_metadata # Get map names for translating map_names = [] GameData::MapMetadata.each { |map| map_names[map.id] = map.real_name } MessageTypes.setMessagesAsHash(MessageTypes::MAP_NAMES, map_names) end #----------------------------------------------------------------------------- # Compile dungeon tileset data. #----------------------------------------------------------------------------- def compile_dungeon_tilesets(*paths) compile_PBS_file_generic(GameData::DungeonTileset, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_dungeon_tilesets : validate_compiled_dungeon_tileset(hash) end end def validate_compiled_dungeon_tileset(hash) end def validate_all_compiled_dungeon_tilesets end #----------------------------------------------------------------------------- # Compile dungeon parameters data. #----------------------------------------------------------------------------- def compile_dungeon_parameters(*paths) compile_PBS_file_generic(GameData::DungeonParameters, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_dungeon_parameters : validate_compiled_dungeon_parameters(hash) end end def validate_compiled_dungeon_parameters(hash) # Split area and version into their own values, generate compound ID from them hash[:area] = hash[:id][0] hash[:version] = hash[:id][1] || 0 if hash[:version] == 0 hash[:id] = hash[:area] else hash[:id] = sprintf("%s_%d", hash[:area].to_s, hash[:version]).to_sym end if GameData::DungeonParameters.exists?(hash[:id]) raise _INTL("Version {1} of dungeon area {2} is defined twice.", hash[:version], hash[:area]) + "\n" + FileLineData.linereport end end def validate_all_compiled_dungeon_parameters end #----------------------------------------------------------------------------- # Compile phone messages. #----------------------------------------------------------------------------- def compile_phone(*paths) compile_PBS_file_generic(GameData::PhoneMessage, *paths) do |final_validate, hash| (final_validate) ? validate_all_compiled_phone_contacts : validate_compiled_phone_contact(hash) end end def validate_compiled_phone_contact(hash) # Split trainer type/name/version into their own values, generate compound ID from them if hash[:id].strip.downcase == "default" hash[:id] = "default" hash[:trainer_type] = hash[:id] else line_data = get_csv_record(hash[:id], [nil, "esU", :TrainerType]) hash[:trainer_type] = line_data[0] hash[:real_name] = line_data[1] hash[:version] = line_data[2] || 0 hash[:id] = [hash[:trainer_type], hash[:real_name], hash[:version]] end end def validate_all_compiled_phone_contacts # Get all phone messages for translating messages = [] GameData::PhoneMessage.each do |contact| [:intro, :intro_morning, :intro_afternoon, :intro_evening, :body, :body1, :body2, :battle_request, :battle_remind, :end].each do |msg_type| msgs = contact.send(msg_type) next if !msgs || msgs.length == 0 msgs.each { |msg| messages.push(msg) } end end MessageTypes.setMessagesAsHash(MessageTypes::PHONE_MESSAGES, messages) end end