Generalised compiler and writer methods for trainers.txt

This commit is contained in:
Maruno17
2022-11-23 22:44:15 +00:00
parent 91efb4684b
commit 427cc45629
2 changed files with 148 additions and 122 deletions

View File

@@ -749,132 +749,157 @@ module Compiler
def compile_trainers(*paths) def compile_trainers(*paths)
GameData::Trainer::DATA.clear GameData::Trainer::DATA.clear
schema = GameData::Trainer.schema schema = GameData::Trainer.schema
max_level = GameData::GrowthRate.max_level sub_schema = GameData::Trainer.sub_schema
trainer_names = [] idx = 0
trainer_lose_texts = [] # Read from PBS file(s)
paths.each do |path| paths.each do |path|
compile_pbs_file_message_start(path) compile_pbs_file_message_start(path)
file_suffix = File.basename(path, ".txt")[GameData::Trainer::PBS_BASE_FILENAME.length + 1, path.length] || "" file_suffix = File.basename(path, ".txt")[GameData::Trainer::PBS_BASE_FILENAME.length + 1, path.length] || ""
trainer_hash = nil data_hash = nil
current_pkmn = nil current_pkmn = nil
section_name = nil
section_line = nil
# Read each line of trainers.txt at a time and compile it as a trainer property # Read each line of trainers.txt at a time and compile it as a trainer property
idx = 0
pbCompilerEachPreppedLine(path) { |line, line_no| pbCompilerEachPreppedLine(path) { |line, line_no|
echo "." if idx % 50 == 0 echo "." if idx % 50 == 0
idx += 1 idx += 1
Graphics.update if idx % 250 == 0 Graphics.update if idx % 250 == 0
FileLineData.setSection(section_name, nil, section_line)
if line[/^\s*\[\s*(.+)\s*\]\s*$/] if line[/^\s*\[\s*(.+)\s*\]\s*$/]
# New section [trainer_type, name] or [trainer_type, name, version] # New section [trainer_type, name] or [trainer_type, name, version]
if trainer_hash section_name = $~[1]
if !current_pkmn section_line = line
raise _INTL("Started new trainer while previous trainer has no Pokémon.\r\n{1}", FileLineData.linereport) if data_hash
end validate_compiled_trainer(data_hash)
# Add trainer's data to records GameData::Trainer.register(data_hash)
trainer_hash[:id] = [trainer_hash[:trainer_type], trainer_hash[:name], trainer_hash[:version]]
GameData::Trainer.register(trainer_hash)
end end
line_data = pbGetCsvRecord($~[1], line_no, [0, "esU", :TrainerType]) FileLineData.setSection(section_name, nil, section_line)
# Construct trainer hash # Construct data hash
trainer_hash = { data_hash = {
:trainer_type => line_data[0],
:name => line_data[1],
:version => line_data[2] || 0,
:pokemon => [],
:pbs_file_suffix => file_suffix :pbs_file_suffix => file_suffix
} }
data_hash[schema["SectionName"][0]] = pbGetCsvRecord(section_name.clone, line_no, schema["SectionName"])
data_hash[schema["Pokemon"][0]] = []
current_pkmn = nil current_pkmn = nil
trainer_names.push(trainer_hash[:name])
elsif line[/^\s*(\w+)\s*=\s*(.*)$/] elsif line[/^\s*(\w+)\s*=\s*(.*)$/]
# XXX=YYY lines # XXX=YYY lines
if !trainer_hash if !data_hash
raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport) raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport)
end end
property_name = $~[1] key = $~[1]
line_schema = schema[property_name] if schema[key] # Property of the trainer
next if !line_schema property_value = pbGetCsvRecord($~[2], line_no, schema[key])
property_value = pbGetCsvRecord($~[2], line_no, line_schema) if key == "Pokemon"
# Error checking in XXX=YYY lines current_pkmn = {
case property_name :species => property_value[0],
when "Pokemon" :level => property_value[1]
if property_value[1] > max_level }
raise _INTL("Bad level: {1} (must be 1-{2}).\r\n{3}", property_value[1], max_level, FileLineData.linereport) data_hash[schema[key][0]].push(current_pkmn)
else
data_hash[schema[key][0]] = property_value
end end
when "Name" elsif sub_schema[key] # Property of a Pokémon
if property_value.length > Pokemon::MAX_NAME_SIZE
raise _INTL("Bad nickname: {1} (must be 1-{2} characters).\r\n{3}", property_value, Pokemon::MAX_NAME_SIZE, FileLineData.linereport)
end
when "Moves"
property_value.uniq!
when "IV"
property_value.each do |iv|
next if iv <= Pokemon::IV_STAT_LIMIT
raise _INTL("Bad IV: {1} (must be 0-{2}).\r\n{3}", iv, Pokemon::IV_STAT_LIMIT, FileLineData.linereport)
end
when "EV"
property_value.each do |ev|
next if ev <= Pokemon::EV_STAT_LIMIT
raise _INTL("Bad EV: {1} (must be 0-{2}).\r\n{3}", ev, Pokemon::EV_STAT_LIMIT, FileLineData.linereport)
end
ev_total = 0
GameData::Stat.each_main do |s|
next if s.pbs_order < 0
ev_total += (property_value[s.pbs_order] || property_value[0])
end
if ev_total > Pokemon::EV_LIMIT
raise _INTL("Total EVs are greater than allowed ({1}).\r\n{2}", Pokemon::EV_LIMIT, FileLineData.linereport)
end
when "Happiness"
if property_value > 255
raise _INTL("Bad happiness: {1} (must be 0-255).\r\n{2}", property_value, FileLineData.linereport)
end
when "Ball"
if !GameData::Item.get(property_value).is_poke_ball?
raise _INTL("Value {1} isn't a defined Poké Ball.\r\n{2}", property_value, FileLineData.linereport)
end
end
# Record XXX=YYY setting
case property_name
when "Items", "LoseText"
trainer_hash[line_schema[0]] = property_value
trainer_lose_texts.push(property_value) if property_name == "LoseText"
when "Pokemon"
current_pkmn = {
:species => property_value[0],
:level => property_value[1]
}
trainer_hash[line_schema[0]].push(current_pkmn)
else
if !current_pkmn if !current_pkmn
raise _INTL("Pokémon hasn't been defined yet!\r\n{1}", FileLineData.linereport) raise _INTL("Pokémon hasn't been defined yet!\r\n{1}", FileLineData.linereport)
end end
case property_name current_pkmn[sub_schema[key][0]] = pbGetCsvRecord($~[2], line_no, sub_schema[key])
when "IV", "EV"
value_hash = {}
GameData::Stat.each_main do |s|
next if s.pbs_order < 0
value_hash[s.id] = property_value[s.pbs_order] || property_value[0]
end
current_pkmn[line_schema[0]] = value_hash
else
current_pkmn[line_schema[0]] = property_value
end
end end
end end
} }
# Add last trainer's data to records # Add last trainer's data to records
if trainer_hash if data_hash
if !current_pkmn FileLineData.setSection(section_name, nil, section_line)
raise _INTL("End of file reached while last trainer has no Pokémon.\r\n{1}", FileLineData.linereport) validate_compiled_trainer(data_hash)
end GameData::Trainer.register(data_hash)
trainer_hash[:id] = [trainer_hash[:trainer_type], trainer_hash[:name], trainer_hash[:version]]
GameData::Trainer.register(trainer_hash)
end end
process_pbs_file_message_end process_pbs_file_message_end
end end
validate_all_compiled_trainers
# Save all data # Save all data
GameData::Trainer.save GameData::Trainer.save
end
def validate_compiled_trainer(hash)
# Split trainer type, name and version into their own values, generate compound ID from them
hash[:id][2] ||= 0
hash[:trainer_type] = hash[:id][0]
hash[:real_name] = hash[:id][1]
hash[:version] = hash[:id][2]
# Ensure the trainer has at least one Pokémon
if hash[:pokemon].empty?
raise _INTL("Trainer with ID {1} has no Pokémon.\r\n{2}", hash[:id], FileLineData.linereport)
end
max_level = GameData::GrowthRate.max_level
hash[:pokemon].each do |pkmn|
# Ensure valid level
if pkmn[:level] > max_level
raise _INTL("Invalid Pokémon level {1} (must be 1-{2}).\r\n{3}",
pkmn[:level], max_level, FileLineData.linereport)
end
# Ensure valid name length
if pkmn[:name] && pkmn[:name].length > Pokemon::MAX_NAME_SIZE
raise _INTL("Invalid Pokémon nickname: {1} (must be 1-{2} characters).\r\n{3}",
pkmn[:name], Pokemon::MAX_NAME_SIZE, FileLineData.linereport)
end
# Ensure no duplicate moves
pkmn[:moves].uniq! if pkmn[:moves]
# Ensure valid IVs, convert IVs to hash format
if pkmn[:iv]
iv_hash = {}
GameData::Stat.each_main do |s|
next if s.pbs_order < 0
iv_hash[s.id] = pkmn[:iv][s.pbs_order] || pkmn[:iv][0]
if iv_hash[s.id] > Pokemon::IV_STAT_LIMIT
raise _INTL("Invalid IV: {1} (must be 0-{2}).\r\n{3}",
iv_hash[s.id], Pokemon::IV_STAT_LIMIT, FileLineData.linereport)
end
end
pkmn[:iv] = iv_hash
end
# Ensure valid EVs, convert EVs to hash format
if pkmn[:ev]
ev_hash = {}
ev_total = 0
GameData::Stat.each_main do |s|
next if s.pbs_order < 0
ev_hash[s.id] = pkmn[:ev][s.pbs_order] || pkmn[:ev][0]
ev_total += ev_hash[s.id]
if ev_hash[s.id] > Pokemon::EV_STAT_LIMIT
raise _INTL("Invalid EV: {1} (must be 0-{2}).\r\n{3}",
ev_hash[s.id], Pokemon::EV_STAT_LIMIT, FileLineData.linereport)
end
end
pkmn[:ev] = ev_hash
if ev_total > Pokemon::EV_LIMIT
raise _INTL("Invalid EV set (must sum to {1} or less).\r\n{2}",
Pokemon::EV_LIMIT, FileLineData.linereport)
end
end
# Ensure valid happiness
if pkmn[:happiness]
if pkmn[:happiness] > 255
raise _INTL("Bad happiness: {1} (must be 0-255).\r\n{2}", pkmn[:happiness], FileLineData.linereport)
end
end
# Ensure valid Poké Ball
if pkmn[:poke_ball]
if !GameData::Item.get(pkmn[:poke_ball]).is_poke_ball?
raise _INTL("Value {1} isn't a defined Poké Ball.\r\n{2}", pkmn[:poke_ball], FileLineData.linereport)
end
end
end
end
def validate_all_compiled_trainers
# Get trainer names and lose texts for translating
trainer_names = []
lose_texts = []
GameData::Trainer.each do |trainer|
trainer_names.push(trainer.real_name)
lose_texts.push(trainer.real_lose_text)
end
MessageTypes.setMessagesAsHash(MessageTypes::TrainerNames, trainer_names) MessageTypes.setMessagesAsHash(MessageTypes::TrainerNames, trainer_names)
MessageTypes.setMessagesAsHash(MessageTypes::TrainerLoseText, trainer_lose_texts) MessageTypes.setMessagesAsHash(MessageTypes::TrainerLoseText, lose_texts)
end end
#============================================================================= #=============================================================================

View File

@@ -452,50 +452,51 @@ module Compiler
#============================================================================= #=============================================================================
def write_trainers def write_trainers
paths = get_all_PBS_file_paths(GameData::Trainer) paths = get_all_PBS_file_paths(GameData::Trainer)
schema = GameData::Trainer.schema
sub_schema = GameData::Trainer.sub_schema
idx = 0 idx = 0
paths.each do |path| paths.each do |path|
write_pbs_file_message_start(path[0]) write_pbs_file_message_start(path[0])
File.open(path[0], "wb") { |f| File.open(path[0], "wb") { |f|
add_PBS_header_to_file(f) add_PBS_header_to_file(f)
# Write each element in turn
GameData::Trainer.each do |element| GameData::Trainer.each do |element|
next if element.pbs_file_suffix != path[1] next if element.pbs_file_suffix != path[1]
echo "." if idx % 50 == 0 echo "." if idx % 50 == 0
Graphics.update if idx % 250 == 0 Graphics.update if idx % 250 == 0
idx += 1 idx += 1
f.write("\#-------------------------------\r\n") f.write("\#-------------------------------\r\n")
if element.version > 0 if schema["SectionName"]
f.write(sprintf("[%s,%s,%d]\r\n", element.trainer_type, element.real_name, element.version)) f.write("[")
pbWriteCsvRecord(element.get_property_for_PBS("SectionName"), f, schema["SectionName"])
f.write("]\r\n")
else else
f.write(sprintf("[%s,%s]\r\n", element.trainer_type, element.real_name)) f.write("[#{element.id}]\r\n")
end end
f.write(sprintf("Items = %s\r\n", element.items.join(","))) if element.items.length > 0 # Write each trainer property
if element.real_lose_text && !element.real_lose_text.empty? schema.each_key do |key|
f.write(sprintf("LoseText = %s\r\n", element.real_lose_text)) next if key == "SectionName" || key == "Pokemon"
val = element.get_property_for_PBS(key)
next if val.nil?
f.write(sprintf("%s = ", key))
pbWriteCsvRecord(val, f, schema[key])
f.write("\r\n")
end end
element.pokemon.each do |pkmn| # Write each Pokémon in turn
f.write(sprintf("Pokemon = %s,%d\r\n", pkmn[:species], pkmn[:level])) element.pokemon.each_with_index do |pkmn, i|
f.write(sprintf(" Name = %s\r\n", pkmn[:name])) if pkmn[:name] && !pkmn[:name].empty? # Write species/level
f.write(sprintf(" Form = %d\r\n", pkmn[:form])) if pkmn[:form] && pkmn[:form] > 0 val = element.get_pokemon_property_for_PBS("Pokemon", i)
f.write(sprintf(" Gender = %s\r\n", (pkmn[:gender] == 1) ? "female" : "male")) if pkmn[:gender] f.write("Pokemon = ")
f.write(" Shiny = yes\r\n") if pkmn[:shininess] && !pkmn[:super_shininess] pbWriteCsvRecord(val, f, schema["Pokemon"])
f.write(" SuperShiny = yes\r\n") if pkmn[:super_shininess] f.write("\r\n")
f.write(" Shadow = yes\r\n") if pkmn[:shadowness] # Write other Pokémon properties
f.write(sprintf(" Moves = %s\r\n", pkmn[:moves].join(","))) if pkmn[:moves] && pkmn[:moves].length > 0 sub_schema.each_key do |key|
f.write(sprintf(" Ability = %s\r\n", pkmn[:ability])) if pkmn[:ability] val = element.get_pokemon_property_for_PBS(key, i)
f.write(sprintf(" AbilityIndex = %d\r\n", pkmn[:ability_index])) if pkmn[:ability_index] next if val.nil?
f.write(sprintf(" Item = %s\r\n", pkmn[:item])) if pkmn[:item] f.write(sprintf(" %s = ", key))
f.write(sprintf(" Nature = %s\r\n", pkmn[:nature])) if pkmn[:nature] pbWriteCsvRecord(val, f, sub_schema[key])
ivs_array = [] f.write("\r\n")
evs_array = []
GameData::Stat.each_main do |s|
next if s.pbs_order < 0
ivs_array[s.pbs_order] = pkmn[:iv][s.id] if pkmn[:iv]
evs_array[s.pbs_order] = pkmn[:ev][s.id] if pkmn[:ev]
end end
f.write(sprintf(" IV = %s\r\n", ivs_array.join(","))) if pkmn[:iv]
f.write(sprintf(" EV = %s\r\n", evs_array.join(","))) if pkmn[:ev]
f.write(sprintf(" Happiness = %d\r\n", pkmn[:happiness])) if pkmn[:happiness]
f.write(sprintf(" Ball = %s\r\n", pkmn[:poke_ball])) if pkmn[:poke_ball]
end end
end end
} }