diff --git a/src/Episode3/DataIndexes.cc b/src/Episode3/DataIndexes.cc index 60ba0fc7..28da6d4a 100644 --- a/src/Episode3/DataIndexes.cc +++ b/src/Episode3/DataIndexes.cc @@ -1359,12 +1359,26 @@ void StateFlags::clear_FF() { this->client_sc_card_types.clear(CardType::INVALID_FF); } +string MapDefinition::CameraSpec::str() const { + return string_printf( + "CameraSpec[a1=(%g %g %g %g %g %g %g %g %g) camera=(%g %g %g) focus=(%g %g %g) a2=(%g %g %g)]", + this->unknown_a1[0].load(), this->unknown_a1[1].load(), + this->unknown_a1[2].load(), this->unknown_a1[3].load(), + this->unknown_a1[4].load(), this->unknown_a1[5].load(), + this->unknown_a1[6].load(), this->unknown_a1[7].load(), + this->unknown_a1[8].load(), this->camera_x.load(), + this->camera_y.load(), this->camera_z.load(), + this->focus_x.load(), this->focus_y.load(), + this->focus_z.load(), this->unknown_a2[0].load(), + this->unknown_a2[1].load(), this->unknown_a2[2].load()); +} + string MapDefinition::str(const CardIndex* card_index) const { deque lines; auto add_map = [&](const parray, 0x10>& tiles) { - for (size_t y = 0; y < 0x10; y++) { + for (size_t y = 0; y < this->height; y++) { string line = " "; - for (size_t x = 0; x < 0x10; x++) { + for (size_t x = 0; x < this->height; x++) { line += string_printf(" %02hhX", tiles[y][x]); } lines.emplace_back(std::move(line)); @@ -1375,7 +1389,7 @@ string MapDefinition::str(const CardIndex* card_index) const { this->map_number.load(), this->width, this->height)); lines.emplace_back(string_printf(" a1=%08" PRIX32, this->unknown_a1.load())); lines.emplace_back(string_printf(" environment_number=%02hhX", this->environment_number)); - lines.emplace_back(string_printf(" num_alt_maps=%02hhX", this->num_alt_maps)); + lines.emplace_back(string_printf(" num_camera_zones=%02hhX", this->num_camera_zones)); lines.emplace_back(" tiles:"); add_map(this->map_tiles); lines.emplace_back(string_printf( @@ -1386,32 +1400,19 @@ string MapDefinition::str(const CardIndex* card_index) const { this->start_tile_definitions[1][0], this->start_tile_definitions[1][1], this->start_tile_definitions[1][2], this->start_tile_definitions[1][3], this->start_tile_definitions[1][4], this->start_tile_definitions[1][5])); - for (size_t z = 0; z < this->num_alt_maps; z++) { + for (size_t z = 0; z < this->num_camera_zones; z++) { for (size_t w = 0; w < 2; w++) { - lines.emplace_back(string_printf(" alt tiles %zu/%zu:", z, w)); - add_map(this->alt_maps1[w][z]); + lines.emplace_back(string_printf(" camera zone %zu (team %zu):", z, w)); + add_map(this->camera_zone_maps[w][z]); } for (size_t w = 0; w < 2; w++) { - lines.emplace_back(string_printf( - " alt tiles a3 %zu/%zu=%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", z, w, - this->alt_maps_unknown_a3[w][z][0x00].load(), this->alt_maps_unknown_a3[w][z][0x01].load(), - this->alt_maps_unknown_a3[w][z][0x02].load(), this->alt_maps_unknown_a3[w][z][0x03].load(), - this->alt_maps_unknown_a3[w][z][0x04].load(), this->alt_maps_unknown_a3[w][z][0x05].load(), - this->alt_maps_unknown_a3[w][z][0x06].load(), this->alt_maps_unknown_a3[w][z][0x07].load(), - this->alt_maps_unknown_a3[w][z][0x08].load(), this->alt_maps_unknown_a3[w][z][0x09].load(), - this->alt_maps_unknown_a3[w][z][0x0A].load(), this->alt_maps_unknown_a3[w][z][0x0B].load(), - this->alt_maps_unknown_a3[w][z][0x0C].load(), this->alt_maps_unknown_a3[w][z][0x0D].load(), - this->alt_maps_unknown_a3[w][z][0x0E].load(), this->alt_maps_unknown_a3[w][z][0x0F].load(), - this->alt_maps_unknown_a3[w][z][0x10].load(), this->alt_maps_unknown_a3[w][z][0x11].load())); + lines.emplace_back(" " + this->camera_zone_specs[w][z].str()); } } for (size_t w = 0; w < 3; w++) { - for (size_t z = 0; z < 0x24; z += 3) { - lines.emplace_back(string_printf( - " a4[%zu][0x%02zX:0x%02zX]=%g %g %g", w, z, z + 3, - this->unknown_a4[w][z + 0].load(), - this->unknown_a4[w][z + 1].load(), - this->unknown_a4[w][z + 2].load())); + for (size_t z = 0; z < 2; z++) { + string spec_str = this->overview_specs[w][z].str(); + lines.emplace_back(string_printf(" overview_specs[%zu][team %zu]=%s", w, z, spec_str.c_str())); } } lines.emplace_back(" modification tiles:"); @@ -1438,28 +1439,26 @@ string MapDefinition::str(const CardIndex* card_index) const { lines.emplace_back(string_printf(" map_xy: %hu %hu", this->map_x.load(), this->map_y.load())); for (size_t z = 0; z < 3; z++) { lines.emplace_back(string_printf(" npc_chars[%zu]:", z)); + lines.emplace_back(" name: " + string(this->npc_ai_params[z].name)); lines.emplace_back(string_printf( - " a1=%04hX %04hX", - this->npc_chars[z].unknown_a1[0].load(), this->npc_chars[z].unknown_a1[1].load())); - lines.emplace_back(string_printf( - " a2=%02hX %02hX %02hX %02hX", - this->npc_chars[z].unknown_a2[0], this->npc_chars[z].unknown_a2[1], - this->npc_chars[z].unknown_a2[2], this->npc_chars[z].unknown_a2[3])); - lines.emplace_back(" name: " + string(this->npc_chars[z].name)); + " ai_params=(a1=%04hX %04hX, is_arkz=%02hhX, a2=%02hX %02hX %02hX)", + this->npc_ai_params[z].unknown_a1[0].load(), this->npc_ai_params[z].unknown_a1[1].load(), + this->npc_ai_params[z].is_arkz, this->npc_ai_params[z].unknown_a2[0], + this->npc_ai_params[z].unknown_a2[1], this->npc_ai_params[z].unknown_a2[2])); for (size_t w = 0; w < 0x78; w += 0x08) { lines.emplace_back(string_printf( - " a3[0x%02zX:0x%02zX]=%04hX %04hX %04hX %04hX %04hX %04hX %04hX %04hX", + " ai_params.a3[0x%02zX:0x%02zX]=%04hX %04hX %04hX %04hX %04hX %04hX %04hX %04hX", w, w + 0x08, - this->npc_chars[z].unknown_a3[w + 0x00].load(), this->npc_chars[z].unknown_a3[w + 0x01].load(), - this->npc_chars[z].unknown_a3[w + 0x02].load(), this->npc_chars[z].unknown_a3[w + 0x03].load(), - this->npc_chars[z].unknown_a3[w + 0x04].load(), this->npc_chars[z].unknown_a3[w + 0x05].load(), - this->npc_chars[z].unknown_a3[w + 0x06].load(), this->npc_chars[z].unknown_a3[w + 0x07].load())); + this->npc_ai_params[z].params[w + 0x00].load(), this->npc_ai_params[z].params[w + 0x01].load(), + this->npc_ai_params[z].params[w + 0x02].load(), this->npc_ai_params[z].params[w + 0x03].load(), + this->npc_ai_params[z].params[w + 0x04].load(), this->npc_ai_params[z].params[w + 0x05].load(), + this->npc_ai_params[z].params[w + 0x06].load(), this->npc_ai_params[z].params[w + 0x07].load())); } lines.emplace_back(string_printf( - " a3[0x78:0x7E]=%04hX %04hX %04hX %04hX %04hX %04hX", - this->npc_chars[z].unknown_a3[0x78].load(), this->npc_chars[z].unknown_a3[0x79].load(), - this->npc_chars[z].unknown_a3[0x7A].load(), this->npc_chars[z].unknown_a3[0x7B].load(), - this->npc_chars[z].unknown_a3[0x7C].load(), this->npc_chars[z].unknown_a3[0x7D].load())); + " ai_params.a3[0x78:0x7E]=%04hX %04hX %04hX %04hX %04hX %04hX", + this->npc_ai_params[z].params[0x78].load(), this->npc_ai_params[z].params[0x79].load(), + this->npc_ai_params[z].params[0x7A].load(), this->npc_ai_params[z].params[0x7B].load(), + this->npc_ai_params[z].params[0x7C].load(), this->npc_ai_params[z].params[0x7D].load())); lines.emplace_back(string_printf(" npc_decks[%zu]:", z)); lines.emplace_back(" name: " + string(this->npc_decks[z].name)); for (size_t w = 0; w < 0x20; w++) { @@ -1490,9 +1489,9 @@ string MapDefinition::str(const CardIndex* card_index) const { } } } - lines.emplace_back(" a7a=" + format_data_string(this->unknown_a7_a.data(), this->unknown_a7_a.bytes())); - lines.emplace_back(string_printf(" a7b=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "]", - this->unknown_a7_b[0].load(), this->unknown_a7_b[1].load(), this->unknown_a7_b[2].load())); + lines.emplace_back(" a7=" + format_data_string(this->unknown_a7.data(), this->unknown_a7.bytes())); + lines.emplace_back(string_printf(" npc_ai_params_entry_index=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "]", + this->npc_ai_params_entry_index[0].load(), this->npc_ai_params_entry_index[1].load(), this->npc_ai_params_entry_index[2].load())); if (this->before_message[0]) { lines.emplace_back(" before_message: " + string(this->before_message)); } @@ -1520,7 +1519,7 @@ string MapDefinition::str(const CardIndex* card_index) const { } lines.emplace_back(string_printf(" level_overrides=[win=%" PRId32 ", loss=%" PRId32 "]", this->win_level_override.load(), this->loss_level_override.load())); - lines.emplace_back(string_printf(" a9=[%04hX %04hX]", this->unknown_a9_c.load(), this->unknown_a9_d.load())); + lines.emplace_back(string_printf(" field_offset=(x: %hd units, y:%hd units) (x: %lg tiles, y: %lg tiles)", this->field_offset_x.load(), this->field_offset_y.load(), static_cast(this->field_offset_x) / 25.0, static_cast(this->field_offset_y) / 25.0)); lines.emplace_back(string_printf(" map_category=%02hhX", this->map_category)); lines.emplace_back(string_printf(" cyber_block_type=%02hhX", this->cyber_block_type)); lines.emplace_back(string_printf(" a11=%02hhX%02hhX", this->unknown_a11[0], this->unknown_a11[1])); @@ -1618,12 +1617,12 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map) width(map.width), height(map.height), environment_number(map.environment_number), - num_alt_maps(map.num_alt_maps), + num_camera_zones(map.num_camera_zones), map_tiles(map.map_tiles), start_tile_definitions(map.start_tile_definitions), - alt_maps1(map.alt_maps1), - alt_maps_unknown_a3(map.alt_maps_unknown_a3), - unknown_a4(map.unknown_a4), + camera_zone_maps(map.camera_zone_maps), + camera_zone_specs(map.camera_zone_specs), + overview_specs(map.overview_specs), modification_tiles(map.modification_tiles), unknown_a5(map.unknown_a5), default_rules(map.default_rules), @@ -1635,9 +1634,9 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map) map_x(map.map_x), map_y(map.map_y), npc_decks(map.npc_decks), - npc_chars(map.npc_chars), - unknown_a7_a(map.unknown_a7_a), - unknown_a7_b(map.unknown_a7_b), + npc_ai_params(map.npc_ai_params), + unknown_a7(map.unknown_a7), + npc_ai_params_entry_index(map.npc_ai_params_entry_index), before_message(map.before_message), after_message(map.after_message), dispatch_message(map.dispatch_message), @@ -1645,8 +1644,8 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map) reward_card_ids(map.reward_card_ids), win_level_override(map.win_level_override), loss_level_override(map.loss_level_override), - unknown_a9_c(map.unknown_a9_c), - unknown_a9_d(map.unknown_a9_d), + field_offset_x(map.field_offset_x), + field_offset_y(map.field_offset_y), map_category(map.map_category), cyber_block_type(map.cyber_block_type), unknown_a11(map.unknown_a11), diff --git a/src/Episode3/DataIndexes.hh b/src/Episode3/DataIndexes.hh index 204c5973..ce78c158 100644 --- a/src/Episode3/DataIndexes.hh +++ b/src/Episode3/DataIndexes.hh @@ -864,7 +864,7 @@ struct CompressedMapHeader { // .mnm file format } __attribute__((packed)); struct MapDefinition { // .mnmd format; also the format of (decompressed) quests - /* 0000 */ be_uint32_t unknown_a1; + /* 0000 */ be_uint32_t unknown_a1; // Should be 0x00000100 /* 0004 */ be_uint32_t map_number; // Must be unique across all maps /* 0008 */ uint8_t width; /* 0009 */ uint8_t height; @@ -909,10 +909,8 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests // TCard00_Select is accessible on newserv with the $ep3battledebug command. /* 000A */ uint8_t environment_number; - // All alt_maps fields (including the floats) past num_alt_maps are filled in - // with FF. For example, if num_alt_maps == 8, the last two fields in each - // alt_maps array are filled with FF. - /* 000B */ uint8_t num_alt_maps; // TODO: What are the alt maps for? + // This field specifies how many of the camera_zone_maps are used. + /* 000B */ uint8_t num_camera_zones; // In the map_tiles array, the values are usually: // 00 = not a valid tile (blocked) @@ -936,9 +934,44 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests // - If the team has 3 players, bytes [3] through [5] are used. /* 010C */ parray, 2> start_tile_definitions; - /* 0118 */ parray, 0x10>, 0x0A>, 2> alt_maps1; - /* 1518 */ parray, 0x0A>, 2> alt_maps_unknown_a3; - /* 1AB8 */ parray, 3> unknown_a4; + struct CameraSpec { + parray unknown_a1; + be_float camera_x; + be_float camera_y; + be_float camera_z; + // It appears that the camera always aligns its +Y raster axis with +Y in + // the virtual world. If the focus point is directly beneath the camera + // point, the logic for deciding which direction should be "up" from the + // camera's perspective can get confused and jitter back and forth as the + // camera moves into position. + be_float focus_x; + be_float focus_y; + be_float focus_z; + parray unknown_a2; + + std::string str() const; + } __attribute__((packed)); + + // This array specifies the camera zone maps. A camera zone map is a subset of + // the main map (specified in map_tiles). Tiles that are part of each camera + // zone are 1 in these arrays; all other tiles are 0. The game evaluates each + // camera zone in order; if all SCs and FCs are within a particular camera + // zone, then the corresponding camera location is used as the default camera + // location. If the player doesn't move the camera with the C stick, then the + // camera zones are evaluated continuously during the battle, and the camera + // will move to focus on the part of the field where the SCs/FCs are. (Or, + // more accurately, where the corresponding entry in camera_zone_specs says + // to focus.) camera_zone_maps is indexed as [team_id][camera_zone_num][x][y]; + // camera_zone_specs is indexed as [team_id][camera_zone_num]. Unused entries + // (beyond num_camera_zones) in both arrays should be filled with FF bytes. + /* 0118 */ parray, 0x10>, 10>, 2> camera_zone_maps; + /* 1518 */ parray, 2> camera_zone_specs; + // These camera specs are used in the Move phase, when the player has chosen + // an SC or FC to move, or when the player presses Start/Z. Normally these are + // defined such that the camera is placed high above the map, giving an + // overhead view of the entire playfield. This is indexed as [???][team_id] + // (it is not yet known what the major index represents). + /* 1AB8 */ parray, 3> overview_specs; // In the modification_tiles array, the values are: // 10 = blocked by rock (as if the corresponding map_tiles value was 00) @@ -971,17 +1004,26 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests } __attribute__((packed)); /* 1FE8 */ parray npc_decks; // Unused if name[0] == 0 - struct NPCCharacter { + // These are almost (but not quite) the same format as the entries in + // aiprm.dat. Unlike in that file, the name field is relevant here (and is + // shown to the player). + struct AIParams { /* 0000 */ parray unknown_a1; - /* 0004 */ parray unknown_a2; + /* 0004 */ uint8_t is_arkz; + /* 0005 */ parray unknown_a2; /* 0008 */ ptext name; - /* 0018 */ parray unknown_a3; + // TODO: Figure out exactly how these are used and document here. + /* 0018 */ parray params; /* 0114 */ } __attribute__((packed)); - /* 20F0 */ parray npc_chars; // Unused if name[0] == 0 + /* 20F0 */ parray npc_ai_params; // Unused if name[0] == 0 - /* 242C */ parray unknown_a7_a; // Always FF? - /* 2434 */ parray unknown_a7_b; // Always FF? + /* 242C */ parray unknown_a7; // Always FF? + + // This array specifies which set of AI parameters to use from aiprm.dat. If + // it's -1, then the corresponding NPC's AI parameters are defined in the + // NPCCharacter structure above. + /* 2434 */ parray npc_ai_params_entry_index; // In story mode, before_message appears before the battle if it's not blank; // in free battle and online mode, before_message is ignored. after_message @@ -1010,14 +1052,16 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests /* 59D0 */ be_int32_t win_level_override; /* 59D4 */ be_int32_t loss_level_override; - /* 59D8 */ be_uint16_t unknown_a9_c; - /* 59DA */ be_uint16_t unknown_a9_d; + // These fields specify where the battlefield should appear relative to the + // center of the environment. THe size of one tile on the field is 25 units + // in these fields. + /* 59D8 */ be_int16_t field_offset_x; + /* 59DA */ be_int16_t field_offset_y; - // map_category specifies where in the menu the map should appear in the maps - // menu. If this is FF, the map appears in the Free Battle section; if it's a - // small number (usually 0 or 2), the map appears in the Quest section - // instead. It's not known if this controls anything else; the values 0, 1, - // and 2 appear to all do the same thing here. + // map_category specifies where the map should appear in the maps menu. If + // this is 0, 1, or 2, the map appears in the Quest section; otherwise, it + // appears in the Free Battle section instead. It's not known if this controls + // anything else. /* 59DC */ uint8_t map_category; // This field determines block graphics to be used in the Cyber environment. @@ -1087,12 +1131,12 @@ struct MapDefinitionTrial { /* 0008 */ uint8_t width; /* 0009 */ uint8_t height; /* 000A */ uint8_t environment_number; - /* 000B */ uint8_t num_alt_maps; + /* 000B */ uint8_t num_camera_zones; /* 000C */ parray, 0x10> map_tiles; /* 010C */ parray, 2> start_tile_definitions; - /* 0118 */ parray, 0x10>, 0x0A>, 2> alt_maps1; - /* 1518 */ parray, 0x0A>, 2> alt_maps_unknown_a3; - /* 1AB8 */ parray, 3> unknown_a4; + /* 0118 */ parray, 0x10>, 10>, 2> camera_zone_maps; + /* 1518 */ parray, 2> camera_zone_specs; + /* 1AB8 */ parray, 3> overview_specs; /* 1C68 */ parray, 0x10> modification_tiles; /* 1D68 */ parray unknown_a5; /* 1DD4 */ Rules default_rules; @@ -1104,18 +1148,18 @@ struct MapDefinitionTrial { /* 1FDC */ be_uint16_t map_x; /* 1FDE */ be_uint16_t map_y; /* 1FE0 */ parray npc_decks; - /* 20E8 */ parray npc_chars; - /* 2424 */ parray unknown_a7_a; - /* 242C */ parray unknown_a7_b; + /* 20E8 */ parray npc_ai_params; + /* 2424 */ parray unknown_a7; + /* 242C */ parray npc_ai_params_entry_index; /* 2438 */ ptext before_message; /* 25C8 */ ptext after_message; /* 2758 */ ptext dispatch_message; /* 28E8 */ parray, 3> dialogue_sets; /* 4148 */ parray reward_card_ids; - /* 4168 */ be_uint32_t win_level_override; - /* 416C */ be_uint32_t loss_level_override; - /* 4170 */ be_uint16_t unknown_a9_c; - /* 4172 */ be_uint16_t unknown_a9_d; + /* 4168 */ be_int32_t win_level_override; + /* 416C */ be_int32_t loss_level_override; + /* 4170 */ be_int16_t field_offset_x; + /* 4172 */ be_int16_t field_offset_y; /* 4174 */ uint8_t map_category; /* 4175 */ uint8_t cyber_block_type; /* 4176 */ parray unknown_a11; diff --git a/system/ep3/maps-free/m000507p_e.bind b/system/ep3/maps-free/m000507p_e.bind index 12d5b09b..57536d3d 100755 Binary files a/system/ep3/maps-free/m000507p_e.bind and b/system/ep3/maps-free/m000507p_e.bind differ