track specialized boxes server-side

This commit is contained in:
Martin Michelsen
2023-11-13 22:07:21 -08:00
parent 4b4627d3e5
commit 4fe238a01a
161 changed files with 119 additions and 17 deletions
+1 -1
View File
@@ -4388,7 +4388,7 @@ struct G_StandardDropItemRequest_DC_6x60 {
le_uint16_t entity_id = 0;
le_float x = 0.0f;
le_float z = 0.0f;
le_uint16_t unknown_a1 = 0;
le_uint16_t section = 0;
le_uint16_t ignore_def = 0;
} __packed__;
+21 -6
View File
@@ -25,21 +25,36 @@ string Map::Enemy::str() const {
name_for_enum(this->type), this->flags, this->last_hit_by_client_id);
}
string Map::Object::str() const {
return string_printf("[Map::Object %04hX @%04hX p1=%g (%s) p456=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "] floor=%02hhX item_drop_checked=%s]",
this->base_type, this->section, this->param1, (this->param1 <= 0.0f) ? "specialized" : "generic",
this->param4, this->param5, this->param6, this->floor, this->item_drop_checked ? "true" : "false");
}
void Map::clear() {
this->enemies.clear();
this->rare_enemy_indexes.clear();
}
void Map::add_objects_from_map_data(const void* data, size_t size) {
void Map::add_objects_from_map_data(uint8_t floor, const void* data, size_t size) {
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");
}
(void)data;
// TODO: Actually track objects, so we can e.g. know what to drop from fixed
// boxes
// const auto* map = reinterpret_cast<const ObjectEntry*>(data);
const auto* objects = reinterpret_cast<const ObjectEntry*>(data);
for (size_t z = 0; z < entry_count; z++) {
this->objects.emplace_back(Object{
.base_type = objects[z].base_type,
.section = objects[z].section,
.param1 = objects[z].param1,
.param4 = objects[z].param4,
.param5 = objects[z].param5,
.param6 = objects[z].param6,
.floor = floor,
.item_drop_checked = false,
});
}
}
bool Map::check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate) {
@@ -734,7 +749,7 @@ void Map::add_enemies_and_objects_from_quest_data(
throw runtime_error("quest layout object section size is not a multiple of object entry size");
}
static_game_data_log.info("(Floor %02zX) Adding objects", floor);
this->add_objects_from_map_data(r.pgetv(sections.objects + sizeof(header), header.data_size), header.data_size);
this->add_objects_from_map_data(floor, r.pgetv(sections.objects + sizeof(header), header.data_size), header.data_size);
}
if (sections.enemies != 0xFFFFFFFF) {
+21 -2
View File
@@ -48,7 +48,7 @@ struct Map {
/* 1C */ le_uint32_t x_angle;
/* 20 */ le_uint32_t y_angle;
/* 24 */ le_uint32_t z_angle;
/* 28 */ le_float param1;
/* 28 */ le_float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
/* 2C */ le_float param2;
/* 30 */ le_float param3;
/* 34 */ le_uint32_t param4;
@@ -192,6 +192,21 @@ struct Map {
static const RareEnemyRates NO_RARE_ENEMIES;
static const RareEnemyRates DEFAULT_RARE_ENEMIES;
struct Object {
// TODO: Add more fields in here if we ever care about them. Currently we
// only care about boxes with fixed item drops.
uint16_t base_type;
uint16_t section;
float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
uint32_t param4;
uint32_t param5;
uint32_t param6;
uint8_t floor;
bool item_drop_checked;
std::string str() const;
};
struct Enemy {
enum Flag {
HIT_BY_PLAYER0 = 0x01,
@@ -211,11 +226,14 @@ struct Map {
std::string str() const;
} __attribute__((packed));
std::vector<Object> objects;
std::vector<Enemy> enemies;
std::vector<size_t> rare_enemy_indexes;
void clear();
void add_objects_from_map_data(const void* data, size_t size);
void add_objects_from_map_data(uint8_t floor, const void* data, size_t size);
bool check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate);
void add_enemy(uint8_t floor, EnemyType type);
void add_enemy(
@@ -244,6 +262,7 @@ struct Map {
StringReader random_enemy_definitions_r,
uint32_t rare_seed,
const RareEnemyRates& rare_rates = Map::DEFAULT_RARE_ENEMIES);
void add_enemies_and_objects_from_quest_data(
Episode episode,
uint8_t difficulty,
+37 -5
View File
@@ -3742,11 +3742,10 @@ shared_ptr<Lobby> create_game_generic(
game->map->add_enemies_from_map_data(
game->episode, game->difficulty, game->event, floor, 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)",
floor, filename.c_str(), entries_loaded);
c->log.info("[Map/%zu:e] Loaded %s (%zu entries)", floor, 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());
static_game_data_log.info("(E-%zX) %s", z, e_str.c_str());
}
any_map_loaded = true;
break;
@@ -3758,10 +3757,43 @@ shared_ptr<Lobby> create_game_generic(
throw runtime_error(string_printf("no enemy maps loaded for floor %zu", floor));
}
}
auto object_filenames = map_filenames_for_variation(
game->episode,
is_solo,
floor,
game->variations[floor * 2],
game->variations[floor * 2 + 1],
false);
if (object_filenames.empty()) {
c->log.info("[Map/%zu:o] No file to load", floor);
} else {
bool any_map_loaded = false;
for (const string& filename : object_filenames) {
try {
auto map_data = s->load_bb_file(filename, "", "map/" + filename);
size_t start_offset = game->map->objects.size();
game->map->add_objects_from_map_data(floor, map_data->data(), map_data->size());
size_t entries_loaded = game->map->objects.size() - start_offset;
c->log.info("[Map/%zu:o] Loaded %s (%zu entries)", floor, filename.c_str(), entries_loaded);
for (size_t z = start_offset; z < game->map->objects.size(); z++) {
string e_str = game->map->objects[z].str();
static_game_data_log.info("(K-%zX) %s", z, e_str.c_str());
}
any_map_loaded = true;
break;
} catch (const exception& e) {
c->log.info("[Map/%zu:o] Failed to load %s: %s", floor, filename.c_str(), e.what());
}
}
if (!any_map_loaded) {
throw runtime_error(string_printf("no object maps loaded for floor %zu", floor));
}
}
}
c->log.info("Loaded maps contain %zu enemy entries overall (%zu as rares)",
game->map->enemies.size(), game->map->rare_enemy_indexes.size());
c->log.info("Loaded maps contain %zu object entries and %zu enemy entries overall (%zu as rares)",
game->map->objects.size(), game->map->enemies.size(), game->map->rare_enemy_indexes.size());
}
return game;
}
+39 -3
View File
@@ -1436,7 +1436,42 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
ItemData item;
if (cmd.rt_index == 0x30) {
if (cmd.ignore_def) {
if (l->map) {
auto& object = l->map->objects.at(cmd.entity_id);
if (cmd.floor != object.floor) {
l->log.warning("Floor %02hhX from command does not match object\'s expected floor %02hhX",
cmd.floor, object.floor);
}
bool object_ignore_def = (object.param1 > 0.0);
if (cmd.ignore_def != object_ignore_def) {
l->log.warning("ignore_def value %s from command does not match object\'s expected ignore_def %s (from p1=%g)",
cmd.ignore_def ? "true" : "false", object_ignore_def ? "true" : "false", object.param1);
}
if (object.item_drop_checked) {
l->log.warning("Object drop check has already occurred");
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
send_text_message_printf(c, "$C5K-%hX %04hX __CHECKED__", cmd.entity_id.load(), object.base_type);
}
} else if (object_ignore_def) {
l->log.info("Creating item from box %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
item = l->item_creator->on_box_item_drop(cmd.entity_id, cmd.effective_area);
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
send_text_message_printf(c, "$C5K-%hX %04hX GEN %s", cmd.entity_id.load(), object.base_type, item.empty() ? "EMPTY" : "ITEM");
}
} else {
l->log.info("Creating item from box %04hX (area %02hX; specialized with %08" PRIX32 " %08" PRIX32 " %08" PRIX32 ")",
cmd.entity_id.load(), cmd.effective_area, object.param4, object.param5, object.param6);
item = l->item_creator->on_specialized_box_item_drop(cmd.entity_id, object.param4, object.param5, object.param6);
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
send_text_message_printf(c, "$C5K-%hX %04hX CST %s", cmd.entity_id.load(), object.base_type, item.empty() ? "EMPTY" : "ITEM");
}
}
object.item_drop_checked = true;
} else if (cmd.ignore_def) {
l->log.info("Creating item from box %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
item = l->item_creator->on_box_item_drop(cmd.entity_id, cmd.effective_area);
} else {
@@ -1444,17 +1479,18 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
cmd.entity_id.load(), cmd.effective_area, cmd.def[0].load(), cmd.def[1].load(), cmd.def[2].load());
item = l->item_creator->on_specialized_box_item_drop(cmd.entity_id, cmd.def[0], cmd.def[1], cmd.def[2]);
}
} else {
l->log.info("Creating item from enemy %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
if (l->map) {
const auto& enemy = l->map->enemies.at(cmd.entity_id);
uint32_t expected_rt_index = rare_table_index_for_enemy_type(enemy.type);
if (cmd.rt_index != expected_rt_index) {
c->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
l->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
cmd.rt_index, expected_rt_index);
}
if (cmd.floor != enemy.floor) {
c->log.warning("Floor %02hhX from command does not match entity\'s expected floor %02hhX",
l->log.warning("Floor %02hhX from command does not match entity\'s expected floor %02hhX",
cmd.floor, enemy.floor);
}
}