diff --git a/src/Main.cc b/src/Main.cc index 4dae81b8..1696bb98 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -3064,6 +3064,21 @@ Action a_check_supermaps( phosg::fwrite_fmt(stderr, "ALL QUEST MAPS: {}\n", all_quests_eff_str); }); +Action a_materialize_map( + "materialize-map", nullptr, + +[](phosg::Arguments& args) { + if (args.get("debug")) { + static_game_data_log.min_level = phosg::LogLevel::L_DEBUG; + } + auto map_data = make_shared(prs_decompress(read_input_data(args))); + auto map_file = make_shared(map_data); + uint32_t seed = args.get("seed", phosg::Arguments::IntFormat::HEX); + Version version = get_cli_version(args); + auto materialized = map_file->materialize_random_sections(seed); + auto disassembly = materialized->disassemble(false, version); + write_output_data(args, disassembly.data(), disassembly.size(), "txt"); + }); + Action a_print_free_supermap( "print-free-supermap", "\ print-free-supermap [--psov2] [--seed=SEED] [--episode=1|2|4]\n\ diff --git a/src/Map.cc b/src/Map.cc index 1cdfb901..3f68a5cf 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -3345,7 +3345,7 @@ string MapFile::RandomEnemyLocation::str() const { } string MapFile::RandomEnemyDefinition::str() const { - return std::format("[RandomEnemyDefinition params=[{:g} {:g} {:g} {:g} {:g} {:04X} {:04X}] entry_num={:08X} min_children={:04X} max_children={:04X}]", + return std::format("[RandomEnemyDefinition params=[{:g} {:g} {:g} {:g} {:g} {:04X} {:04X}] entry_index={:08X} min_children={:04X} max_children={:04X}]", this->param1, this->param2, this->param3, @@ -3353,7 +3353,7 @@ string MapFile::RandomEnemyDefinition::str() const { this->param5, this->param6, this->param7, - this->entry_num, + this->entry_index, this->min_children, this->max_children); } @@ -3364,10 +3364,10 @@ string MapFile::RandomEnemyWeight::str() const { base_type_index_str = std::format("(->{:04X})", MapFile::RAND_ENEMY_BASE_TYPES.at(this->base_type_index)); } catch (const std::out_of_range&) { } - return std::format("[RandomEnemyWeight base_type_index={:02X}{} def_entry_num={} weight={:02X} a4={:02X}]", + return std::format("[RandomEnemyWeight base_type_index={:02X}{} def_entry_index={} weight={:02X} a4={:02X}]", this->base_type_index, base_type_index_str, - this->def_entry_num, + this->def_entry_index, this->weight, this->unknown_a4); } @@ -3735,7 +3735,7 @@ std::shared_ptr MapFile::materialize_random_sections(uint32_t random_se if (det < weight_entry.weight) { static_game_data_log.debug_f("(Floor {} event {} wave {} enemy {}) This results in weight entry {}", floor, source_event_index, remaining_waves, remaining_enemies, weight_entry.str()); - if ((weight_entry.base_type_index != 0xFF) && (weight_entry.def_entry_num != 0xFF)) { + if ((weight_entry.base_type_index != 0xFF) && (weight_entry.def_entry_index != 0xFF)) { if (definitions_header.entry_count == 0) { throw runtime_error("no available random enemy definitions"); } @@ -3750,7 +3750,7 @@ std::shared_ptr MapFile::materialize_random_sections(uint32_t random_se size_t bs_max = definitions_header.entry_count - 1; do { size_t bs_mid = (bs_min + bs_max) / 2; - if (definitions_r.pget(bs_mid * sizeof(RandomEnemyDefinition)).entry_num < weight_entry.def_entry_num) { + if (definitions_r.pget(bs_mid * sizeof(RandomEnemyDefinition)).entry_index < weight_entry.def_entry_index) { bs_min = bs_mid + 1; } else { bs_max = bs_mid; @@ -3758,7 +3758,7 @@ std::shared_ptr MapFile::materialize_random_sections(uint32_t random_se } while (bs_min < bs_max); const auto& def = definitions_r.pget(bs_min * sizeof(RandomEnemyDefinition)); - if (def.entry_num == weight_entry.def_entry_num) { + if (def.entry_index == weight_entry.def_entry_index) { static_game_data_log.debug_f("(Floor {} event {} wave {} enemy {}) Using parameters from {}", floor, source_event_index, remaining_waves, remaining_enemies, def.str()); e.param1 = def.param1; @@ -3768,6 +3768,8 @@ std::shared_ptr MapFile::materialize_random_sections(uint32_t random_se e.param5 = def.param5; e.param6 = def.param6; e.param7 = def.param7; + // Note: The original code calls this with (min_children, max_children + 1); we add 1 inside the + // function instead, which is functionally equivalent. e.num_children = random_state.rand_int_biased(def.min_children, def.max_children); } else { throw runtime_error("random enemy definition not found"); diff --git a/src/Map.hh b/src/Map.hh index 22b83cc7..06eceb49 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -337,8 +337,8 @@ public: } __packed_ws__(RandomEnemyDefinitionsHeader, 0x10); struct RandomEnemyDefinition { // Section type 5 (RANDOM_ENEMY_DEFINITIONS) - // All fields through entry_num map to the corresponding fields in EnemySetEntry. Note that the order of param6 and - // param7 is switched! + // All fields through entry_index map to the corresponding fields in EnemySetEntry. Note that the order of param6 + // and param7 is switched! /* 00 */ le_float param1; /* 04 */ le_float param2; /* 08 */ le_float param3; @@ -346,7 +346,8 @@ public: /* 10 */ le_float param5; /* 14 */ le_int16_t param7; /* 16 */ le_int16_t param6; - /* 18 */ le_uint32_t entry_num; + /* 18 */ le_uint16_t entry_index; + /* 1A */ le_uint16_t unknown_a1; /* 1C */ le_uint16_t min_children; /* 1E */ le_uint16_t max_children; /* 20 */ @@ -356,7 +357,7 @@ public: struct RandomEnemyWeight { // Section type 5 (RANDOM_ENEMY_DEFINITIONS) /* 00 */ uint8_t base_type_index; - /* 01 */ uint8_t def_entry_num; + /* 01 */ uint8_t def_entry_index; /* 02 */ uint8_t weight; /* 03 */ uint8_t unknown_a4; /* 04 */