track specialized boxes server-side
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user