refine ObjectEntry structure
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user