refine ObjectEntry structure

This commit is contained in:
Martin Michelsen
2023-11-02 23:20:00 -07:00
parent f1b00d3ee0
commit 6b812520bc
5 changed files with 101 additions and 56 deletions
+2 -1
View File
@@ -4393,7 +4393,8 @@ struct G_StandardDropItemRequest_DC_6x60 {
} __packed__;
struct G_StandardDropItemRequest_PC_V3_BB_6x60 : G_StandardDropItemRequest_DC_6x60 {
le_float unknown_a2 = 0.0f;
uint8_t effective_area = 0;
parray<uint8_t, 3> unused;
} __packed__;
// 6x61: Activate MAG effect
+45 -16
View File
@@ -71,9 +71,9 @@ struct ObjectEntry {
/* 1C */ le_uint32_t x_angle;
/* 20 */ le_uint32_t y_angle;
/* 24 */ le_uint32_t z_angle;
/* 28 */ le_uint32_t unknown_a4;
/* 2C */ le_uint32_t unknown_a5;
/* 30 */ le_uint32_t unknown_a6;
/* 28 */ le_float unknown_a4;
/* 2C */ le_float unknown_a5;
/* 30 */ le_float unknown_a6;
/* 34 */ le_uint32_t unknown_a7;
/* 38 */ le_uint32_t unknown_a8;
/* 3C */ le_uint32_t unknown_a9;
@@ -81,12 +81,11 @@ struct ObjectEntry {
/* 44 */
string str() const {
return string_printf("ObjectEntry(base_type=%hX, a1=%hX, a2=%" PRIX32 ", id=%hX, group=%hX, section=%hX, a3=%hX, x=%g, y=%g, z=%g, x_angle=%" PRIX32 ", y_angle=%" PRIX32 ", z_angle=%" PRIX32 ", a3=%" PRIX32 ", a4=%" PRIX32 ", a5=%" PRIX32 ", a6=%" PRIX32 ", a7=%" PRIX32 ", a8=%" PRIX32 ", a9=%" PRIX32 ")",
return string_printf("ObjectEntry(base_type=%hX, a1=%hX, a2=%" PRIX32 ", id=%hX, group=%hX, section=%hX, a3=%hX, x=%g, y=%g, z=%g, x_angle=%" PRIX32 ", y_angle=%" PRIX32 ", z_angle=%" PRIX32 ", a4=%g, a5=%g, a6=%g, a7=%" PRIX32 ", a8=%" PRIX32 ", a9=%" PRIX32 ", a9=%" PRIX32 ")",
this->base_type.load(), this->unknown_a1.load(), this->unknown_a2.load(), this->id.load(), this->group.load(),
this->section.load(), this->unknown_a3.load(), this->x.load(), this->y.load(), this->z.load(), this->x_angle.load(),
this->y_angle.load(), this->z_angle.load(), this->unknown_a3.load(), this->unknown_a4.load(),
this->unknown_a5.load(), this->unknown_a6.load(), this->unknown_a7.load(), this->unknown_a8.load(),
this->unknown_a9.load());
this->y_angle.load(), this->z_angle.load(), this->unknown_a4.load(), this->unknown_a5.load(), this->unknown_a6.load(),
this->unknown_a7.load(), this->unknown_a8.load(), this->unknown_a9.load(), this->unknown_a9.load());
}
} __attribute__((packed));
@@ -95,6 +94,20 @@ void Map::clear() {
this->rare_enemy_indexes.clear();
}
void Map::add_objects_from_map_data(const void* data, size_t size) {
const auto* map = reinterpret_cast<const ObjectEntry*>(data);
size_t entry_count = size / sizeof(ObjectEntry);
if (size != entry_count * sizeof(ObjectEntry)) {
throw runtime_error("data size is not a multiple of entry size");
}
for (size_t y = 0; y < entry_count; y++) {
const auto& e = map[y];
string hex = format_data_string(&e, sizeof(e));
fprintf(stderr, "[%04zX] %s\n", y, hex.c_str());
}
}
void Map::add_enemies_from_map_data(
Episode episode,
uint8_t difficulty,
@@ -487,7 +500,7 @@ struct DATSectionHeader {
le_uint32_t data_size;
} __attribute__((packed));
void Map::add_enemies_from_quest_data(
void Map::add_enemies_and_objects_from_quest_data(
Episode episode,
uint8_t difficulty,
uint8_t event,
@@ -502,11 +515,19 @@ void Map::add_enemies_from_quest_data(
if (header.section_size < sizeof(header)) {
throw runtime_error(string_printf("quest layout has invalid section header at offset 0x%zX", r.where() - sizeof(header)));
}
if (header.type == 2) {
if (header.type == 1) {
if (header.data_size % sizeof(ObjectEntry)) {
throw runtime_error("quest layout object section size is not a multiple of object entry size");
}
this->add_objects_from_map_data(r.getv(header.data_size), header.data_size);
} else if (header.type == 2) {
if (header.data_size % sizeof(EnemyEntry)) {
throw runtime_error("quest layout enemy section size is not a multiple of enemy entry size");
}
this->add_enemies_from_map_data(episode, difficulty, event, r.getv(header.data_size), header.data_size);
} else {
r.skip(header.section_size - sizeof(header));
}
@@ -756,7 +777,7 @@ void generate_variations(
}
vector<string> map_filenames_for_variation(
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2) {
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2, bool is_enemies) {
// Map filenames are like map_<name_token>[_VV][_VV][_off]<e|o>[_s].dat
// name_token comes from AreaMapFileIndex
// _VV are the values from the variation<1|2>_values vector (in contrast to
@@ -785,13 +806,21 @@ vector<string> map_filenames_for_variation(
filename += string_printf("_%02" PRIX32, a->variation2_values.at(var2));
}
// Try both _off<e|o>.dat and <e|o>_s.dat suffixes first before falling back
// to non-solo version
vector<string> ret;
if (is_solo) {
// Try both _offe.dat and e_s.dat suffixes first before falling back to
// non-solo version
ret.emplace_back(filename + "_offe.dat");
ret.emplace_back(filename + "e_s.dat");
if (is_enemies) {
if (is_solo) {
ret.emplace_back(filename + "_offe.dat");
ret.emplace_back(filename + "e_s.dat");
}
ret.emplace_back(filename + "e.dat");
} else {
if (is_solo) {
ret.emplace_back(filename + "_offo.dat");
ret.emplace_back(filename + "o_s.dat");
}
ret.emplace_back(filename + "o.dat");
}
ret.emplace_back(filename + "e.dat");
return ret;
}
+3 -2
View File
@@ -47,6 +47,7 @@ struct Map {
std::vector<size_t> rare_enemy_indexes;
void clear();
void add_objects_from_map_data(const void* data, size_t size);
void add_enemies_from_map_data(
Episode episode,
uint8_t difficulty,
@@ -54,7 +55,7 @@ struct Map {
const void* data,
size_t size,
const RareEnemyRates* rare_rates = nullptr);
void add_enemies_from_quest_data(
void add_enemies_and_objects_from_quest_data(
Episode episode,
uint8_t difficulty,
uint8_t event,
@@ -97,5 +98,5 @@ void generate_variations(
Episode episode,
bool is_solo);
std::vector<std::string> map_filenames_for_variation(
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2);
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2, bool is_enemies);
void load_map_files();
+30 -29
View File
@@ -2516,7 +2516,7 @@ static void on_AC_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
auto vq = l->quest->version(QuestScriptVersion::BB_V4, c->language());
auto dat_contents = prs_decompress(*vq->dat_contents);
l->map->clear();
l->map->add_enemies_from_quest_data(l->episode, l->difficulty, l->event, dat_contents.data(), dat_contents.size());
l->map->add_enemies_and_objects_from_quest_data(l->episode, l->difficulty, l->event, dat_contents.data(), dat_contents.size());
c->log.info("Replaced enemies list with quest layout (%zu entries)",
l->map->enemies.size());
for (size_t z = 0; z < l->map->enemies.size(); z++) {
@@ -3472,43 +3472,44 @@ shared_ptr<Lobby> create_game_generic(
for (size_t area = 0; area < 0x10; area++) {
c->log.info("[Map/%zu] Using variations %" PRIX32 ", %" PRIX32,
area, game->variations[area * 2].load(), game->variations[area * 2 + 1].load());
auto filenames = map_filenames_for_variation(
auto enemy_filenames = map_filenames_for_variation(
game->episode,
is_solo,
area,
game->variations[area * 2],
game->variations[area * 2 + 1]);
if (filenames.empty()) {
c->log.info("[Map/%zu] No file to load", area);
continue;
}
bool any_map_loaded = false;
for (const string& filename : filenames) {
try {
auto map_data = s->load_bb_file(filename, "", "map/" + filename);
size_t start_offset = game->map->enemies.size();
game->map->add_enemies_from_map_data(
game->episode, game->difficulty, game->event, map_data->data(), map_data->size());
size_t entries_loaded = game->map->enemies.size() - start_offset;
c->log.info("[Map/%zu] Loaded %s (%zu entries)",
area, filename.c_str(), entries_loaded);
for (size_t z = start_offset; z < game->map->enemies.size(); z++) {
string e_str = game->map->enemies[z].str();
static_game_data_log.info("(Entry %zX) %s", z, e_str.c_str());
game->variations[area * 2 + 1],
true);
if (enemy_filenames.empty()) {
c->log.info("[Map/%zu:e] No file to load", area);
} else {
bool any_map_loaded = false;
for (const string& filename : enemy_filenames) {
try {
auto map_data = s->load_bb_file(filename, "", "map/" + filename);
size_t start_offset = game->map->enemies.size();
game->map->add_enemies_from_map_data(
game->episode, game->difficulty, game->event, map_data->data(), map_data->size());
size_t entries_loaded = game->map->enemies.size() - start_offset;
c->log.info("[Map/%zu:e] Loaded %s (%zu entries)",
area, filename.c_str(), entries_loaded);
for (size_t z = start_offset; z < game->map->enemies.size(); z++) {
string e_str = game->map->enemies[z].str();
static_game_data_log.info("(Entry %zX) %s", z, e_str.c_str());
}
any_map_loaded = true;
break;
} catch (const exception& e) {
c->log.info("[Map/%zu:e] Failed to load %s: %s", area, filename.c_str(), e.what());
}
any_map_loaded = true;
break;
} catch (const exception& e) {
c->log.info("[Map/%zu] Failed to load %s: %s", area, filename.c_str(), e.what());
}
}
if (!any_map_loaded) {
throw runtime_error(string_printf("no maps loaded for area %zu", area));
if (!any_map_loaded) {
throw runtime_error(string_printf("no enemy maps loaded for area %zu", area));
}
}
}
c->log.info("Loaded maps contain %zu entries overall (%zu as rares)",
c->log.info("Loaded maps contain %zu enemy entries overall (%zu as rares)",
game->map->enemies.size(), game->map->rare_enemy_indexes.size());
}
return game;
+21 -8
View File
@@ -1320,12 +1320,12 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
if (size == sizeof(G_SpecializableItemDropRequest_6xA2)) {
cmd = check_size_t<G_SpecializableItemDropRequest_6xA2>(data, size);
if (cmd.header.subcommand != 0xA2) {
throw runtime_error("item drop request has specializable size but non-specializable command");
throw runtime_error("item drop request has incorrect subcommand");
}
} else {
const auto& in_cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(data, size, 0xFFFF);
} else if (size == sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60)) {
const auto& in_cmd = check_size_t<G_StandardDropItemRequest_PC_V3_BB_6x60>(data, size);
if (in_cmd.header.subcommand != 0x60) {
throw runtime_error("item drop request has non-specializable size but specializable command");
throw runtime_error("item drop request has incorrect subcommand");
}
cmd.entity_id = in_cmd.entity_id;
cmd.area = in_cmd.area;
@@ -1333,12 +1333,25 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
cmd.x = in_cmd.x;
cmd.z = in_cmd.z;
cmd.ignore_def = true;
cmd.effective_area = in_cmd.effective_area;
} else {
const auto& in_cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(data, size);
if (in_cmd.header.subcommand != 0x60) {
throw runtime_error("item drop request has incorrect subcommand");
}
cmd.entity_id = in_cmd.entity_id;
cmd.area = in_cmd.area;
cmd.rt_index = in_cmd.rt_index;
cmd.x = in_cmd.x;
cmd.z = in_cmd.z;
cmd.ignore_def = true;
cmd.effective_area = in_cmd.area;
}
ItemData item;
if (cmd.rt_index == 0x30) {
if (cmd.ignore_def) {
item = l->item_creator->on_box_item_drop(cmd.entity_id, cmd.area);
item = l->item_creator->on_box_item_drop(cmd.entity_id, cmd.effective_area);
} else {
item = l->item_creator->on_specialized_box_item_drop(cmd.entity_id, cmd.def[0], cmd.def[1], cmd.def[2]);
}
@@ -1351,7 +1364,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
cmd.rt_index, expected_rt_index);
}
}
item = l->item_creator->on_monster_item_drop(cmd.entity_id, cmd.rt_index, cmd.area);
item = l->item_creator->on_monster_item_drop(cmd.entity_id, cmd.rt_index, cmd.effective_area);
}
item.id = l->generate_item_id(0xFF);
@@ -1429,8 +1442,8 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
2,
0,
},
0xE0AEDC01,
};
0x01,
{}};
send_command_t(c, 0x62, l->leader_id, req);
}
}