load maps on all versions

This commit is contained in:
Martin Michelsen
2024-01-03 00:22:28 -08:00
parent 78e407a70f
commit df29a60a6e
9 changed files with 968 additions and 266 deletions
+164 -120
View File
@@ -2097,7 +2097,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
cmd.rt_index = in_cmd.rt_index;
cmd.x = in_cmd.x;
cmd.z = in_cmd.z;
cmd.ignore_def = 1;
cmd.ignore_def = in_cmd.ignore_def;
cmd.effective_area = in_cmd.effective_area;
} else {
const auto& in_cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(data, size);
@@ -2109,83 +2109,154 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
cmd.rt_index = in_cmd.rt_index;
cmd.x = in_cmd.x;
cmd.z = in_cmd.z;
cmd.ignore_def = 1;
cmd.ignore_def = in_cmd.ignore_def;
cmd.effective_area = in_cmd.floor;
}
auto generate_item = [&]() -> ItemCreator::DropResult {
if (cmd.rt_index == 0x30) {
if (l->map) {
auto& object = l->map->objects.at(cmd.entity_id);
Map::Object* map_object = nullptr;
Map::Enemy* map_enemy = nullptr;
bool ignore_def = (cmd.ignore_def != 0);
uint8_t effective_rt_index = 0xFF;
if (cmd.rt_index == 0x30) {
if (l->map) {
map_object = &l->map->objects.at(cmd.entity_id);
l->log.info("Drop check for K-%hX %c %s",
map_object->object_id, ignore_def ? 'G' : 'S', Map::name_for_object_type(map_object->base_type));
if (cmd.floor != map_object->floor) {
l->log.warning("Floor %02hhX from command does not match object\'s expected floor %02hhX", cmd.floor, map_object->floor);
}
if (is_v1_or_v2(l->base_version) && (l->base_version != Version::GC_NTE)) {
// V1 and V2 don't have 6xA2, so we can't get ignore_def or the object
// parameters from the client on those versions
cmd.param3 = map_object->param3;
cmd.param4 = map_object->param4;
cmd.param5 = map_object->param5;
cmd.param6 = map_object->param6;
}
bool object_ignore_def = (map_object->param1 > 0.0);
if (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)",
ignore_def ? "true" : "false", object_ignore_def ? "true" : "false", map_object->param1);
}
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
send_text_message_printf(c, "$C5K-%hX %c %s",
map_object->object_id, ignore_def ? 'G' : 'S', Map::name_for_object_type(map_object->base_type));
}
}
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);
} else {
if (l->map) {
map_enemy = &l->map->enemies.at(cmd.entity_id);
l->log.info("Drop check for E-%hX %s", map_enemy->enemy_id, name_for_enum(map_enemy->type));
effective_rt_index = rare_table_index_for_enemy_type(map_enemy->type);
// rt_indexes in Episode 4 don't match those sent in the command; we just
// ignore what the client sends.
if ((l->episode != Episode::EP4) && (cmd.rt_index != effective_rt_index)) {
l->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
cmd.rt_index, effective_rt_index);
if (!is_v4(l->base_version)) {
effective_rt_index = cmd.rt_index;
}
}
if (cmd.floor != map_enemy->floor) {
l->log.warning("Floor %02hhX from command does not match entity\'s expected floor %02hhX",
cmd.floor, map_enemy->floor);
}
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
send_text_message_printf(c, "$C5E-%hX %s", map_enemy->enemy_id, name_for_enum(map_enemy->type));
}
}
}
if (cmd.ignore_def) {
bool should_drop = true;
if (map_object) {
if (map_object->item_drop_checked) {
l->log.info("Drop check has already occurred for K-%04hX; skipping it", map_object->object_id);
should_drop = false;
} else {
map_object->item_drop_checked = true;
}
}
if (map_enemy) {
if (map_enemy->state_flags & Map::Enemy::Flag::ITEM_DROPPED) {
l->log.info("Drop check has already occurred for E-%04hX; skipping it", map_enemy->enemy_id);
should_drop = false;
} else {
map_enemy->state_flags |= Map::Enemy::Flag::ITEM_DROPPED;
}
}
if (should_drop) {
auto generate_item = [&]() -> ItemCreator::DropResult {
if (cmd.rt_index == 0x30) {
if (ignore_def) {
l->log.info("Creating item from box %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
return l->item_creator->on_box_item_drop(cmd.entity_id, cmd.effective_area);
return l->item_creator->on_box_item_drop(cmd.effective_area);
} else {
l->log.info("Creating item from box %04hX (area %02hX; specialized with %g %08" PRIX32 " %08" PRIX32 " %08" PRIX32 ")",
cmd.entity_id.load(), cmd.effective_area, object.param3, object.param4, object.param5, object.param6);
return l->item_creator->on_specialized_box_item_drop(cmd.entity_id, cmd.effective_area, object.param3, object.param4, object.param5, object.param6);
cmd.entity_id.load(), cmd.effective_area, cmd.param3.load(), cmd.param4.load(), cmd.param5.load(), cmd.param6.load());
return l->item_creator->on_specialized_box_item_drop(cmd.effective_area, cmd.param3, cmd.param4, cmd.param5, cmd.param6);
}
} else if (cmd.ignore_def) {
l->log.info("Creating item from box %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
return l->item_creator->on_box_item_drop(cmd.entity_id, cmd.effective_area);
} else {
l->log.info("Creating item from box %04hX (area %02hX; specialized with %g %08" PRIX32 " %08" PRIX32 " %08" PRIX32 ")",
cmd.entity_id.load(), cmd.effective_area, cmd.param3.load(), cmd.param4.load(), cmd.param5.load(), cmd.param6.load());
return l->item_creator->on_specialized_box_item_drop(
cmd.entity_id, cmd.effective_area, cmd.param3, cmd.param4, cmd.param5, cmd.param6);
l->log.info("Creating item from enemy %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
return l->item_creator->on_monster_item_drop(effective_rt_index, cmd.effective_area);
}
};
} else {
l->log.info("Creating item from enemy %04hX (area %02hX)", cmd.entity_id.load(), cmd.effective_area);
switch (l->drop_mode) {
case Lobby::DropMode::DISABLED:
case Lobby::DropMode::CLIENT:
throw logic_error("unhandled simple drop mode");
case Lobby::DropMode::SERVER_SHARED:
case Lobby::DropMode::SERVER_DUPLICATE: {
// TODO: In SERVER_DUPLICATE mode, should we reduce the rates for rare
// items? Maybe by a factor of l->count_clients()?
auto res = generate_item();
if (res.item.empty()) {
l->log.info("No item was created");
} else {
string name = s->describe_item(l->base_version, res.item, false);
l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str());
if (l->drop_mode == Lobby::DropMode::SERVER_DUPLICATE) {
for (const auto& lc : l->clients) {
if (lc && ((cmd.rt_index == 0x30) || (lc->floor == cmd.floor))) {
res.item.id = l->generate_item_id(0xFF);
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s",
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str());
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, (1 << lc->lobby_client_id));
send_drop_item_to_channel(s, lc->channel, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
if (res.is_from_rare_table) {
send_rare_notification_if_needed(lc, res.item);
}
}
}
uint8_t effective_rt_index = cmd.rt_index;
if (l->map) {
const auto& enemy = l->map->enemies.at(cmd.entity_id);
effective_rt_index = rare_table_index_for_enemy_type(enemy.type);
// rt_indexes in Episode 4 don't match those sent in the command; we just
// ignore what the client sends.
if ((l->episode != Episode::EP4) && (cmd.rt_index != effective_rt_index)) {
l->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
cmd.rt_index, effective_rt_index);
}
if (cmd.floor != enemy.floor) {
l->log.warning("Floor %02hhX from command does not match entity\'s expected floor %02hhX",
cmd.floor, enemy.floor);
} else {
res.item.id = l->generate_item_id(0xFF);
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for all clients",
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load());
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x00F);
send_drop_item_to_lobby(l, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
if (res.is_from_rare_table) {
for (auto lc : l->clients) {
if (lc) {
send_rare_notification_if_needed(lc, res.item);
}
}
}
}
}
break;
}
return l->item_creator->on_monster_item_drop(cmd.entity_id, effective_rt_index, cmd.effective_area);
}
};
switch (l->drop_mode) {
case Lobby::DropMode::DISABLED:
case Lobby::DropMode::CLIENT:
throw logic_error("unhandled simple drop mode");
case Lobby::DropMode::SERVER_SHARED:
case Lobby::DropMode::SERVER_DUPLICATE: {
// TODO: In SERVER_DUPLICATE mode, should we reduce the rates for rare
// items? Maybe by a factor of l->count_clients()?
auto res = generate_item();
if (res.item.empty()) {
l->log.info("No item was created");
} else {
string name = s->describe_item(l->base_version, res.item, false);
l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str());
if (l->drop_mode == Lobby::DropMode::SERVER_DUPLICATE) {
for (const auto& lc : l->clients) {
if (lc && ((cmd.rt_index == 0x30) || (lc->floor == cmd.floor))) {
case Lobby::DropMode::SERVER_PRIVATE: {
for (const auto& lc : l->clients) {
if (lc && ((cmd.rt_index == 0x30) || (lc->floor == cmd.floor))) {
auto res = generate_item();
if (res.item.empty()) {
l->log.info("No item was created for %s", lc->channel.name.c_str());
} else {
string name = s->describe_item(l->base_version, res.item, false);
l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str());
res.item.id = l->generate_item_id(0xFF);
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s",
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str());
@@ -2196,54 +2267,12 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
}
}
}
} else {
res.item.id = l->generate_item_id(0xFF);
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for all clients",
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load());
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x00F);
send_drop_item_to_lobby(l, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
if (res.is_from_rare_table) {
for (auto lc : l->clients) {
if (lc) {
send_rare_notification_if_needed(lc, res.item);
}
}
}
}
break;
}
break;
default:
throw logic_error("invalid drop mode");
}
case Lobby::DropMode::SERVER_PRIVATE: {
for (const auto& lc : l->clients) {
if (lc && ((cmd.rt_index == 0x30) || (lc->floor == cmd.floor))) {
auto res = generate_item();
if (res.item.empty()) {
l->log.info("No item was created for %s", lc->channel.name.c_str());
} else {
string name = s->describe_item(l->base_version, res.item, false);
l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str());
res.item.id = l->generate_item_id(0xFF);
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s",
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str());
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, (1 << lc->lobby_client_id));
send_drop_item_to_channel(s, lc->channel, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
if (res.is_from_rare_table) {
send_rare_notification_if_needed(lc, res.item);
}
}
}
}
break;
}
default:
throw logic_error("invalid drop mode");
}
if (cmd.rt_index == 0x30) {
l->item_creator->set_box_destroyed(cmd.entity_id);
} else {
l->item_creator->set_monster_destroyed(cmd.entity_id);
}
}
@@ -2290,30 +2319,45 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
forward_subcommand(c, command, flag, data, size);
if (is_v3(c->version())) {
bool should_send_boss_drop_req = false;
if (is_v3(c->version()) && (l->drop_mode != Lobby::DropMode::DISABLED)) {
EnemyType boss_enemy_type = EnemyType::NONE;
bool is_ep2 = (l->episode == Episode::EP2);
if ((l->episode == Episode::EP1) && (c->floor == 0x0E)) {
// On Normal, Dark Falz does not have a third phase, so send the drop
// request after the end of the second phase. On all other difficulty
// levels, send it after the third phase.
if (((difficulty == 0) && (flag_index == 0x0035)) ||
((difficulty != 0) && (flag_index == 0x0037))) {
should_send_boss_drop_req = true;
if ((difficulty == 0) && (flag_index == 0x0035)) {
boss_enemy_type = EnemyType::DARK_FALZ_2;
} else if ((difficulty != 0) && (flag_index == 0x0037)) {
boss_enemy_type = EnemyType::DARK_FALZ_3;
}
} else if (is_ep2 && (flag_index == 0x0057) && (c->floor == 0x0D)) {
should_send_boss_drop_req = true;
boss_enemy_type = EnemyType::OLGA_FLOW_2;
}
if (should_send_boss_drop_req) {
auto c = l->clients.at(l->leader_id);
if (c) {
G_StandardDropItemRequest_PC_V3_BB_6x60 req = {
if (boss_enemy_type != EnemyType::NONE) {
l->log.info("Creating item from final boss (%s)", name_for_enum(boss_enemy_type));
uint16_t enemy_id = 0xFFFF;
if (l->map) {
try {
const auto& enemy = l->map->find_enemy(c->floor, boss_enemy_type);
enemy_id = enemy.enemy_id;
if (c->floor != enemy.floor) {
l->log.warning("Floor %02" PRIX32 " from client does not match entity\'s expected floor %02hhX", c->floor, enemy.floor);
}
l->log.info("Found enemy E-%hX on floor %" PRIX32, enemy_id, enemy.floor);
} catch (const out_of_range&) {
l->log.warning("Could not find enemy on floor %" PRIX32 "; unable to determine enemy type", c->floor);
}
}
if (boss_enemy_type != EnemyType::NONE) {
G_StandardDropItemRequest_PC_V3_BB_6x60 drop_req = {
{
{0x60, 0x06, 0x0000},
static_cast<uint8_t>(c->floor),
static_cast<uint8_t>(is_ep2 ? 0x4E : 0x2F),
0x0B4F,
rare_table_index_for_enemy_type(boss_enemy_type),
enemy_id == 0xFFFF ? 0x0B4F : enemy_id,
is_ep2 ? -9999.0f : 10160.58984375f,
0.0f,
2,
@@ -2321,7 +2365,7 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
},
0x01,
{}};
send_command_t(c, 0x62, l->leader_id, req);
on_entity_drop_item_request(c, 0x62, l->leader_id, &drop_req, sizeof(drop_req));
}
}
}