diff --git a/Data/Scripts/010_Data/002_PBS data/002_Type.rb b/Data/Scripts/010_Data/002_PBS data/002_Type.rb index 7ee4bd041..5b2f620d7 100644 --- a/Data/Scripts/010_Data/002_PBS data/002_Type.rb +++ b/Data/Scripts/010_Data/002_PBS data/002_Type.rb @@ -14,15 +14,15 @@ module GameData DATA_FILENAME = "types.dat" SCHEMA = { - "Name" => [0, "s"], - "InternalName" => [0, "s"], - "IsSpecialType" => [0, "b"], - "IsPseudoType" => [0, "b"], - "Flags" => [0, "*s"], - "Weaknesses" => [0, "*s"], - "Resistances" => [0, "*s"], - "Immunities" => [0, "*s"], - "IconPosition" => [0, "u"] + "SectionName" => [:id, "m"], + "Name" => [:real_name, "s"], + "IsSpecialType" => [:special_type, "b"], + "IsPseudoType" => [:pseudo_type, "b"], + "Flags" => [:flags, "*s"], + "Weaknesses" => [:weaknesses, "*m"], + "Resistances" => [:resistances, "*m"], + "Immunities" => [:immunities, "*m"], + "IconPosition" => [:icon_position, "u"] } extend ClassMethodsSymbols @@ -30,7 +30,7 @@ module GameData def initialize(hash) @id = hash[:id] - @real_name = hash[:name] || "Unnamed" + @real_name = hash[:real_name] || "Unnamed" @special_type = hash[:special_type] || false @pseudo_type = hash[:pseudo_type] || false @flags = hash[:flags] || [] diff --git a/Data/Scripts/010_Data/002_PBS data/003_Ability.rb b/Data/Scripts/010_Data/002_PBS data/003_Ability.rb index a34535e30..182ea2deb 100644 --- a/Data/Scripts/010_Data/002_PBS data/003_Ability.rb +++ b/Data/Scripts/010_Data/002_PBS data/003_Ability.rb @@ -12,16 +12,17 @@ module GameData include InstanceMethods SCHEMA = { - "Name" => [:name, "s"], - "Description" => [:description, "q"], - "Flags" => [:flags, "*s"] + "SectionName" => [:id, "m"], + "Name" => [:real_name, "s"], + "Description" => [:real_description, "q"], + "Flags" => [:flags, "*s"] } def initialize(hash) @id = hash[:id] - @real_name = hash[:name] || "Unnamed" - @real_description = hash[:description] || "???" - @flags = hash[:flags] || [] + @real_name = hash[:real_name] || "Unnamed" + @real_description = hash[:real_description] || "???" + @flags = hash[:flags] || [] end # @return [String] the translated name of this ability diff --git a/Data/Scripts/010_Data/002_PBS data/004_Move.rb b/Data/Scripts/010_Data/002_PBS data/004_Move.rb index 2241b79ed..d28494deb 100644 --- a/Data/Scripts/010_Data/002_PBS data/004_Move.rb +++ b/Data/Scripts/010_Data/002_PBS data/004_Move.rb @@ -19,21 +19,19 @@ module GameData DATA_FILENAME = "moves.dat" SCHEMA = { - "Name" => [:name, "s"], - "Type" => [:type, "e", :Type], - "Category" => [:category, "e", ["Physical", "Special", "Status"]], - "Power" => [:base_damage, "u"], - "Accuracy" => [:accuracy, "u"], - "TotalPP" => [:total_pp, "u"], - "Target" => [:target, "e", :Target], - "Priority" => [:priority, "i"], - "FunctionCode" => [:function_code, "s"], - "Flags" => [:flags, "*s"], - "EffectChance" => [:effect_chance, "u"], - "Description" => [:description, "q"], - # All properties below here are old names for some properties above. - # They will be removed in v21. - "BaseDamage" => [:base_damage, "u"] + "SectionName" => [:id, "m"], + "Name" => [:real_name, "s"], + "Type" => [:type, "e", :Type], + "Category" => [:category, "e", ["Physical", "Special", "Status"]], + "Power" => [:base_damage, "u"], + "Accuracy" => [:accuracy, "u"], + "TotalPP" => [:total_pp, "u"], + "Target" => [:target, "e", :Target], + "Priority" => [:priority, "i"], + "FunctionCode" => [:function_code, "s"], + "Flags" => [:flags, "*s"], + "EffectChance" => [:effect_chance, "u"], + "Description" => [:real_description, "q"] } extend ClassMethodsSymbols @@ -42,19 +40,19 @@ module GameData def initialize(hash) convert_move_data(hash) @id = hash[:id] - @real_name = hash[:name] || "Unnamed" - @type = hash[:type] || :NONE - @category = hash[:category] || 2 - @base_damage = hash[:base_damage] || 0 - @accuracy = hash[:accuracy] || 100 - @total_pp = hash[:total_pp] || 5 - @target = hash[:target] || :None - @priority = hash[:priority] || 0 - @function_code = hash[:function_code] || "None" - @flags = hash[:flags] || [] + @real_name = hash[:real_name] || "Unnamed" + @type = hash[:type] || :NONE + @category = hash[:category] || 2 + @base_damage = hash[:base_damage] || 0 + @accuracy = hash[:accuracy] || 100 + @total_pp = hash[:total_pp] || 5 + @target = hash[:target] || :None + @priority = hash[:priority] || 0 + @function_code = hash[:function_code] || "None" + @flags = hash[:flags] || [] @flags = [@flags] if !@flags.is_a?(Array) - @effect_chance = hash[:effect_chance] || 0 - @real_description = hash[:description] || "???" + @effect_chance = hash[:effect_chance] || 0 + @real_description = hash[:real_description] || "???" end # @return [String] the translated name of this move diff --git a/Data/Scripts/010_Data/002_PBS data/005_Item.rb b/Data/Scripts/010_Data/002_PBS data/005_Item.rb index 0c377b323..6c2b74d71 100644 --- a/Data/Scripts/010_Data/002_PBS data/005_Item.rb +++ b/Data/Scripts/010_Data/002_PBS data/005_Item.rb @@ -9,27 +9,28 @@ module GameData attr_reader :real_description attr_reader :field_use attr_reader :battle_use - attr_reader :consumable attr_reader :flags + attr_reader :consumable attr_reader :move DATA = {} DATA_FILENAME = "items.dat" SCHEMA = { - "Name" => [:name, "s"], - "NamePlural" => [:name_plural, "s"], - "Pocket" => [:pocket, "v"], - "Price" => [:price, "u"], - "SellPrice" => [:sell_price, "u"], - "Description" => [:description, "q"], - "FieldUse" => [:field_use, "e", { "OnPokemon" => 1, "Direct" => 2, "TM" => 3, - "HM" => 4, "TR" => 5 }], - "BattleUse" => [:battle_use, "e", { "OnPokemon" => 1, "OnMove" => 2, "OnBattler" => 3, - "OnFoe" => 4, "Direct" => 5 }], - "Consumable" => [:consumable, "b"], - "Flags" => [:flags, "*s"], - "Move" => [:move, "e", :Move] + "SectionName" => [:id, "m"], + "Name" => [:real_name, "s"], + "NamePlural" => [:real_name_plural, "s"], + "Pocket" => [:pocket, "v"], + "Price" => [:price, "u"], + "SellPrice" => [:sell_price, "u"], + "Description" => [:real_description, "q"], + "FieldUse" => [:field_use, "e", { "OnPokemon" => 1, "Direct" => 2, "TM" => 3, + "HM" => 4, "TR" => 5 }], + "BattleUse" => [:battle_use, "e", { "OnPokemon" => 1, "OnMove" => 2, "OnBattler" => 3, + "OnFoe" => 4, "Direct" => 5 }], + "Flags" => [:flags, "*s"], + "Consumable" => [:consumable, "b"], + "Move" => [:move, "e", :Move] } extend ClassMethodsSymbols @@ -82,15 +83,15 @@ module GameData def initialize(hash) @id = hash[:id] - @real_name = hash[:name] || "Unnamed" - @real_name_plural = hash[:name_plural] || "Unnamed" - @pocket = hash[:pocket] || 1 - @price = hash[:price] || 0 - @sell_price = hash[:sell_price] || (@price / 2) - @real_description = hash[:description] || "???" - @field_use = hash[:field_use] || 0 - @battle_use = hash[:battle_use] || 0 - @flags = hash[:flags] || [] + @real_name = hash[:real_name] || "Unnamed" + @real_name_plural = hash[:real_name_plural] || "Unnamed" + @pocket = hash[:pocket] || 1 + @price = hash[:price] || 0 + @sell_price = hash[:sell_price] || (@price / 2) + @real_description = hash[:real_description] || "???" + @field_use = hash[:field_use] || 0 + @battle_use = hash[:battle_use] || 0 + @flags = hash[:flags] || [] @consumable = hash[:consumable] @consumable = !is_important? if @consumable.nil? @move = hash[:move] diff --git a/Data/Scripts/010_Data/002_PBS data/006_BerryPlant.rb b/Data/Scripts/010_Data/002_PBS data/006_BerryPlant.rb index 054e84be7..47490f4ad 100644 --- a/Data/Scripts/010_Data/002_PBS data/006_BerryPlant.rb +++ b/Data/Scripts/010_Data/002_PBS data/006_BerryPlant.rb @@ -9,6 +9,7 @@ module GameData DATA_FILENAME = "berry_plants.dat" SCHEMA = { + "SectionName" => [:id, "m"], "HoursPerStage" => [:hours_per_stage, "v"], "DryingPerHour" => [:drying_per_hour, "u"], "Yield" => [:yield, "uv"] diff --git a/Data/Scripts/010_Data/002_PBS data/007_Species.rb b/Data/Scripts/010_Data/002_PBS data/007_Species.rb index 97ff78e03..91f02de10 100644 --- a/Data/Scripts/010_Data/002_PBS data/007_Species.rb +++ b/Data/Scripts/010_Data/002_PBS data/007_Species.rb @@ -73,70 +73,50 @@ module GameData def self.schema(compiling_forms = false) ret = { - "FormName" => [0, "q"], - "Category" => [0, "s"], - "Pokedex" => [0, "q"], - "Types" => [0, "eE", :Type, :Type], - "BaseStats" => [0, "vvvvvv"], - "EVs" => [0, "*ev", :Stat], - "BaseExp" => [0, "v"], - "CatchRate" => [0, "u"], - "Happiness" => [0, "u"], - "Moves" => [0, "*ue", nil, :Move], - "TutorMoves" => [0, "*e", :Move], - "EggMoves" => [0, "*e", :Move], - "Abilities" => [0, "*e", :Ability], - "HiddenAbilities" => [0, "*e", :Ability], - "WildItemCommon" => [0, "*e", :Item], - "WildItemUncommon" => [0, "*e", :Item], - "WildItemRare" => [0, "*e", :Item], - "EggGroups" => [0, "*e", :EggGroup], - "HatchSteps" => [0, "v"], - "Height" => [0, "f"], - "Weight" => [0, "f"], - "Color" => [0, "e", :BodyColor], - "Shape" => [0, "e", :BodyShape], - "Habitat" => [0, "e", :Habitat], - "Generation" => [0, "i"], - "Flags" => [0, "*s"], - "BattlerPlayerX" => [0, "i"], - "BattlerPlayerY" => [0, "i"], - "BattlerEnemyX" => [0, "i"], - "BattlerEnemyY" => [0, "i"], - "BattlerAltitude" => [0, "i"], - "BattlerShadowX" => [0, "i"], - "BattlerShadowSize" => [0, "u"], - # All properties below here are old names for some properties above. - # They will be removed in v21. - "Type1" => [0, "e", :Type], - "Type2" => [0, "e", :Type], - "Rareness" => [0, "u"], - "Compatibility" => [0, "*e", :EggGroup], - "Kind" => [0, "s"], - "BaseEXP" => [0, "v"], - "EffortPoints" => [0, "*ev", :Stat], - "HiddenAbility" => [0, "*e", :Ability], - "StepsToHatch" => [0, "v"] + "FormName" => [:real_form_name, "q"], + "Category" => [:real_category, "s"], + "Pokedex" => [:real_pokedex_entry, "q"], + "Types" => [:types, "eE", :Type, :Type], + "BaseStats" => [:base_stats, "vvvvvv"], + "EVs" => [:evs, "*ev", :Stat], + "BaseExp" => [:base_exp, "v"], + "CatchRate" => [:catch_rate, "u"], + "Happiness" => [:happiness, "u"], + "Moves" => [:moves, "*ue", nil, :Move], + "TutorMoves" => [:tutor_moves, "*e", :Move], + "EggMoves" => [:egg_moves, "*e", :Move], + "Abilities" => [:abilities, "*e", :Ability], + "HiddenAbilities" => [:hidden_abilities, "*e", :Ability], + "WildItemCommon" => [:wild_item_common, "*e", :Item], + "WildItemUncommon" => [:wild_item_uncommon, "*e", :Item], + "WildItemRare" => [:wild_item_rare, "*e", :Item], + "EggGroups" => [:egg_groups, "*e", :EggGroup], + "HatchSteps" => [:hatch_steps, "v"], + "Height" => [:height, "f"], + "Weight" => [:weight, "f"], + "Color" => [:color, "e", :BodyColor], + "Shape" => [:shape, "e", :BodyShape], + "Habitat" => [:habitat, "e", :Habitat], + "Generation" => [:generation, "i"], + "Flags" => [:flags, "*s"] } if compiling_forms - ret["PokedexForm"] = [0, "u"] - ret["Offspring"] = [0, "*e", :Species] - ret["Evolutions"] = [0, "*ees", :Species, :Evolution, nil] - ret["MegaStone"] = [0, "e", :Item] - ret["MegaMove"] = [0, "e", :Move] - ret["UnmegaForm"] = [0, "u"] - ret["MegaMessage"] = [0, "u"] + ret["SectionName"] = [:id, "ev", :Species] + ret["PokedexForm"] = [:pokedex_form, "u"] + ret["Offspring"] = [:offspring, "*e", :Species] + ret["Evolutions"] = [:evolutions, "*ees", :Species, :Evolution, nil] + ret["MegaStone"] = [:mega_stone, "e", :Item] + ret["MegaMove"] = [:mega_move, "e", :Move] + ret["UnmegaForm"] = [:unmega_form, "u"] + ret["MegaMessage"] = [:mega_message, "u"] else - ret["InternalName"] = [0, "n"] - ret["Name"] = [0, "s"] - ret["GrowthRate"] = [0, "e", :GrowthRate] - ret["GenderRatio"] = [0, "e", :GenderRatio] - ret["Incense"] = [0, "e", :Item] - ret["Offspring"] = [0, "*s"] - ret["Evolutions"] = [0, "*ses", nil, :Evolution, nil] - # All properties below here are old names for some properties above. - # They will be removed in v21. - ret["GenderRate"] = [0, "e", :GenderRatio] + ret["SectionName"] = [:id, "m"] + ret["Name"] = [:real_name, "s"] + ret["GrowthRate"] = [:growth_rate, "e", :GrowthRate] + ret["GenderRatio"] = [:gender_ratio, "e", :GenderRatio] + ret["Incense"] = [:incense, "e", :Item] + ret["Offspring"] = [:offspring, "*s"] + ret["Evolutions"] = [:evolutions, "*ses", nil, :Evolution, nil] end return ret end @@ -145,10 +125,10 @@ module GameData @id = hash[:id] @species = hash[:species] || @id @form = hash[:form] || 0 - @real_name = hash[:name] || "Unnamed" - @real_form_name = hash[:form_name] - @real_category = hash[:category] || "???" - @real_pokedex_entry = hash[:pokedex_entry] || "???" + @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] || {} diff --git a/Data/Scripts/010_Data/002_PBS data/009_SpeciesMetrics.rb b/Data/Scripts/010_Data/002_PBS data/009_SpeciesMetrics.rb index ae1b222c5..910f5a043 100644 --- a/Data/Scripts/010_Data/002_PBS data/009_SpeciesMetrics.rb +++ b/Data/Scripts/010_Data/002_PBS data/009_SpeciesMetrics.rb @@ -13,11 +13,12 @@ module GameData DATA_FILENAME = "species_metrics.dat" SCHEMA = { - "BackSprite" => [0, "ii"], - "FrontSprite" => [0, "ii"], - "FrontSpriteAltitude" => [0, "i"], - "ShadowX" => [0, "i"], - "ShadowSize" => [0, "u"] + "SectionName" => [:id, "eV", :Species], + "BackSprite" => [:back_sprite, "ii"], + "FrontSprite" => [:front_sprite, "ii"], + "FrontSpriteAltitude" => [:front_sprite_altitude, "i"], + "ShadowX" => [:shadow_x, "i"], + "ShadowSize" => [:shadow_size, "u"] } extend ClassMethodsSymbols diff --git a/Data/Scripts/010_Data/002_PBS data/010_ShadowPokemon.rb b/Data/Scripts/010_Data/002_PBS data/010_ShadowPokemon.rb index 828ed7667..8cfe28db9 100644 --- a/Data/Scripts/010_Data/002_PBS data/010_ShadowPokemon.rb +++ b/Data/Scripts/010_Data/002_PBS data/010_ShadowPokemon.rb @@ -9,9 +9,10 @@ module GameData DATA_FILENAME = "shadow_pokemon.dat" SCHEMA = { - "GaugeSize" => [:gauge_size, "v"], - "Moves" => [:moves, "*s"], # Not enumerated when compiled - "Flags" => [:flags, "*s"] + "SectionName" => [:id, "e", :Species], + "GaugeSize" => [:gauge_size, "v"], + "Moves" => [:moves, "*m"], # Not enumerated when compiled + "Flags" => [:flags, "*s"] } HEART_GAUGE_SIZE = 4000 # Default gauge size @@ -20,8 +21,8 @@ module GameData def initialize(hash) @id = hash[:id] - @moves = hash[:moves] || [] @gauge_size = hash[:gauge_size] || HEART_GAUGE_SIZE + @moves = hash[:moves] || [] @flags = hash[:flags] || [] end diff --git a/Data/Scripts/010_Data/002_PBS data/011_Ribbon.rb b/Data/Scripts/010_Data/002_PBS data/011_Ribbon.rb index e2344d217..d153c9cdc 100644 --- a/Data/Scripts/010_Data/002_PBS data/011_Ribbon.rb +++ b/Data/Scripts/010_Data/002_PBS data/011_Ribbon.rb @@ -10,10 +10,11 @@ module GameData DATA_FILENAME = "ribbons.dat" SCHEMA = { - "Name" => [:name, "s"], - "IconPosition" => [:icon_position, "u"], - "Description" => [:description, "q"], - "Flags" => [:flags, "*s"] + "SectionName" => [:id, "m"], + "Name" => [:real_name, "s"], + "IconPosition" => [:icon_position, "u"], + "Description" => [:real_description, "q"], + "Flags" => [:flags, "*s"] } extend ClassMethodsSymbols @@ -21,10 +22,10 @@ module GameData def initialize(hash) @id = hash[:id] - @real_name = hash[:name] || "Unnamed" - @icon_position = hash[:icon_position] || 0 - @real_description = hash[:description] || "???" - @flags = hash[:flags] || [] + @real_name = hash[:real_name] || "Unnamed" + @icon_position = hash[:icon_position] || 0 + @real_description = hash[:real_description] || "???" + @flags = hash[:flags] || [] end # @return [String] the translated name of this ribbon diff --git a/Data/Scripts/010_Data/002_PBS data/013_TrainerType.rb b/Data/Scripts/010_Data/002_PBS data/013_TrainerType.rb index 57991e127..1fec8d4e8 100644 --- a/Data/Scripts/010_Data/002_PBS data/013_TrainerType.rb +++ b/Data/Scripts/010_Data/002_PBS data/013_TrainerType.rb @@ -14,17 +14,18 @@ module GameData DATA_FILENAME = "trainer_types.dat" SCHEMA = { - "Name" => [:name, "s"], - "Gender" => [:gender, "e", { "Male" => 0, "male" => 0, "M" => 0, "m" => 0, "0" => 0, - "Female" => 1, "female" => 1, "F" => 1, "f" => 1, "1" => 1, - "Unknown" => 2, "unknown" => 2, "Other" => 2, "other" => 2, - "Mixed" => 2, "mixed" => 2, "X" => 2, "x" => 2, "2" => 2 }], - "BaseMoney" => [:base_money, "u"], - "SkillLevel" => [:skill_level, "u"], - "Flags" => [:flags, "*s"], - "IntroBGM" => [:intro_BGM, "s"], - "BattleBGM" => [:battle_BGM, "s"], - "VictoryBGM" => [:victory_BGM, "s"] + "SectionName" => [:id, "m"], + "Name" => [:real_name, "s"], + "Gender" => [:gender, "e", { "Male" => 0, "male" => 0, "M" => 0, "m" => 0, "0" => 0, + "Female" => 1, "female" => 1, "F" => 1, "f" => 1, "1" => 1, + "Unknown" => 2, "unknown" => 2, "Other" => 2, "other" => 2, + "Mixed" => 2, "mixed" => 2, "X" => 2, "x" => 2, "2" => 2 }], + "BaseMoney" => [:base_money, "u"], + "SkillLevel" => [:skill_level, "u"], + "Flags" => [:flags, "*s"], + "IntroBGM" => [:intro_BGM, "s"], + "BattleBGM" => [:battle_BGM, "s"], + "VictoryBGM" => [:victory_BGM, "s"] } extend ClassMethodsSymbols @@ -81,7 +82,7 @@ module GameData def initialize(hash) @id = hash[:id] - @real_name = hash[:name] || "Unnamed" + @real_name = hash[:real_name] || "Unnamed" @gender = hash[:gender] || 2 @base_money = hash[:base_money] || 30 @skill_level = hash[:skill_level] || @base_money diff --git a/Data/Scripts/010_Data/002_PBS data/015_Metadata.rb b/Data/Scripts/010_Data/002_PBS data/015_Metadata.rb index dae4443f4..a2869b1b2 100644 --- a/Data/Scripts/010_Data/002_PBS data/015_Metadata.rb +++ b/Data/Scripts/010_Data/002_PBS data/015_Metadata.rb @@ -17,17 +17,18 @@ module GameData DATA_FILENAME = "metadata.dat" SCHEMA = { - "StartMoney" => [1, "u"], - "StartItemStorage" => [2, "*e", :Item], - "Home" => [3, "vuuu"], - "StorageCreator" => [4, "s"], - "WildBattleBGM" => [5, "s"], - "TrainerBattleBGM" => [6, "s"], - "WildVictoryBGM" => [7, "s"], - "TrainerVictoryBGM" => [8, "s"], - "WildCaptureME" => [9, "s"], - "SurfBGM" => [10, "s"], - "BicycleBGM" => [11, "s"] + "SectionName" => [:id, "u"], + "StartMoney" => [:start_money, "u"], + "StartItemStorage" => [:start_item_storage, "*e", :Item], + "Home" => [:home, "vuuu"], + "StorageCreator" => [:real_storage_creator, "s"], + "WildBattleBGM" => [:wild_battle_BGM, "s"], + "TrainerBattleBGM" => [:trainer_battle_BGM, "s"], + "WildVictoryBGM" => [:wild_victory_BGM, "s"], + "TrainerVictoryBGM" => [:trainer_victory_BGM, "s"], + "WildCaptureME" => [:wild_capture_ME, "s"], + "SurfBGM" => [:surf_BGM, "s"], + "BicycleBGM" => [:bicycle_BGM, "s"] } extend ClassMethodsIDNumbers @@ -55,10 +56,10 @@ module GameData def initialize(hash) @id = hash[:id] - @start_money = hash[:start_money] || 3000 + @start_money = hash[:start_money] || 3000 @start_item_storage = hash[:start_item_storage] || [] @home = hash[:home] - @real_storage_creator = hash[:storage_creator] + @real_storage_creator = hash[:real_storage_creator] @wild_battle_BGM = hash[:wild_battle_BGM] @trainer_battle_BGM = hash[:trainer_battle_BGM] @wild_victory_BGM = hash[:wild_victory_BGM] diff --git a/Data/Scripts/010_Data/002_PBS data/016_PlayerMetadata.rb b/Data/Scripts/010_Data/002_PBS data/016_PlayerMetadata.rb index 18c98a8cb..a050296e8 100644 --- a/Data/Scripts/010_Data/002_PBS data/016_PlayerMetadata.rb +++ b/Data/Scripts/010_Data/002_PBS data/016_PlayerMetadata.rb @@ -9,15 +9,16 @@ module GameData DATA_FILENAME = "player_metadata.dat" SCHEMA = { - "TrainerType" => [1, "e", :TrainerType], - "WalkCharset" => [2, "s"], - "RunCharset" => [3, "s"], - "CycleCharset" => [4, "s"], - "SurfCharset" => [5, "s"], - "DiveCharset" => [6, "s"], - "FishCharset" => [7, "s"], - "SurfFishCharset" => [8, "s"], - "Home" => [9, "vuuu"] + "SectionName" => [:id, "u"], + "TrainerType" => [:trainer_type, "e", :TrainerType], + "WalkCharset" => [:walk_charset, "s"], + "RunCharset" => [:run_charset, "s"], + "CycleCharset" => [:cycle_charset, "s"], + "SurfCharset" => [:surf_charset, "s"], + "DiveCharset" => [:dive_charset, "s"], + "FishCharset" => [:fish_charset, "s"], + "SurfFishCharset" => [:surf_fish_charset, "s"], + "Home" => [:home, "vuuu"] } extend ClassMethodsIDNumbers diff --git a/Data/Scripts/010_Data/002_PBS data/017_MapMetadata.rb b/Data/Scripts/010_Data/002_PBS data/017_MapMetadata.rb index e485c86fc..bf6f9397d 100644 --- a/Data/Scripts/010_Data/002_PBS data/017_MapMetadata.rb +++ b/Data/Scripts/010_Data/002_PBS data/017_MapMetadata.rb @@ -28,28 +28,29 @@ module GameData DATA_FILENAME = "map_metadata.dat" SCHEMA = { - "Name" => [1, "s"], - "Outdoor" => [2, "b"], - "ShowArea" => [3, "b"], - "Bicycle" => [4, "b"], - "BicycleAlways" => [5, "b"], - "HealingSpot" => [6, "vuu"], - "Weather" => [7, "eu", :Weather], - "MapPosition" => [8, "uuu"], - "DiveMap" => [9, "v"], - "DarkMap" => [10, "b"], - "SafariMap" => [11, "b"], - "SnapEdges" => [12, "b"], - "Dungeon" => [13, "b"], - "BattleBack" => [14, "s"], - "WildBattleBGM" => [15, "s"], - "TrainerBattleBGM" => [16, "s"], - "WildVictoryBGM" => [17, "s"], - "TrainerVictoryBGM" => [18, "s"], - "WildCaptureME" => [19, "s"], - "MapSize" => [20, "us"], - "Environment" => [21, "e", :Environment], - "Flags" => [22, "*s"] + "SectionName" => [:id, "u"], + "Name" => [:real_name, "s"], + "Outdoor" => [:outdoor_map, "b"], + "ShowArea" => [:announce_location, "b"], + "Bicycle" => [:can_bicycle, "b"], + "BicycleAlways" => [:always_bicycle, "b"], + "HealingSpot" => [:teleport_destination, "vuu"], + "Weather" => [:weather, "eu", :Weather], + "MapPosition" => [:town_map_position, "uuu"], + "DiveMap" => [:dive_map_id, "v"], + "DarkMap" => [:dark_map, "b"], + "SafariMap" => [:safari_map, "b"], + "SnapEdges" => [:snap_edges, "b"], + "Dungeon" => [:random_dungeon, "b"], + "BattleBack" => [:battle_background, "s"], + "WildBattleBGM" => [:wild_battle_BGM, "s"], + "TrainerBattleBGM" => [:trainer_battle_BGM, "s"], + "WildVictoryBGM" => [:wild_victory_BGM, "s"], + "TrainerVictoryBGM" => [:trainer_victory_BGM, "s"], + "WildCaptureME" => [:wild_capture_ME, "s"], + "MapSize" => [:town_map_size, "us"], + "Environment" => [:battle_environment, "e", :Environment], + "Flags" => [:flags, "*s"] } extend ClassMethodsIDNumbers @@ -84,7 +85,7 @@ module GameData def initialize(hash) @id = hash[:id] - @real_name = hash[:name] + @real_name = hash[:real_name] @outdoor_map = hash[:outdoor_map] @announce_location = hash[:announce_location] @can_bicycle = hash[:can_bicycle] diff --git a/Data/Scripts/010_Data/002_PBS data/018_DungeonTileset.rb b/Data/Scripts/010_Data/002_PBS data/018_DungeonTileset.rb index d9b10f8fd..b7c03e68b 100644 --- a/Data/Scripts/010_Data/002_PBS data/018_DungeonTileset.rb +++ b/Data/Scripts/010_Data/002_PBS data/018_DungeonTileset.rb @@ -15,8 +15,8 @@ module GameData DATA_FILENAME = "dungeon_tilesets.dat" SCHEMA = { - "Autotile" => [:autotile, "us"], - "Tile" => [:tile, "us"], + "Autotile" => [:autotile, "um"], + "Tile" => [:tile, "um"], "SnapToLargeGrid" => [:snap_to_large_grid, "b"], "LargeVoidTiles" => [:large_void_tiles, "b"], "LargeWallTiles" => [:large_wall_tiles, "b"], @@ -56,7 +56,7 @@ module GameData [hash[:autotile], hash[:tile]].each_with_index do |array, i| array.each do |tile_info| next if !tile_info - tile_type = tile_info[1].downcase.to_sym + tile_type = tile_info[1] if tile_type == :walls if @double_walls if @large_wall_tiles diff --git a/Data/Scripts/010_Data/002_PBS data/020_DungeonParameters.rb b/Data/Scripts/010_Data/002_PBS data/020_DungeonParameters.rb index 9c0b0281f..44285c04f 100644 --- a/Data/Scripts/010_Data/002_PBS data/020_DungeonParameters.rb +++ b/Data/Scripts/010_Data/002_PBS data/020_DungeonParameters.rb @@ -30,12 +30,13 @@ module GameData DATA_FILENAME = "dungeon_parameters.dat" SCHEMA = { + "SectionName" => [:id, "mV"], "DungeonSize" => [:dungeon_size, "vv"], "CellSize" => [:cell_size, "vv"], "MinRoomSize" => [:min_room_size, "vv"], "MaxRoomSize" => [:max_room_size, "vv"], "CorridorWidth" => [:corridor_width, "v"], - "ShiftCorridors" => [:shift_corridors, "b"], + "ShiftCorridors" => [:random_corridor_shift, "b"], "NodeLayout" => [:node_layout, "s"], "RoomLayout" => [:room_layout, "s"], "RoomChance" => [:room_chance, "v"], @@ -76,7 +77,7 @@ module GameData @room_max_width = (hash[:max_room_size]) ? hash[:max_room_size][0] : @cell_width - 1 @room_max_height = (hash[:max_room_size]) ? hash[:max_room_size][1] : @cell_height - 1 @corridor_width = hash[:corridor_width] || 2 - @random_corridor_shift = hash[:shift_corridors] + @random_corridor_shift = hash[:random_corridor_shift] @node_layout = hash[:node_layout]&.downcase&.to_sym || :full @room_layout = hash[:room_layout]&.downcase&.to_sym || :full @room_chance = hash[:room_chance] || 70 diff --git a/Data/Scripts/021_Compiler/001_Compiler.rb b/Data/Scripts/021_Compiler/001_Compiler.rb index ab2e6edc3..d0c1c4c3f 100644 --- a/Data/Scripts/021_Compiler/001_Compiler.rb +++ b/Data/Scripts/021_Compiler/001_Compiler.rb @@ -129,7 +129,10 @@ module Compiler yield lastsection, sectionname if havesection end - # Used for types.txt, pokemon.txt, battle_facility_lists.txt and Battle Tower trainers PBS files + # Used for types.txt, abilities.txt, moves.txt, items.txt, berry_plants.txt, + # pokemon.txt, pokemon_forms.txt, pokemon_metrics.txt, shadow_pokemon.txt, + # ribbons.txt, trainer_types.txt, battle_facility_lists.txt, Battle Tower + # trainers PBS files and dungeon_parameters.txt def pbEachFileSection(f) pbEachFileSectionEx(f) { |section, name| yield section, name if block_given? && name[/^.+$/] @@ -143,14 +146,7 @@ module Compiler } end - # Used for pokemon_forms.txt - def pbEachFileSectionPokemonForms(f) - pbEachFileSectionEx(f) { |section, name| - yield section, name if block_given? && name[/^\w+[-,\s]{1}\d+$/] - } - end - - # Used for phone.txt + # Unused def pbEachSection(f) lineno = 1 havesection = false @@ -193,7 +189,7 @@ module Compiler } end - # Used for many PBS files + # Used for town_map.txt and Battle Tower Pokémon PBS files def pbCompilerEachCommentedLine(filename) File.open(filename, "rb") { |f| FileLineData.file = filename @@ -226,7 +222,8 @@ module Compiler } end - # Used for map_connections.txt, abilities.txt, moves.txt, regional_dexes.txt + # Used for map_connections.txt, phone.txt, regional_dexes.txt, encounters.txt, + # trainers.txt and dungeon_tilesets.txt def pbCompilerEachPreppedLine(filename) File.open(filename, "rb") { |f| FileLineData.file = filename @@ -519,6 +516,21 @@ module Compiler subrecord.push(rec) rec = "" end + when "m" # Symbol + field = csvfield!(rec) + if !field[/^(?![0-9])\w+$/] + raise _INTL("Field '{1}' must contain only letters, digits, and\r\nunderscores and can't begin with a number.\r\n{2}", field, FileLineData.linereport) + end + subrecord.push(field.to_sym) + when "M" # Optional symbol + field = csvfield!(rec) + if nil_or_empty?(field) + subrecord.push(nil) + elsif !field[/^(?![0-9])\w+$/] + raise _INTL("Field '{1}' must contain only letters, digits, and\r\nunderscores and can't begin with a number.\r\n{2}", field, FileLineData.linereport) + else + subrecord.push(field.to_sym) + end when "e" # Enumerable subrecord.push(csvEnumField!(rec, schema[2 + i - start], "", FileLineData.linereport)) when "E" # Optional enumerable @@ -548,7 +560,7 @@ module Compiler break if repeat && nil_or_empty?(rec) break unless repeat end - return (schema[1].length == 1) ? record[0] : record + return (!repeat && schema[1].length == 1) ? record[0] : record end #============================================================================= diff --git a/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb b/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb index 92c24bf0e..a8616ba76 100644 --- a/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb +++ b/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb @@ -174,7 +174,6 @@ module Compiler def compile_types(path = "PBS/types.txt") compile_pbs_file_message_start(path) GameData::Type::DATA.clear - type_names = [] # Read from PBS file File.open(path, "rb") { |f| FileLineData.file = path # For error reporting @@ -182,49 +181,52 @@ module Compiler # 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). schema = GameData::Type::SCHEMA - pbEachFileSection(f) { |contents, type_id| - contents["InternalName"] = type_id if !type_id[/^\d+/] - icon_pos = (type_id[/^\d+/]) ? type_id.to_i : nil + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - FileLineData.setSection(type_id, key, contents[key]) # For error reporting - # Skip empty properties, or raise an error if a required property is - # empty - if contents[key].nil? - if ["Name", "InternalName"].include?(key) - raise _INTL("The entry {1} is required in {2} section {3}.", key, path, type_id) - end + FileLineData.setSection(section_name, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name, key, schema[key]) next end + # Skip empty properties + next if contents[key].nil? # Compile value for key value = pbGetCsvRecord(contents[key], key, schema[key]) value = nil if value.is_a?(Array) && value.empty? - contents[key] = value - # Ensure weaknesses/resistances/immunities are in arrays and are symbols - if value && ["Weaknesses", "Resistances", "Immunities"].include?(key) - contents[key].map! { |x| x.to_sym } - contents[key].uniq! - end + data_hash[schema[key][0]] = value + end + # Validate and modify the compiled data + validate_compiled_type(data_hash) + if GameData::Type.exists?(data_hash[:id]) + raise _INTL("Type ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Construct type hash - type_hash = { - :id => contents["InternalName"].to_sym, - :name => contents["Name"], - :pseudo_type => contents["IsPseudoType"], - :special_type => contents["IsSpecialType"], - :flags => contents["Flags"], - :weaknesses => contents["Weaknesses"], - :resistances => contents["Resistances"], - :immunities => contents["Immunities"], - :icon_position => contents["IconPosition"] || icon_pos - } # Add type's data to records - GameData::Type.register(type_hash) - type_names.push(type_hash[:name]) + GameData::Type.register(data_hash) } } - # Ensure all weaknesses/resistances/immunities are valid types + validate_all_compiled_types + # Save all data + GameData::Type.save + process_pbs_file_message_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 ({2}, section {3}, Weaknesses).", other_type.to_s, path, type.id) @@ -237,11 +239,10 @@ module Compiler next if GameData::Type.exists?(other_type) raise _INTL("'{1}' is not a defined type ({2}, section {3}, Immunities).", other_type.to_s, path, type.id) end + # Get type names for translating + type_names.push(type.real_name) end - # Save all data - GameData::Type.save MessageTypes.setMessagesAsHash(MessageTypes::Types, type_names) - process_pbs_file_message_end end #============================================================================= @@ -250,69 +251,61 @@ module Compiler def compile_abilities(path = "PBS/abilities.txt") compile_pbs_file_message_start(path) GameData::Ability::DATA.clear - schema = GameData::Ability::SCHEMA - ability_names = [] - ability_descriptions = [] - ability_hash = nil - pbCompilerEachPreppedLine(path) { |line, line_no| - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [ability_id] - # Add previous ability's data to records - GameData::Ability.register(ability_hash) if ability_hash - # Parse ability ID - ability_id = $~[1].to_sym - if GameData::Ability.exists?(ability_id) - raise _INTL("Ability ID '{1}' is used twice.\r\n{2}", ability_id, FileLineData.linereport) + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::Ability::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next + end + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct ability hash - ability_hash = { - :id => ability_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - if !ability_hash - raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) + # Validate and modify the compiled data + validate_compiled_ability(data_hash) + if GameData::Ability.exists?(data_hash[:id]) + raise _INTL("Ability ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - ability_hash[line_schema[0]] = property_value - case property_name - when "Name" - ability_names.push(ability_hash[:name]) - when "Description" - ability_descriptions.push(ability_hash[:description]) - end - else # Old format - # Add previous ability's data to records - GameData::Ability.register(ability_hash) if ability_hash - # Parse ability - line = pbGetCsvRecord(line, line_no, [0, "snss"]) - ability_id = line[1].to_sym - if GameData::Ability::DATA[ability_id] - raise _INTL("Ability ID '{1}' is used twice.\r\n{2}", ability_id, FileLineData.linereport) - end - # Construct ability hash - ability_hash = { - :id => ability_id, - :name => line[2], - :description => line[3] - } # Add ability's data to records - GameData::Ability.register(ability_hash) - ability_names.push(ability_hash[:name]) - ability_descriptions.push(ability_hash[:description]) - ability_hash = nil - end + GameData::Ability.register(data_hash) + } } - # Add last ability's data to records - GameData::Ability.register(ability_hash) if ability_hash + validate_all_compiled_abilities # Save all data GameData::Ability.save + process_pbs_file_message_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::Abilities, ability_names) MessageTypes.setMessagesAsHash(MessageTypes::AbilityDescs, ability_descriptions) - process_pbs_file_message_end end #============================================================================= @@ -321,137 +314,69 @@ module Compiler def compile_moves(path = "PBS/moves.txt") compile_pbs_file_message_start(path) GameData::Move::DATA.clear - schema = GameData::Move::SCHEMA - move_names = [] - move_descriptions = [] - move_hash = nil - # Read each line of moves.txt at a time and compile it into an move - idx = 0 - pbCompilerEachPreppedLine(path) { |line, line_no| - echo "." if idx % 500 == 0 - idx += 1 - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [move_id] - # Add previous move's data to records - if move_hash - # Sanitise data - if (move_hash[:category] || 2) == 2 && (move_hash[:base_damage] || 0) != 0 - raise _INTL("Move {1} is defined as a Status move with a non-zero base damage.\r\n{2}", - move_hash[:name], FileLineData.linereport) - elsif (move_hash[:category] || 2) != 2 && (move_hash[:base_damage] || 0) == 0 - print _INTL("Warning: Move {1} was defined as Physical or Special but had a base damage of 0. Changing it to a Status move.\r\n{2}", - move_hash[:name], FileLineData.linereport) - move_hash[:category] = 2 + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::Move::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next end - GameData::Move.register(move_hash) + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Parse move ID - move_id = $~[1].to_sym - if GameData::Move.exists?(move_id) - raise _INTL("Move ID '{1}' is used twice.\r\n{2}", move_id, FileLineData.linereport) + # Validate and modify the compiled data + validate_compiled_move(data_hash) + if GameData::Move.exists?(data_hash[:id]) + raise _INTL("Move ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Construct move hash - move_hash = { - :id => move_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - if !move_hash - raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) - end - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - move_hash[line_schema[0]] = property_value - case property_name - when "Name" - move_names.push(move_hash[:name]) - when "Description" - move_descriptions.push(move_hash[:description]) - end - else # Old format - # Add previous move's data to records - if move_hash - # Sanitise data - if (move_hash[:category] || 2) == 2 && (move_hash[:base_damage] || 0) != 0 - raise _INTL("Move {1} is defined as a Status move with a non-zero base damage.\r\n{2}", - move_hash[:name], FileLineData.linereport) - elsif (move_hash[:category] || 2) != 2 && (move_hash[:base_damage] || 0) == 0 - print _INTL("Warning: Move {1} was defined as Physical or Special but had a base damage of 0. Changing it to a Status move.\r\n{2}", - move_hash[:name], FileLineData.linereport) - move_hash[:category] = 2 - end - GameData::Move.register(move_hash) - end - # Parse move - line = pbGetCsvRecord(line, line_no, - [0, "snssueeuuueiss", - nil, nil, nil, nil, nil, :Type, ["Physical", "Special", "Status"], - nil, nil, nil, :Target, nil, nil, nil]) - move_id = line[1].to_sym - if GameData::Move::DATA[move_id] - raise _INTL("Move ID '{1}' is used twice.\r\n{2}", move_id, FileLineData.linereport) - end - # Sanitise data - if line[6] == 2 && line[4] != 0 - raise _INTL("Move {1} is defined as a Status move with a non-zero base damage.\r\n{2}", line[2], FileLineData.linereport) - elsif line[6] != 2 && line[4] == 0 - print _INTL("Warning: Move {1} was defined as Physical or Special but had a base damage of 0. Changing it to a Status move.\r\n{2}", line[2], FileLineData.linereport) - line[6] = 2 - end - flags = [] - flags.push("Contact") if line[12][/a/] - flags.push("CanProtect") if line[12][/b/] - flags.push("CanMirrorMove") if line[12][/e/] - flags.push("ThawsUser") if line[12][/g/] - flags.push("HighCriticalHitRate") if line[12][/h/] - flags.push("Biting") if line[12][/i/] - flags.push("Punching") if line[12][/j/] - flags.push("Sound") if line[12][/k/] - flags.push("Powder") if line[12][/l/] - flags.push("Pulse") if line[12][/m/] - flags.push("Bomb") if line[12][/n/] - flags.push("Dance") if line[12][/o/] - # Construct move hash - move_hash = { - :id => move_id, - :name => line[2], - :function_code => line[3], - :base_damage => line[4], - :type => line[5], - :category => line[6], - :accuracy => line[7], - :total_pp => line[8], - :effect_chance => line[9], - :target => line[10], - :priority => line[11], - :flags => flags, - :description => line[13] - } # Add move's data to records - GameData::Move.register(move_hash) - move_names.push(move_hash[:name]) - move_descriptions.push(move_hash[:description]) - move_hash = nil - end + GameData::Move.register(data_hash) + } } - # Add last move's data to records - if move_hash - # Sanitise data - if (move_hash[:category] || 2) == 2 && (move_hash[:base_damage] || 0) != 0 - raise _INTL("Move {1} is defined as a Status move with a non-zero base damage.\r\n{2}", line[2], FileLineData.linereport) - elsif (move_hash[:category] || 2) != 2 && (move_hash[:base_damage] || 0) == 0 - print _INTL("Warning: Move {1} was defined as Physical or Special but had a base damage of 0. Changing it to a Status move.\r\n{2}", line[2], FileLineData.linereport) - move_hash[:category] = 2 - end - GameData::Move.register(move_hash) - end + validate_all_compiled_moves # Save all data GameData::Move.save + process_pbs_file_message_end + end + + def validate_compiled_move(hash) + if (hash[:category] || 2) == 2 && (hash[:base_damage] || 0) != 0 + raise _INTL("Move {1} is defined as a Status move with a non-zero base damage.\r\n{2}", + hash[:name], FileLineData.linereport) + elsif (hash[:category] || 2) != 2 && (hash[:base_damage] || 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.\r\n{2}", + hash[:name], 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::Moves, move_names) MessageTypes.setMessagesAsHash(MessageTypes::MoveDescriptions, move_descriptions) - process_pbs_file_message_end end #============================================================================= @@ -460,94 +385,64 @@ module Compiler def compile_items(path = "PBS/items.txt") compile_pbs_file_message_start(path) GameData::Item::DATA.clear - schema = GameData::Item::SCHEMA - item_names = [] - item_names_plural = [] - item_descriptions = [] - item_hash = nil - # Read each line of items.txt at a time and compile it into an item - idx = 0 - pbCompilerEachPreppedLine(path) { |line, line_no| - echo "." if idx % 250 == 0 - idx += 1 - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [item_id] - # Add previous item's data to records - GameData::Item.register(item_hash) if item_hash - # Parse item ID - item_id = $~[1].to_sym - if GameData::Item.exists?(item_id) - raise _INTL("Item ID '{1}' is used twice.\r\n{2}", item_id, FileLineData.linereport) + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::Item::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next + end + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct item hash - item_hash = { - :id => item_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - if !item_hash - raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) + # Validate and modify the compiled data + validate_compiled_item(data_hash) + if GameData::Item.exists?(data_hash[:id]) + raise _INTL("Item ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - item_hash[line_schema[0]] = property_value - case property_name - when "Name" - item_names.push(item_hash[:name]) - when "NamePlural" - item_names_plural.push(item_hash[:name_plural]) - when "Description" - item_descriptions.push(item_hash[:description]) - end - else # Old format - # Add previous item's data to records - GameData::Item.register(item_hash) if item_hash - # Parse item - line = pbGetCsvRecord(line, line_no, - [0, "snssvusuuUE", nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, :Move]) - item_id = line[1].to_sym - if GameData::Item.exists?(item_id) - raise _INTL("Item ID '{1}' is used twice.\r\n{2}", item_id, FileLineData.linereport) - end - consumable = !([3, 4, 5].include?(line[7]) || line[8] >= 6) - line[7] = 1 if line[7] == 5 - line[7] = 5 if line[7] == 6 - line[8] -= 5 if line[8] > 5 - flags = [] - flags.push(line[9]) if !nil_or_empty?(line[9]) - # Construct item hash - item_hash = { - :id => item_id, - :name => line[2], - :name_plural => line[3], - :pocket => line[4], - :price => line[5], - :description => line[6], - :field_use => line[7], - :battle_use => line[8], - :consumable => consumable, - :flags => flags, - :move => line[10] - } # Add item's data to records - GameData::Item.register(item_hash) - item_names.push(item_hash[:name]) - item_names_plural.push(item_hash[:name_plural]) - item_descriptions.push(item_hash[:description]) - item_hash = nil - end + GameData::Item.register(data_hash) + } } - # Add last item's data to records - GameData::Item.register(item_hash) if item_hash + validate_all_compiled_items # Save all data GameData::Item.save + process_pbs_file_message_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_descriptions = [] + GameData::Item.each do |item| + item_names.push(item.real_name) + item_names_plural.push(item.real_name_plural) + item_descriptions.push(item.real_description) + end MessageTypes.setMessagesAsHash(MessageTypes::Items, item_names) MessageTypes.setMessagesAsHash(MessageTypes::ItemPlurals, item_names_plural) MessageTypes.setMessagesAsHash(MessageTypes::ItemDescriptions, item_descriptions) - process_pbs_file_message_end end #============================================================================= @@ -556,77 +451,60 @@ module Compiler def compile_berry_plants(path = "PBS/berry_plants.txt") compile_pbs_file_message_start(path) GameData::BerryPlant::DATA.clear - schema = GameData::BerryPlant::SCHEMA - item_hash = nil - old_format = nil - # Read each line of berry_plants.txt at a time and compile it into a berry plant - idx = 0 - pbCompilerEachPreppedLine(path) { |line, line_no| - echo "." if idx % 250 == 0 - idx += 1 - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [item_id] - old_format = false if old_format.nil? - if old_format - raise _INTL("Can't mix old and new formats.\r\n{1}", FileLineData.linereport) - end - # Add previous berry plant's data to records - GameData::BerryPlant.register(item_hash) if item_hash - # Parse item ID - item_id = $~[1].to_sym - if GameData::BerryPlant.exists?(item_id) - raise _INTL("Item ID '{1}' is used twice.\r\n{2}", item_id, FileLineData.linereport) - end - # Construct item hash - item_hash = { - :id => item_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - old_format = true if old_format.nil? - if old_format - key = $1 - value = $2 - item_id = parseItem(key) - line = pbGetCsvRecord(value, line_no, [0, "vuuv"]) - # Construct berry plant hash - berry_plant_hash = { - :id => item_id, - :hours_per_stage => line[0], - :drying_per_hour => line[1], - :yield => [line[2], line[3]] - } - # Add berry plant's data to records - GameData::BerryPlant.register(berry_plant_hash) - else - if !item_hash - raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::BerryPlant::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next end - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - item_hash[line_schema[0]] = property_value + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], 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_berry_plant(data_hash) + if GameData::BerryPlant.exists?(data_hash[:id]) + raise _INTL("Berry plant ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) + end + # Add berry plant's data to records + GameData::BerryPlant.register(data_hash) + } } - # Add last berry plant's data to records - GameData::BerryPlant.register(item_hash) if item_hash + validate_all_compiled_berry_plants # Save all data GameData::BerryPlant.save process_pbs_file_message_end end + def validate_compiled_berry_plant(hash) + end + + def validate_all_compiled_berry_plants + end + #============================================================================= # Compile Pokémon data #============================================================================= def compile_pokemon(path = "PBS/pokemon.txt") compile_pbs_file_message_start(path) GameData::Species::DATA.clear - species_names = [] - species_form_names = [] - species_categories = [] - species_pokedex_entries = [] # Read from PBS file File.open(path, "rb") { |f| FileLineData.file = path # For error reporting @@ -635,131 +513,71 @@ module Compiler # the keys are the XXX and the values are the YYY (as unprocessed strings). schema = GameData::Species.schema idx = 0 - pbEachFileSection(f) { |contents, species_id| + pbEachFileSection(f) { |contents, section_name| echo "." if idx % 50 == 0 - idx += 1 Graphics.update if idx % 250 == 0 - FileLineData.setSection(species_id, "header", nil) # For error reporting - contents["InternalName"] = species_id if !species_id[/^\d+/] - # Ensure all required properties have been defined, and raise an error - # if not - schema.each_key do |key| - next if !nil_or_empty?(contents[key]) - if ["Name", "InternalName"].include?(key) - raise _INTL("The entry {1} is required in {2} section {3}.", key, path, species_id) - end - contents[key] = nil - end - # Raise an error if a species ID is used twice - if GameData::Species::DATA[contents["InternalName"].to_sym] - raise _INTL("Species ID '{1}' is used twice.\r\n{2}", contents["InternalName"], FileLineData.linereport) - end + idx += 1 + data_hash = {:id => section_name.to_sym} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - next if nil_or_empty?(contents[key]) - FileLineData.setSection(species_id, key, contents[key]) # For error reporting + FileLineData.setSection(section_name, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name, key, schema[key]) + next + end + # Skip empty properties + next if contents[key].nil? # Compile value for key - if ["EVs", "EffortPoints"].include?(key) && contents[key].split(",")[0].numeric? - value = pbGetCsvRecord(contents[key], key, [0, "uuuuuu"]) # Old format - else - value = pbGetCsvRecord(contents[key], key, schema[key]) - end + value = pbGetCsvRecord(contents[key], key, schema[key]) value = nil if value.is_a?(Array) && value.empty? - contents[key] = value - # Sanitise data - case key - when "BaseStats" - value_hash = {} - GameData::Stat.each_main do |s| - value_hash[s.id] = value[s.pbs_order] if s.pbs_order >= 0 - end - contents[key] = value_hash - when "EVs", "EffortPoints" - if value[0].is_a?(Array) # New format - value_hash = {} - value.each { |val| value_hash[val[0]] = val[1] } - GameData::Stat.each_main { |s| value_hash[s.id] ||= 0 } - contents[key] = value_hash - else # Old format - value_hash = {} - GameData::Stat.each_main do |s| - value_hash[s.id] = value[s.pbs_order] if s.pbs_order >= 0 - end - contents[key] = value_hash - end - when "Height", "Weight" - # Convert height/weight to 1 decimal place and multiply by 10 - value = (value * 10).round - if value <= 0 - raise _INTL("Value for '{1}' can't be less than or close to 0 (section {2}, {3})", key, species_id, path) - end - contents[key] = value - when "Evolutions" - contents[key].each { |evo| evo[3] = false } - end + data_hash[schema[key][0]] = value end - # Construct species hash - types = contents["Types"] || [contents["Type1"], contents["Type2"]] - types = [types] if !types.is_a?(Array) - types = types.uniq.compact - species_hash = { - :id => contents["InternalName"].to_sym, - :name => contents["Name"], - :form_name => contents["FormName"], - :category => contents["Category"] || contents["Kind"], - :pokedex_entry => contents["Pokedex"], - :types => types, - :base_stats => contents["BaseStats"], - :evs => contents["EVs"] || contents["EffortPoints"], - :base_exp => contents["BaseExp"] || contents["BaseEXP"], - :growth_rate => contents["GrowthRate"], - :gender_ratio => contents["GenderRatio"] || contents["GenderRate"], - :catch_rate => contents["CatchRate"] || contents["Rareness"], - :happiness => contents["Happiness"], - :moves => contents["Moves"], - :tutor_moves => contents["TutorMoves"], - :egg_moves => contents["EggMoves"], - :abilities => contents["Abilities"], - :hidden_abilities => contents["HiddenAbilities"] || contents["HiddenAbility"], - :wild_item_common => contents["WildItemCommon"], - :wild_item_uncommon => contents["WildItemUncommon"], - :wild_item_rare => contents["WildItemRare"], - :egg_groups => contents["EggGroups"] || contents["Compatibility"], - :hatch_steps => contents["HatchSteps"] || contents["StepsToHatch"], - :incense => contents["Incense"], - :offspring => contents["Offspring"], - :evolutions => contents["Evolutions"], - :height => contents["Height"], - :weight => contents["Weight"], - :color => contents["Color"], - :shape => contents["Shape"], - :habitat => contents["Habitat"], - :generation => contents["Generation"], - :flags => contents["Flags"] - } - # Add species' data to records - GameData::Species.register(species_hash) - species_names.push(species_hash[:name]) - species_form_names.push(species_hash[:form_name]) - species_categories.push(species_hash[:category]) - species_pokedex_entries.push(species_hash[:pokedex_entry]) - # Save metrics data if defined (backwards compatibility) - if contents["BattlerPlayerX"] || contents["BattlerPlayerY"] || - contents["BattlerEnemyX"] || contents["BattlerEnemyY"] || - contents["BattlerAltitude"] || contents["BattlerShadowX"] || - contents["BattlerShadowSize"] - metrics_hash = { - :id => contents["InternalName"].to_sym, - :back_sprite => [contents["BattlerPlayerX"] || 0, contents["BattlerPlayerY"] || 0], - :front_sprite => [contents["BattlerEnemyX"] || 0, contents["BattlerEnemyY"] || 0], - :front_sprite_altitude => contents["BattlerAltitude"] || 0, - :shadow_x => contents["BattlerShadowX"] || 0, - :shadow_size => contents["BattlerShadowSize"] || 2 - } - GameData::SpeciesMetrics.register(metrics_hash) + # Validate and modify the compiled data + validate_compiled_pokemon(data_hash) + if GameData::Species.exists?(data_hash[:id]) + raise _INTL("Species ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end + # Add species's data to records + GameData::Species.register(data_hash) } } + validate_all_compiled_pokemon + # Save all data + GameData::Species.save + process_pbs_file_message_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] + # Record all evolutions as not being prevolutions + if hash[:evolutions].is_a?(Array) + hash[:evolutions].each { |evo| evo[3] = false } + end + # Remove duplicate types + hash[:types].uniq! if hash[:types].is_a?(Array) + 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 @@ -793,14 +611,21 @@ module Compiler GameData::Species.each do |species| # Distribute prevolutions species.evolutions.push(all_evos[species.species].clone) if all_evos[species.species] end - # Save all data - GameData::Species.save - GameData::SpeciesMetrics.save + # 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, species_names) MessageTypes.setMessagesAsHash(MessageTypes::FormNames, species_form_names) MessageTypes.setMessagesAsHash(MessageTypes::Kinds, species_categories) MessageTypes.setMessagesAsHash(MessageTypes::Entries, species_pokedex_entries) - process_pbs_file_message_end end #============================================================================= @@ -808,11 +633,6 @@ module Compiler #============================================================================= def compile_pokemon_forms(path = "PBS/pokemon_forms.txt") compile_pbs_file_message_start(path) - species_names = [] - species_form_names = [] - species_categories = [] - species_pokedex_entries = [] - used_forms = {} # Read from PBS file File.open(path, "rb") { |f| FileLineData.file = path # For error reporting @@ -821,190 +641,81 @@ module Compiler # the keys are the XXX and the values are the YYY (as unprocessed strings). schema = GameData::Species.schema(true) idx = 0 - pbEachFileSectionPokemonForms(f) { |contents, section_name| + pbEachFileSection(f) { |contents, section_name| echo "." if idx % 50 == 0 - idx += 1 Graphics.update if idx % 250 == 0 - FileLineData.setSection(section_name, "header", nil) # For error reporting - # Split section_name into a species number and form number - split_section_name = section_name.split(/[-,\s]/) - if split_section_name.length != 2 - raise _INTL("Section name {1} is invalid ({2}). Expected syntax like [XXX,Y] (XXX=species ID, Y=form number).", section_name, path) - end - species_symbol = csvEnumField!(split_section_name[0], :Species, nil, nil) - form = csvPosInt!(split_section_name[1]) - # Raise an error if a species is undefined, the form number is invalid or - # a species/form combo is used twice - if !GameData::Species.exists?(species_symbol) - raise _INTL("Species ID '{1}' is not defined in {2}.\r\n{3}", species_symbol, path, FileLineData.linereport) - elsif form == 0 - raise _INTL("A form cannot be defined with a form number of 0.\r\n{1}", FileLineData.linereport) - elsif used_forms[species_symbol]&.include?(form) - raise _INTL("Form {1} for species ID {2} is defined twice.\r\n{3}", form, species_symbol, FileLineData.linereport) - end - used_forms[species_symbol] = [] if !used_forms[species_symbol] - used_forms[species_symbol].push(form) - base_data = GameData::Species.get(species_symbol) + idx += 1 + data_hash = {:id => section_name.to_sym} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - # Skip empty properties (none are required) - if nil_or_empty?(contents[key]) - contents[key] = nil + FileLineData.setSection(section_name, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name, key, schema[key]) next end - FileLineData.setSection(section_name, key, contents[key]) # For error reporting + # Skip empty properties + next if contents[key].nil? # Compile value for key - if ["EVs", "EffortPoints"].include?(key) && contents[key].split(",")[0].numeric? - value = pbGetCsvRecord(contents[key], key, [0, "uuuuuu"]) # Old format - else - value = pbGetCsvRecord(contents[key], key, schema[key]) - end - value = nil if value.is_a?(Array) && value.length == 0 - contents[key] = value - # Sanitise data - case key - when "BaseStats" - value_hash = {} - GameData::Stat.each_main do |s| - value_hash[s.id] = value[s.pbs_order] if s.pbs_order >= 0 - end - contents[key] = value_hash - when "EVs", "EffortPoints" - if value[0].is_a?(Array) # New format - value_hash = {} - value.each { |val| value_hash[val[0]] = val[1] } - GameData::Stat.each_main { |s| value_hash[s.id] ||= 0 } - contents[key] = value_hash - else # Old format - value_hash = {} - GameData::Stat.each_main do |s| - value_hash[s.id] = value[s.pbs_order] if s.pbs_order >= 0 - end - contents[key] = value_hash - end - when "Height", "Weight" - # Convert height/weight to 1 decimal place and multiply by 10 - value = (value * 10).round - if value <= 0 - raise _INTL("Value for '{1}' can't be less than or close to 0 (section {2}, {3})", key, section_name, path) - end - contents[key] = value - when "Evolutions" - contents[key].each do |evo| - evo[3] = false - param_type = GameData::Evolution.get(evo[1]).parameter - if param_type.nil? - evo[2] = nil - elsif param_type == Integer - evo[2] = csvPosInt!(evo[2]) - elsif param_type != String - evo[2] = csvEnumField!(evo[2], param_type, "Evolutions", section_name) - end - end - end + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct species hash - form_symbol = sprintf("%s_%d", species_symbol.to_s, form).to_sym - types = contents["Types"] - types ||= [contents["Type1"], contents["Type2"]] if contents["Type1"] - types ||= base_data.types.clone - types = [types] if !types.is_a?(Array) - types = types.uniq.compact - moves = contents["Moves"] - if !moves - moves = [] - base_data.moves.each { |m| moves.push(m.clone) } - end - evolutions = contents["Evolutions"] - if !evolutions - evolutions = [] - base_data.evolutions.each { |e| evolutions.push(e.clone) } - end - species_hash = { - :id => form_symbol, - :species => species_symbol, - :form => form, - :name => base_data.real_name, - :form_name => contents["FormName"], - :category => contents["Category"] || contents["Kind"] || base_data.real_category, - :pokedex_entry => contents["Pokedex"] || base_data.real_pokedex_entry, - :pokedex_form => contents["PokedexForm"], - :types => types, - :base_stats => contents["BaseStats"] || base_data.base_stats, - :evs => contents["EVs"] || contents["EffortPoints"] || base_data.evs, - :base_exp => contents["BaseExp"] || contents["BaseEXP"] || base_data.base_exp, - :growth_rate => base_data.growth_rate, - :gender_ratio => base_data.gender_ratio, - :catch_rate => contents["CatchRate"] || contents["Rareness"] || base_data.catch_rate, - :happiness => contents["Happiness"] || base_data.happiness, - :moves => moves, - :tutor_moves => contents["TutorMoves"] || base_data.tutor_moves.clone, - :egg_moves => contents["EggMoves"] || base_data.egg_moves.clone, - :abilities => contents["Abilities"] || base_data.abilities.clone, - :hidden_abilities => contents["HiddenAbilities"] || contents["HiddenAbility"] || base_data.hidden_abilities.clone, - :wild_item_common => contents["WildItemCommon"] || base_data.wild_item_common.clone, - :wild_item_uncommon => contents["WildItemUncommon"] || base_data.wild_item_uncommon.clone, - :wild_item_rare => contents["WildItemRare"] || base_data.wild_item_rare.clone, - :egg_groups => contents["EggGroups"] || contents["Compatibility"] || base_data.egg_groups.clone, - :hatch_steps => contents["HatchSteps"] || contents["StepsToHatch"] || base_data.hatch_steps, - :incense => base_data.incense, - :offspring => contents["Offspring"] || base_data.offspring.clone, - :evolutions => evolutions, - :height => contents["Height"] || base_data.height, - :weight => contents["Weight"] || base_data.weight, - :color => contents["Color"] || base_data.color, - :shape => contents["Shape"] || base_data.shape, - :habitat => contents["Habitat"] || base_data.habitat, - :generation => contents["Generation"] || base_data.generation, - :flags => contents["Flags"] || base_data.flags.clone, - :mega_stone => contents["MegaStone"], - :mega_move => contents["MegaMove"], - :unmega_form => contents["UnmegaForm"], - :mega_message => contents["MegaMessage"] - } - # If form has any wild items, ensure none are inherited from base species - if (contents["WildItemCommon"] && !contents["WildItemCommon"].empty?) || - (contents["WildItemUncommon"] && !contents["WildItemUncommon"].empty?) || - (contents["WildItemRare"] && !contents["WildItemRare"].empty?) - species_hash[:wild_item_common] = contents["WildItemCommon"] - species_hash[:wild_item_uncommon] = contents["WildItemUncommon"] - species_hash[:wild_item_rare] = contents["WildItemRare"] - end - # Add form's data to records - GameData::Species.register(species_hash) - species_names.push(species_hash[:name]) - species_form_names.push(species_hash[:form_name]) - species_categories.push(species_hash[:category]) - species_pokedex_entries.push(species_hash[:pokedex_entry]) - # Save metrics data if defined (backwards compatibility) - if contents["BattlerPlayerX"] || contents["BattlerPlayerY"] || - contents["BattlerEnemyX"] || contents["BattlerEnemyY"] || - contents["BattlerAltitude"] || contents["BattlerShadowX"] || - contents["BattlerShadowSize"] - base_metrics = GameData::SpeciesMetrics.get_species_form(species_symbol, 0) - back_x = contents["BattlerPlayerX"] || base_metrics.back_sprite[0] - back_y = contents["BattlerPlayerY"] || base_metrics.back_sprite[1] - front_x = contents["BattlerEnemyX"] || base_metrics.front_sprite[0] - front_y = contents["BattlerEnemyY"] || base_metrics.front_sprite[1] - altitude = contents["BattlerAltitude"] || base_metrics.front_sprite_altitude - shadow_x = contents["BattlerShadowX"] || base_metrics.shadow_x - shadow_size = contents["BattlerShadowSize"] || base_metrics.shadow_size - metrics_hash = { - :id => form_symbol, - :species => species_symbol, - :form => form, - :back_sprite => [back_x, back_y], - :front_sprite => [front_x, front_y], - :front_sprite_altitude => altitude, - :shadow_x => shadow_x, - :shadow_size => shadow_size - } - GameData::SpeciesMetrics.register(metrics_hash) + # Validate and modify the compiled data + validate_compiled_pokemon_form(data_hash) + if GameData::Species.exists?(data_hash[:id]) + raise _INTL("Species ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end + # Add species's data to records + GameData::Species.register(data_hash) } } - # Add prevolution "evolution" entry for all evolved forms that define their - # own evolution methods (and thus won't have a prevolution listed already) + validate_all_compiled_pokemon_forms + # Save all data + GameData::Species.save + process_pbs_file_message_end + 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}'.\r\n{3}", hash[:species], FileLineData.linereport) + elsif GameData::Species.exists?(hash[:id]) + raise _INTL("Form {1} for species ID {2} is defined twice.\r\n{3}", hash[:form], hash[:species], 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 + # 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| @@ -1022,142 +733,139 @@ module Compiler prevo.evolutions.push([species.species, :None, nil]) end end - # Save all data - GameData::Species.save - GameData::SpeciesMetrics.save - MessageTypes.addMessagesAsHash(MessageTypes::Species, species_names) + # 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::FormNames, species_form_names) MessageTypes.addMessagesAsHash(MessageTypes::Kinds, species_categories) MessageTypes.addMessagesAsHash(MessageTypes::Entries, species_pokedex_entries) - process_pbs_file_message_end end #============================================================================= # Compile Pokémon metrics data #============================================================================= def compile_pokemon_metrics(path = "PBS/pokemon_metrics.txt") - return if !safeExists?(path) compile_pbs_file_message_start(path) - schema = GameData::SpeciesMetrics::SCHEMA + GameData::SpeciesMetrics::DATA.clear # Read from PBS file File.open(path, "rb") { |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). + schema = GameData::SpeciesMetrics::SCHEMA idx = 0 pbEachFileSection(f) { |contents, section_name| echo "." if idx % 50 == 0 - idx += 1 Graphics.update if idx % 250 == 0 - FileLineData.setSection(section_name, "header", nil) # For error reporting - # Split section_name into a species number and form number - split_section_name = section_name.split(/[-,\s]/) - if split_section_name.length == 0 || split_section_name.length > 2 - raise _INTL("Section name {1} is invalid ({2}). Expected syntax like [XXX] or [XXX,Y] (XXX=species ID, Y=form number).", section_name, path) - end - species_symbol = csvEnumField!(split_section_name[0], :Species, nil, nil) - form = (split_section_name[1]) ? csvPosInt!(split_section_name[1]) : 0 + idx += 1 + data_hash = {:id => section_name.to_sym} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - # Skip empty properties (none are required) - if nil_or_empty?(contents[key]) - contents[key] = nil + FileLineData.setSection(section_name, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name, key, schema[key]) next end - FileLineData.setSection(section_name, key, contents[key]) # For error reporting + # Skip empty properties + next if contents[key].nil? # Compile value for key value = pbGetCsvRecord(contents[key], key, schema[key]) - value = nil if value.is_a?(Array) && value.length == 0 - contents[key] = value + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct species hash - form_symbol = (form > 0) ? sprintf("%s_%d", species_symbol.to_s, form).to_sym : species_symbol - species_hash = { - :id => form_symbol, - :species => species_symbol, - :form => form, - :back_sprite => contents["BackSprite"], - :front_sprite => contents["FrontSprite"], - :front_sprite_altitude => contents["FrontSpriteAltitude"], - :shadow_x => contents["ShadowX"], - :shadow_size => contents["ShadowSize"] - } - # Add form's data to records - GameData::SpeciesMetrics.register(species_hash) + # Validate and modify the compiled data + validate_compiled_pokemon_metrics(data_hash) + if GameData::SpeciesMetrics.exists?(data_hash[:id]) + raise _INTL("Metrics for species '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) + end + # Add species' metrics to records + GameData::SpeciesMetrics.register(data_hash) } } + validate_all_compiled_pokemon_metrics # Save all data GameData::SpeciesMetrics.save process_pbs_file_message_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(path = "PBS/shadow_pokemon.txt") compile_pbs_file_message_start(path) GameData::ShadowPokemon::DATA.clear - schema = GameData::ShadowPokemon::SCHEMA - shadow_hash = nil - old_format = nil - # Read each line of shadow_pokemon.txt at a time and compile it into a - # Shadow Pokémon's data - idx = 0 - pbCompilerEachPreppedLine(path) { |line, line_no| - echo "." if idx % 250 == 0 - idx += 1 - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [species_id] - old_format = false if old_format.nil? - if old_format - raise _INTL("Can't mix old and new formats.\r\n{1}", FileLineData.linereport) + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::ShadowPokemon::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next + end + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Add previous Shadow Pokémon's data to records - GameData::ShadowPokemon.register(shadow_hash) if shadow_hash - # Parse species ID - species_id = $~[1].to_sym - if GameData::ShadowPokemon.exists?(species_id) - raise _INTL("Shadow Pokémon data for species '{1}' is defined twice.\r\n{2}", species_id, FileLineData.linereport) + # Validate and modify the compiled data + validate_compiled_shadow_pokemon(data_hash) + if GameData::ShadowPokemon.exists?(data_hash[:id]) + raise _INTL("Species ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Construct Shadow Pokémon hash - shadow_hash = { - :id => species_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - old_format = true if old_format.nil? - if old_format - key = $1 - value = $2 - value = value.split(",") - species = parseSpecies(key) - value.each { |val| val.strip! } - value.delete_if { |val| nil_or_empty?(val) } - # Construct Shadow Pokémon hash - shadow_hash = { - :id => species, - :moves => value - } - # Add Shadow Pokémons data to records - GameData::ShadowPokemon.register(shadow_hash) - shadow_hash = nil - else - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - shadow_hash[line_schema[0]] = property_value - end - end + # Add Shadow Pokémon data to records + GameData::ShadowPokemon.register(data_hash) + } } - # Add last item's data to records - GameData::ShadowPokemon.register(shadow_hash) if shadow_hash + validate_all_compiled_shadow_pokemon # Save all data GameData::ShadowPokemon.save process_pbs_file_message_end end + def validate_compiled_shadow_pokemon(hash) + end + + def validate_all_compiled_shadow_pokemon + end + #============================================================================= # Compile Regional Dexes #============================================================================= @@ -1203,70 +911,61 @@ module Compiler def compile_ribbons(path = "PBS/ribbons.txt") compile_pbs_file_message_start(path) GameData::Ribbon::DATA.clear - schema = GameData::Ribbon::SCHEMA - ribbon_names = [] - ribbon_descriptions = [] - ribbon_hash = nil - pbCompilerEachPreppedLine(path) { |line, line_no| - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [ribbon_id] - # Add previous ribbon's data to records - GameData::Ribbon.register(ribbon_hash) if ribbon_hash - # Parse ribbon ID - ribbon_id = $~[1].to_sym - if GameData::Ribbon.exists?(ribbon_id) - raise _INTL("Ribbon ID '{1}' is used twice.\r\n{2}", ribbon_id, FileLineData.linereport) + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::Ribbon::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next + end + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct ribbon hash - ribbon_hash = { - :id => ribbon_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - if !ribbon_hash - raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) + # Validate and modify the compiled data + validate_compiled_ribbon(data_hash) + if GameData::Ribbon.exists?(data_hash[:id]) + raise _INTL("Ribbon ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - ribbon_hash[line_schema[0]] = property_value - case property_name - when "Name" - ribbon_names.push(ribbon_hash[:name]) - when "Description" - ribbon_descriptions.push(ribbon_hash[:description]) - end - else # Old format - # Add previous ribbon's data to records - GameData::Ribbon.register(ribbon_hash) if ribbon_hash - # Parse ribbon - line = pbGetCsvRecord(line, line_no, [0, "unss"]) - ribbon_id = line[1].to_sym - if GameData::Ribbon::DATA[ribbon_id] - raise _INTL("Ribbon ID '{1}' is used twice.\r\n{2}", ribbon_id, FileLineData.linereport) - end - # Construct ribbon hash - ribbon_hash = { - :id => ribbon_id, - :name => line[2], - :description => line[3], - :icon_position => line[0] - 1 - } - # Add ribbon's data to records - GameData::Ribbon.register(ribbon_hash) - ribbon_names.push(ribbon_hash[:name]) - ribbon_descriptions.push(ribbon_hash[:description]) - ribbon_hash = nil - end + # Add ribbon data to records + GameData::Ribbon.register(data_hash) + } } - # Add last ribbon's data to records - GameData::Ribbon.register(ribbon_hash) if ribbon_hash + validate_all_compiled_ribbons # Save all data GameData::Ribbon.save + process_pbs_file_message_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::RibbonNames, ribbon_names) MessageTypes.setMessagesAsHash(MessageTypes::RibbonDescriptions, ribbon_descriptions) - process_pbs_file_message_end end #============================================================================= @@ -1384,76 +1083,60 @@ module Compiler def compile_trainer_types(path = "PBS/trainer_types.txt") compile_pbs_file_message_start(path) GameData::TrainerType::DATA.clear - schema = GameData::TrainerType::SCHEMA - tr_type_names = [] - tr_type_hash = nil - # Read each line of trainer_types.txt at a time and compile it into a trainer type - pbCompilerEachPreppedLine(path) { |line, line_no| - if line[/^\s*\[\s*(.+)\s*\]\s*$/] # New section [tr_type_id] - # Add previous trainer type's data to records - GameData::TrainerType.register(tr_type_hash) if tr_type_hash - # Parse trainer type ID - tr_type_id = $~[1].to_sym - if GameData::TrainerType.exists?(tr_type_id) - raise _INTL("Trainer Type ID '{1}' is used twice.\r\n{2}", tr_type_id, FileLineData.linereport) + # Read from PBS file + File.open(path, "rb") { |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). + schema = GameData::TrainerType::SCHEMA + idx = 0 + pbEachFileSection(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name.to_sym} + # 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]] = pbGetCsvRecord(section_name, key, schema[key]) + next + end + # Skip empty properties + next if contents[key].nil? + # Compile value for key + value = pbGetCsvRecord(contents[key], key, schema[key]) + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct trainer type hash - tr_type_hash = { - :id => tr_type_id - } - elsif line[/^\s*(\w+)\s*=\s*(.*)\s*$/] # XXX=YYY lines - if !tr_type_hash - raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) + # Validate and modify the compiled data + validate_compiled_trainer_type(data_hash) + if GameData::TrainerType.exists?(data_hash[:id]) + raise _INTL("Trainer type ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) end - # Parse property and value - property_name = $~[1] - line_schema = schema[property_name] - next if !line_schema - property_value = pbGetCsvRecord($~[2], line_no, line_schema) - # Record XXX=YYY setting - tr_type_hash[line_schema[0]] = property_value - tr_type_names.push(tr_type_hash[:name]) if property_name == "Name" - else # Old format - # Add previous trainer type's data to records - GameData::TrainerType.register(tr_type_hash) if tr_type_hash - # Parse trainer type - line = pbGetCsvRecord(line, line_no, - [0, "snsUSSSeUS", - nil, nil, nil, nil, nil, nil, nil, - { "Male" => 0, "M" => 0, "0" => 0, - "Female" => 1, "F" => 1, "1" => 1, - "Mixed" => 2, "X" => 2, "2" => 2, "" => 2 }, - nil, nil]) - tr_type_id = line[1].to_sym - if GameData::TrainerType.exists?(tr_type_id) - raise _INTL("Trainer Type ID '{1}' is used twice.\r\n{2}", tr_type_id, FileLineData.linereport) - end - # Construct trainer type hash - tr_type_hash = { - :id => tr_type_id, - :name => line[2], - :base_money => line[3], - :battle_BGM => line[4], - :victory_BGM => line[5], - :intro_BGM => line[6], - :gender => line[7], - :skill_level => line[8], - :flags => line[9] - } - # Add trainer type's data to records - GameData::TrainerType.register(tr_type_hash) - tr_type_names.push(tr_type_hash[:name]) - tr_type_hash = nil - end + # Add trainer type data to records + GameData::TrainerType.register(data_hash) + } } - # Add last trainer type's data to records - GameData::TrainerType.register(tr_type_hash) if tr_type_hash + validate_all_compiled_trainer_types # Save all data GameData::TrainerType.save - MessageTypes.setMessagesAsHash(MessageTypes::TrainerTypes, tr_type_names) process_pbs_file_message_end end + def validate_compiled_trainer_type(hash) + 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::TrainerTypes, trainer_type_names) + end + #============================================================================= # Compile individual trainer data #============================================================================= @@ -1708,87 +1391,91 @@ module Compiler compile_pbs_file_message_start(path) GameData::Metadata::DATA.clear GameData::PlayerMetadata::DATA.clear - storage_creator = [] # Read from PBS file File.open(path, "rb") { |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). - pbEachFileSectionNumbered(f) { |contents, section_id| - schema = (section_id == 0) ? GameData::Metadata::SCHEMA : GameData::PlayerMetadata::SCHEMA + global_schema = GameData::Metadata::SCHEMA + player_schema = GameData::PlayerMetadata::SCHEMA + idx = 0 + pbEachFileSectionNumbered(f) { |contents, section_name| + echo "." if idx % 50 == 0 + Graphics.update if idx % 250 == 0 + idx += 1 + schema = (section_name == 0) ? global_schema : player_schema + data_hash = {:id => section_name} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - FileLineData.setSection(section_id, key, contents[key]) # For error reporting - # Skip empty properties, or raise an error if a required property is - # empty - if contents[key].nil? - if section_id == 0 && ["Home"].include?(key) - raise _INTL("The entry {1} is required in {2} section 0.", key, path) - end + FileLineData.setSection(section_name.to_s, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name.to_s, key, schema[key]) next end + # Skip empty properties + next if contents[key].nil? # Compile value for key value = pbGetCsvRecord(contents[key], key, schema[key]) - value = nil if value.is_a?(Array) && value.length == 0 - contents[key] = value + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - if section_id == 0 # Metadata - # Construct metadata hash - metadata_hash = { - :id => section_id, - :start_money => contents["StartMoney"], - :start_item_storage => contents["StartItemStorage"], - :home => contents["Home"], - :storage_creator => contents["StorageCreator"], - :wild_battle_BGM => contents["WildBattleBGM"], - :trainer_battle_BGM => contents["TrainerBattleBGM"], - :wild_victory_BGM => contents["WildVictoryBGM"], - :trainer_victory_BGM => contents["TrainerVictoryBGM"], - :wild_capture_ME => contents["WildCaptureME"], - :surf_BGM => contents["SurfBGM"], - :bicycle_BGM => contents["BicycleBGM"] - } - storage_creator[0] = contents["StorageCreator"] - # Add metadata's data to records - GameData::Metadata.register(metadata_hash) - else # Player metadata - # Construct metadata hash - metadata_hash = { - :id => section_id, - :trainer_type => contents["TrainerType"], - :walk_charset => contents["WalkCharset"], - :run_charset => contents["RunCharset"], - :cycle_charset => contents["CycleCharset"], - :surf_charset => contents["SurfCharset"], - :dive_charset => contents["DiveCharset"], - :fish_charset => contents["FishCharset"], - :surf_fish_charset => contents["SurfFishCharset"] - } - # Add metadata's data to records - GameData::PlayerMetadata.register(metadata_hash) + # Validate and modify the compiled data + if section_name == 0 + validate_compiled_global_metadata(data_hash) + if GameData::Metadata.exists?(data_hash[:id]) + raise _INTL("Global metadata ID '{1}' is used twice.\r\n{2}", data_hash[:id], 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.\r\n{2}", data_hash[:id], FileLineData.linereport) + end + end + # Add trainer type data to records + if section_name == 0 + GameData::Metadata.register(data_hash) + else + GameData::PlayerMetadata.register(data_hash) end } } - if !GameData::PlayerMetadata.exists?(1) - raise _INTL("Metadata for player character 1 in {1} is not defined but should be.", path) - end + validate_all_compiled_metadata # Save all data GameData::Metadata.save GameData::PlayerMetadata.save - MessageTypes.setMessages(MessageTypes::StorageCreator, storage_creator) process_pbs_file_message_end end + def validate_compiled_global_metadata(hash) + if hash[:home].nil? + raise _INTL("The entry 'Home' is required in metadata.txt section 0.\r\n{1}", FileLineData.linereport) + end + end + + def validate_compiled_player_metadata(hash) + end + + 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.\r\n{1}", 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.\r\n{1}", FileLineData.linereport) + end + # Get storage creator's name for translating + storage_creator = [GameData::Metadata.get.real_storage_creator] + MessageTypes.setMessages(MessageTypes::StorageCreator, storage_creator) + end + #============================================================================= # Compile map metadata #============================================================================= def compile_map_metadata(path = "PBS/map_metadata.txt") compile_pbs_file_message_start(path) GameData::MapMetadata::DATA.clear - map_infos = pbLoadMapInfos - map_names = [] - map_infos.each_key { |id| map_names[id] = map_infos[id].name } # Read from PBS file File.open(path, "rb") { |f| FileLineData.file = path # For error reporting @@ -1797,57 +1484,56 @@ module Compiler # the keys are the XXX and the values are the YYY (as unprocessed strings). schema = GameData::MapMetadata::SCHEMA idx = 0 - pbEachFileSectionNumbered(f) { |contents, map_id| + pbEachFileSectionNumbered(f) { |contents, section_name| echo "." if idx % 50 == 0 - idx += 1 Graphics.update if idx % 250 == 0 + idx += 1 + data_hash = {:id => section_name} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - FileLineData.setSection(map_id, key, contents[key]) # For error reporting + FileLineData.setSection(section_name.to_s, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name.to_s, key, schema[key]) + next + end # Skip empty properties next if contents[key].nil? # Compile value for key value = pbGetCsvRecord(contents[key], key, schema[key]) - value = nil if value.is_a?(Array) && value.length == 0 - contents[key] = value + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Construct map metadata hash - metadata_hash = { - :id => map_id, - :name => contents["Name"], - :outdoor_map => contents["Outdoor"], - :announce_location => contents["ShowArea"], - :can_bicycle => contents["Bicycle"], - :always_bicycle => contents["BicycleAlways"], - :teleport_destination => contents["HealingSpot"], - :weather => contents["Weather"], - :town_map_position => contents["MapPosition"], - :dive_map_id => contents["DiveMap"], - :dark_map => contents["DarkMap"], - :safari_map => contents["SafariMap"], - :snap_edges => contents["SnapEdges"], - :random_dungeon => contents["Dungeon"], - :battle_background => contents["BattleBack"], - :wild_battle_BGM => contents["WildBattleBGM"], - :trainer_battle_BGM => contents["TrainerBattleBGM"], - :wild_victory_BGM => contents["WildVictoryBGM"], - :trainer_victory_BGM => contents["TrainerVictoryBGM"], - :wild_capture_ME => contents["WildCaptureME"], - :town_map_size => contents["MapSize"], - :battle_environment => contents["Environment"], - :flags => contents["Flags"] - } - # Add map metadata's data to records - GameData::MapMetadata.register(metadata_hash) - map_names[map_id] = metadata_hash[:name] if !nil_or_empty?(metadata_hash[:name]) + # Validate and modify the compiled data + validate_compiled_map_metadata(data_hash) + if GameData::MapMetadata.exists?(data_hash[:id]) + raise _INTL("Map metadata for map '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) + end + # Add map metadata to records + GameData::MapMetadata.register(data_hash) } } + validate_all_compiled_map_metadata # Save all data GameData::MapMetadata.save - MessageTypes.setMessages(MessageTypes::MapNames, map_names) process_pbs_file_message_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[id].name + end + end + + def validate_all_compiled_map_metadata + # Get map names for translating + map_names = [] + GameData::MapMetadata.each do |map| + map_names[map.id] = map.real_name + end + MessageTypes.setMessages(MessageTypes::MapNames, map_names) + end + #============================================================================= # Compile dungeon tileset data #============================================================================= @@ -1903,55 +1589,65 @@ module Compiler def compile_dungeon_parameters(path = "PBS/dungeon_parameters.txt") compile_pbs_file_message_start(path) GameData::DungeonParameters::DATA.clear - schema = GameData::DungeonParameters::SCHEMA # Read from PBS file File.open(path, "rb") { |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). + schema = GameData::DungeonParameters::SCHEMA idx = 0 pbEachFileSection(f) { |contents, section_name| echo "." if idx % 50 == 0 - idx += 1 Graphics.update if idx % 250 == 0 - FileLineData.setSection(section_name, "header", nil) # For error reporting - # Split section_name into an area and version number - split_section_name = section_name.split(/[-,\s]/) - if split_section_name.length == 0 || split_section_name.length > 2 - raise _INTL("Section name {1} is invalid ({2}). Expected syntax like [XXX] or [XXX,Y] (XXX=area, Y=version).", section_name, path) - end - area_symbol = split_section_name[0].downcase.to_sym - version = (split_section_name[1]) ? csvPosInt!(split_section_name[1]) : 0 - # Construct parameters hash - area_version = (version > 0) ? sprintf("%s_%d", area_symbol.to_s, version).to_sym : area_symbol - parameters_hash = { - :id => area_version, - :area => area_symbol, - :version => version - } + idx += 1 + data_hash = {:id => section_name} # Go through schema hash of compilable data and compile this section schema.each_key do |key| - # Skip empty properties (none are required) - if nil_or_empty?(contents[key]) - contents[key] = nil + FileLineData.setSection(section_name.to_s, key, contents[key]) # For error reporting + if key == "SectionName" + data_hash[schema[key][0]] = pbGetCsvRecord(section_name.to_s, key, schema[key]) next end - FileLineData.setSection(section_name, key, contents[key]) # For error reporting + # Skip empty properties + next if contents[key].nil? # Compile value for key value = pbGetCsvRecord(contents[key], key, schema[key]) - value = nil if value.is_a?(Array) && value.length == 0 - parameters_hash[schema[key][0]] = value + value = nil if value.is_a?(Array) && value.empty? + data_hash[schema[key][0]] = value end - # Add parameters data to records - GameData::DungeonParameters.register(parameters_hash) + # Validate and modify the compiled data + validate_compiled_dungeon_parameters(data_hash) + if GameData::DungeonParameters.exists?(data_hash[:id]) + raise _INTL("Dungeon ID '{1}' is used twice.\r\n{2}", data_hash[:id], FileLineData.linereport) + end + # Add dungeon parameters to records + GameData::DungeonParameters.register(data_hash) } } + validate_all_compiled_dungeon_parameters # Save all data GameData::DungeonParameters.save process_pbs_file_message_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.\r\n{3}", hash[:version], hash[:area], FileLineData.linereport) + end + end + + def validate_all_compiled_dungeon_parameters + end + #============================================================================= # Compile battle animations #============================================================================= diff --git a/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb b/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb index 6d9b68116..4f5ce6cc8 100644 --- a/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb +++ b/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb @@ -812,8 +812,7 @@ module Compiler f.write("[0]\r\n") metadata = GameData::Metadata.get schema = GameData::Metadata::SCHEMA - keys = schema.keys.sort { |a, b| schema[a][0] <=> schema[b][0] } - keys.each do |key| + schema.keys.each do |key| record = metadata.property_from_string(key) next if record.nil? || (record.is_a?(Array) && record.empty?) f.write(sprintf("%s = ", key)) @@ -822,11 +821,10 @@ module Compiler end # Write player metadata schema = GameData::PlayerMetadata::SCHEMA - keys = schema.keys.sort { |a, b| schema[a][0] <=> schema[b][0] } GameData::PlayerMetadata.each do |player_data| f.write("\#-------------------------------\r\n") f.write(sprintf("[%d]\r\n", player_data.id)) - keys.each do |key| + schema.keys.each do |key| record = player_data.property_from_string(key) next if record.nil? || (record.is_a?(Array) && record.empty?) f.write(sprintf("%s = ", key)) @@ -845,7 +843,6 @@ module Compiler write_pbs_file_message_start(path) map_infos = pbLoadMapInfos schema = GameData::MapMetadata::SCHEMA - keys = schema.keys.sort { |a, b| schema[a][0] <=> schema[b][0] } File.open(path, "wb") { |f| idx = 0 add_PBS_header_to_file(f) @@ -861,7 +858,7 @@ module Compiler else f.write(sprintf("[%03d]\r\n", map_data.id)) end - keys.each do |key| + schema.keys.each do |key| record = map_data.property_from_string(key) next if record.nil? || (record.is_a?(Array) && record.empty?) f.write(sprintf("%s = ", key))