document sorted restriction on random enemy room entries
This commit is contained in:
+25
-22
@@ -3330,13 +3330,13 @@ string MapFile::Event2Entry::str() const {
|
|||||||
this->action_stream_offset);
|
this->action_stream_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
string MapFile::RandomEnemyLocationSection::str() const {
|
string MapFile::RandomEnemyRoom::str() const {
|
||||||
string count_warning_str;
|
string count_warning_str;
|
||||||
if (count > 0x20) {
|
if (count > 0x20) {
|
||||||
count_warning_str = " /* warning: count is too large */";
|
count_warning_str = " /* warning: count is too large */";
|
||||||
}
|
}
|
||||||
return std::format("[RandomEnemyLocationSection room={:04X} count={:04X}{} offset={:08X}(index={:04X})]",
|
return std::format("[RandomEnemyRoom room={:04X} count={:04X}{} offset={:08X}(index={:04X})]",
|
||||||
this->room, this->count, count_warning_str, this->offset, this->offset / sizeof(RandomEnemyLocation));
|
this->room_id, this->count, count_warning_str, this->offset, this->offset / sizeof(RandomEnemyRoom));
|
||||||
}
|
}
|
||||||
|
|
||||||
string MapFile::RandomEnemyLocation::str() const {
|
string MapFile::RandomEnemyLocation::str() const {
|
||||||
@@ -3405,26 +3405,26 @@ uint32_t MapFile::RandomState::next_location_index() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MapFile::RandomState::generate_shuffled_location_table(
|
void MapFile::RandomState::generate_shuffled_location_table(
|
||||||
const RandomEnemyLocationsHeader& header, phosg::StringReader r, uint16_t room) {
|
const RandomEnemyLocationsHeader& header, phosg::StringReader r, uint16_t room_id) {
|
||||||
if (header.num_rooms == 0) {
|
if (header.num_rooms == 0) {
|
||||||
throw runtime_error("no locations defined");
|
throw runtime_error("no locations defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
phosg::StringReader rooms_r = r.sub(header.room_table_offset, header.num_rooms * sizeof(RandomEnemyLocationSection));
|
phosg::StringReader rooms_r = r.sub(header.room_table_offset, header.num_rooms * sizeof(RandomEnemyRoom));
|
||||||
|
|
||||||
size_t bs_min = 0;
|
size_t bs_min = 0;
|
||||||
size_t bs_max = header.num_rooms - 1;
|
size_t bs_max = header.num_rooms - 1;
|
||||||
do {
|
do {
|
||||||
size_t bs_mid = (bs_min + bs_max) / 2;
|
size_t bs_mid = (bs_min + bs_max) / 2;
|
||||||
if (rooms_r.pget<RandomEnemyLocationSection>(bs_mid * sizeof(RandomEnemyLocationSection)).room < room) {
|
if (rooms_r.pget<RandomEnemyRoom>(bs_mid * sizeof(RandomEnemyRoom)).room_id < room_id) {
|
||||||
bs_min = bs_mid + 1;
|
bs_min = bs_mid + 1;
|
||||||
} else {
|
} else {
|
||||||
bs_max = bs_mid;
|
bs_max = bs_mid;
|
||||||
}
|
}
|
||||||
} while (bs_min < bs_max);
|
} while (bs_min < bs_max);
|
||||||
|
|
||||||
const auto& sec = rooms_r.pget<RandomEnemyLocationSection>(bs_min * sizeof(RandomEnemyLocationSection));
|
const auto& sec = rooms_r.pget<RandomEnemyRoom>(bs_min * sizeof(RandomEnemyRoom));
|
||||||
if (room != sec.room) {
|
if (room_id != sec.room_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3612,12 +3612,12 @@ void MapFile::set_random_enemy_locations_for_floor(uint8_t floor, size_t file_of
|
|||||||
|
|
||||||
phosg::StringReader r(data, size);
|
phosg::StringReader r(data, size);
|
||||||
const auto& header = r.get<RandomEnemyLocationsHeader>();
|
const auto& header = r.get<RandomEnemyLocationsHeader>();
|
||||||
floor_sections.random_enemy_location_section_count = header.num_rooms;
|
floor_sections.random_enemy_room_count = header.num_rooms;
|
||||||
floor_sections.random_enemy_location_sections = &r.pget<RandomEnemyLocationSection>(
|
floor_sections.random_enemy_rooms = &r.pget<RandomEnemyRoom>(
|
||||||
header.room_table_offset, floor_sections.random_enemy_location_section_count * sizeof(RandomEnemyLocationSection));
|
header.room_table_offset, floor_sections.random_enemy_room_count * sizeof(RandomEnemyRoom));
|
||||||
size_t max_offset = 0;
|
size_t max_offset = 0;
|
||||||
for (size_t z = 0; z < floor_sections.random_enemy_location_section_count; z++) {
|
for (size_t z = 0; z < floor_sections.random_enemy_room_count; z++) {
|
||||||
const auto& sec = floor_sections.random_enemy_location_sections[z];
|
const auto& sec = floor_sections.random_enemy_rooms[z];
|
||||||
if (sec.offset % sizeof(RandomEnemyLocation)) {
|
if (sec.offset % sizeof(RandomEnemyLocation)) {
|
||||||
// TODO: We probably could support this if it's actually needed somewhere
|
// TODO: We probably could support this if it's actually needed somewhere
|
||||||
throw std::runtime_error(std::format("random enemy location offset {:08X} is not a multiple of struct size {:08X}",
|
throw std::runtime_error(std::format("random enemy location offset {:08X} is not a multiple of struct size {:08X}",
|
||||||
@@ -3647,9 +3647,9 @@ void MapFile::set_random_enemy_definitions_for_floor(uint8_t floor, size_t file_
|
|||||||
floor_sections.random_enemy_definition_count = header.entry_count;
|
floor_sections.random_enemy_definition_count = header.entry_count;
|
||||||
floor_sections.random_enemy_definitions = &r.pget<RandomEnemyDefinition>(
|
floor_sections.random_enemy_definitions = &r.pget<RandomEnemyDefinition>(
|
||||||
header.entries_offset, floor_sections.random_enemy_definition_count * sizeof(RandomEnemyDefinition));
|
header.entries_offset, floor_sections.random_enemy_definition_count * sizeof(RandomEnemyDefinition));
|
||||||
floor_sections.random_enemy_definition_weight_count = header.weight_entry_count;
|
floor_sections.random_enemy_weight_count = header.weight_entry_count;
|
||||||
floor_sections.random_enemy_definition_weights = &r.pget<RandomEnemyWeight>(
|
floor_sections.random_enemy_weights = &r.pget<RandomEnemyWeight>(
|
||||||
header.weight_entries_offset, floor_sections.random_enemy_definition_weight_count * sizeof(RandomEnemyWeight));
|
header.weight_entries_offset, floor_sections.random_enemy_weight_count * sizeof(RandomEnemyWeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const MapFile> MapFile::materialize_random_sections(uint32_t random_seed) const {
|
std::shared_ptr<const MapFile> MapFile::materialize_random_sections(uint32_t random_seed) const {
|
||||||
@@ -4075,12 +4075,15 @@ string MapFile::disassemble(bool reassembly, Version version) const {
|
|||||||
ret.emplace_back(std::format(".random_enemy_locations {} /* offset 0x{:X} in file; 0x{:X} bytes */",
|
ret.emplace_back(std::format(".random_enemy_locations {} /* offset 0x{:X} in file; 0x{:X} bytes */",
|
||||||
floor, sf.random_enemy_locations_file_offset, sf.random_enemy_locations_file_size));
|
floor, sf.random_enemy_locations_file_offset, sf.random_enemy_locations_file_size));
|
||||||
}
|
}
|
||||||
for (size_t z = 0; z < sf.random_enemy_location_section_count; z++) {
|
uint16_t last_room_id = 0;
|
||||||
const auto& sec = sf.random_enemy_location_sections[z];
|
for (size_t z = 0; z < sf.random_enemy_room_count; z++) {
|
||||||
|
const auto& sec = sf.random_enemy_rooms[z];
|
||||||
|
const std::string& warning_text = (sec.room_id < last_room_id) ? " /* warning: room IDs out of order */" : "";
|
||||||
|
last_room_id = sec.room_id;
|
||||||
if (reassembly) {
|
if (reassembly) {
|
||||||
ret.emplace_back(sec.str());
|
ret.emplace_back(std::format("{}{}", sec.str(), warning_text));
|
||||||
} else {
|
} else {
|
||||||
ret.emplace_back(std::format("/* section index {:04X} */ {}", z, sec.str()));
|
ret.emplace_back(std::format("/* room index {:04X} */ {}{}", z, sec.str(), warning_text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t z = 0; z < sf.random_enemy_location_count; z++) {
|
for (size_t z = 0; z < sf.random_enemy_location_count; z++) {
|
||||||
@@ -4109,8 +4112,8 @@ string MapFile::disassemble(bool reassembly, Version version) const {
|
|||||||
ret.emplace_back(std::format("/* definition index {:04X} */ {}", z, def.str()));
|
ret.emplace_back(std::format("/* definition index {:04X} */ {}", z, def.str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t z = 0; z < sf.random_enemy_definition_weight_count; z++) {
|
for (size_t z = 0; z < sf.random_enemy_weight_count; z++) {
|
||||||
const auto& weight = sf.random_enemy_definition_weights[z];
|
const auto& weight = sf.random_enemy_weights[z];
|
||||||
if (reassembly) {
|
if (reassembly) {
|
||||||
ret.emplace_back(weight.str());
|
ret.emplace_back(weight.str());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+14
-9
@@ -303,20 +303,24 @@ public:
|
|||||||
} __packed_ws__(Event2Entry, 0x18);
|
} __packed_ws__(Event2Entry, 0x18);
|
||||||
|
|
||||||
struct RandomEnemyLocationsHeader { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
struct RandomEnemyLocationsHeader { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||||
/* 00 */ le_uint32_t room_table_offset; // Offset to RandomEnemyLocationSection structs, from start of this struct
|
// The room table specifies which locations are valid for each room, but these are specified indirectly as offsets
|
||||||
|
// into the entries table (pointed to by entries_offset). The entries in the room table must be sorted in
|
||||||
|
// increasing order of room_id; if they aren't, the client may fail to find a valid room during enemy placement,
|
||||||
|
// which can crash the client.
|
||||||
|
/* 00 */ le_uint32_t room_table_offset; // Offset to RandomEnemyRoom structs, from start of this struct
|
||||||
/* 04 */ le_uint32_t entries_offset; // Offset to RandomEnemyLocation structs, from start of this struct
|
/* 04 */ le_uint32_t entries_offset; // Offset to RandomEnemyLocation structs, from start of this struct
|
||||||
/* 08 */ le_uint32_t num_rooms;
|
/* 08 */ le_uint32_t num_rooms;
|
||||||
/* 0C */
|
/* 0C */
|
||||||
} __packed_ws__(RandomEnemyLocationsHeader, 0x0C);
|
} __packed_ws__(RandomEnemyLocationsHeader, 0x0C);
|
||||||
|
|
||||||
struct RandomEnemyLocationSection { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
struct RandomEnemyRoom { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||||
/* 00 */ le_uint16_t room;
|
/* 00 */ le_uint16_t room_id;
|
||||||
/* 02 */ le_uint16_t count;
|
/* 02 */ le_uint16_t count;
|
||||||
/* 04 */ le_uint32_t offset; // Bytes from start of RandomEnemyLocation section
|
/* 04 */ le_uint32_t offset; // Bytes from start of RandomEnemyLocation section
|
||||||
/* 08 */
|
/* 08 */
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
} __packed_ws__(RandomEnemyLocationSection, 8);
|
} __packed_ws__(RandomEnemyRoom, 8);
|
||||||
|
|
||||||
struct RandomEnemyLocation { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
struct RandomEnemyLocation { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||||
/* 00 */ VectorXYZF pos;
|
/* 00 */ VectorXYZF pos;
|
||||||
@@ -376,7 +380,8 @@ public:
|
|||||||
explicit RandomState(uint32_t random_seed);
|
explicit RandomState(uint32_t random_seed);
|
||||||
size_t rand_int_biased(size_t min_v, size_t max_v);
|
size_t rand_int_biased(size_t min_v, size_t max_v);
|
||||||
uint32_t next_location_index();
|
uint32_t next_location_index();
|
||||||
void generate_shuffled_location_table(const RandomEnemyLocationsHeader& header, phosg::StringReader r, uint16_t room);
|
void generate_shuffled_location_table(
|
||||||
|
const RandomEnemyLocationsHeader& header, phosg::StringReader r, uint16_t room_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FloorSections {
|
struct FloorSections {
|
||||||
@@ -409,8 +414,8 @@ public:
|
|||||||
size_t random_enemy_locations_file_size = 0;
|
size_t random_enemy_locations_file_size = 0;
|
||||||
const void* random_enemy_locations_data = nullptr;
|
const void* random_enemy_locations_data = nullptr;
|
||||||
size_t random_enemy_locations_data_size = 0;
|
size_t random_enemy_locations_data_size = 0;
|
||||||
const RandomEnemyLocationSection* random_enemy_location_sections = nullptr;
|
const RandomEnemyRoom* random_enemy_rooms = nullptr;
|
||||||
size_t random_enemy_location_section_count = 0;
|
size_t random_enemy_room_count = 0;
|
||||||
const RandomEnemyLocation* random_enemy_locations = nullptr;
|
const RandomEnemyLocation* random_enemy_locations = nullptr;
|
||||||
size_t random_enemy_location_count = 0;
|
size_t random_enemy_location_count = 0;
|
||||||
|
|
||||||
@@ -420,8 +425,8 @@ public:
|
|||||||
size_t random_enemy_definitions_data_size = 0;
|
size_t random_enemy_definitions_data_size = 0;
|
||||||
const RandomEnemyDefinition* random_enemy_definitions = nullptr;
|
const RandomEnemyDefinition* random_enemy_definitions = nullptr;
|
||||||
size_t random_enemy_definition_count = 0;
|
size_t random_enemy_definition_count = 0;
|
||||||
const RandomEnemyWeight* random_enemy_definition_weights = nullptr;
|
const RandomEnemyWeight* random_enemy_weights = nullptr;
|
||||||
size_t random_enemy_definition_weight_count = 0;
|
size_t random_enemy_weight_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Quest constructor
|
// Quest constructor
|
||||||
|
|||||||
Reference in New Issue
Block a user