From ea83935dc414998237c78a68da94ba9fcd2f27b0 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 26 Nov 2023 16:21:09 -0800 Subject: [PATCH] implement BB EXP requests properly --- src/CommandFormats.hh | 12 ++++---- src/Map.cc | 4 +-- src/Map.hh | 13 ++++---- src/ReceiveSubcommands.cc | 64 +++++++++++++++++---------------------- 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index e5ed2bb9..be90e8a4 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4199,8 +4199,8 @@ struct G_AttackFinished_6x46 { le_uint16_t unknown_a1 = 0; le_uint16_t unknown_a2 = 0; } __packed__; - // The client mauy send a shorter command if not all of these are used. - parray targets; + // The client may send a shorter command if not all of these are used. + parray targets; } __packed__; // 6x47: Cast technique @@ -5574,13 +5574,13 @@ struct G_ChargeAttack_BB_6xC7 { le_uint32_t meseta_amount = 0; } __packed__; -// 6xC8: Enemy killed (BB; handled by the server) +// 6xC8: Enemy EXP request (BB; handled by the server) -struct G_EnemyKilled_BB_6xC8 { +struct G_EnemyEXPRequest_BB_6xC8 { G_EnemyIDHeader header; le_uint16_t enemy_index = 0; - le_uint16_t killer_client_id = 0; - uint8_t unknown_a1 = 0; + le_uint16_t requesting_client_id = 0; + uint8_t is_killer = 0; parray unused; } __packed__; diff --git a/src/Map.cc b/src/Map.cc index 3c70a05a..c6132f1b 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -71,13 +71,13 @@ Map::Enemy::Enemy(size_t source_index, uint8_t floor, EnemyType type) : source_index(source_index), type(type), floor(floor), - flags(0), + state_flags(0), last_hit_by_client_id(0) { } string Map::Enemy::str() const { return string_printf("[Map::Enemy source %zX %s flags=%02hhX last_hit_by_client_id=%hu]", - this->source_index, name_for_enum(this->type), this->flags, this->last_hit_by_client_id); + this->source_index, name_for_enum(this->type), this->state_flags, this->last_hit_by_client_id); } string Map::Object::str(shared_ptr name_index) const { diff --git a/src/Map.hh b/src/Map.hh index a97bd649..16872d37 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -216,17 +216,16 @@ struct Map { struct Enemy { enum Flag { - HIT_BY_PLAYER0 = 0x01, - HIT_BY_PLAYER1 = 0x02, - HIT_BY_PLAYER2 = 0x04, - HIT_BY_PLAYER3 = 0x08, - DEFEATED = 0x10, - ITEM_DROPPED = 0x20, + EXP_REQUESTED_BY_PLAYER0 = 0x01, + EXP_REQUESTED_BY_PLAYER1 = 0x02, + EXP_REQUESTED_BY_PLAYER2 = 0x04, + EXP_REQUESTED_BY_PLAYER3 = 0x08, + ITEM_DROPPED = 0x10, }; size_t source_index; EnemyType type; uint8_t floor; - uint8_t flags; + uint8_t state_flags; uint8_t last_hit_by_client_id; Enemy(size_t source_index, uint8_t floor, EnemyType type); diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 2da4bc63..9be96da4 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1815,10 +1815,6 @@ static void on_enemy_hit(shared_ptr c, uint8_t command, uint8_t, const v } auto& enemy = l->map->enemies[cmd.enemy_index]; - if (enemy.flags & Map::Enemy::Flag::DEFEATED) { - return; - } - enemy.flags |= (Map::Enemy::Flag::HIT_BY_PLAYER0 << c->lobby_client_id); enemy.last_hit_by_client_id = c->lobby_client_id; } @@ -1945,7 +1941,7 @@ static void on_steal_exp_bb(shared_ptr c, uint8_t, uint8_t, const void* add_player_exp(c, stolen_exp); } -static void on_enemy_killed_bb(shared_ptr c, uint8_t, uint8_t, const void* data, size_t size) { +static void on_enemy_exp_request_bb(shared_ptr c, uint8_t, uint8_t, const void* data, size_t size) { auto s = c->require_server_state(); auto l = c->require_lobby(); @@ -1953,7 +1949,7 @@ static void on_enemy_killed_bb(shared_ptr c, uint8_t, uint8_t, const voi throw runtime_error("BB-only command sent in non-BB game"); } - const auto& cmd = check_size_t(data, size); + const auto& cmd = check_size_t(data, size); if (!l->is_game()) { throw runtime_error("client should not kill enemies outside of games"); @@ -1965,16 +1961,19 @@ static void on_enemy_killed_bb(shared_ptr c, uint8_t, uint8_t, const voi send_text_message(c, "$C6Missing enemy killed"); return; } + if (c->lobby_client_id > 3) { + throw runtime_error("client ID is too large"); + } auto& e = l->map->enemies[cmd.enemy_index]; string e_str = e.str(); - c->log.info("Enemy killed: E-%hX => %s", cmd.enemy_index.load(), e_str.c_str()); - if (e.flags & Map::Enemy::Flag::DEFEATED) { - if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { - send_text_message_printf(c, "$C5E-%hX __DEFEATED__", cmd.enemy_index.load()); - } - return; + c->log.info("EXP requested for E-%hX => %s", cmd.enemy_index.load(), e_str.c_str()); + + uint8_t state_flag = Map::Enemy::Flag::EXP_REQUESTED_BY_PLAYER0 << c->lobby_client_id; + if (e.state_flags & state_flag) { + throw runtime_error("duplicate EXP request"); } + e.state_flags |= state_flag; double experience = 0.0; try { @@ -1989,44 +1988,35 @@ static void on_enemy_killed_bb(shared_ptr c, uint8_t, uint8_t, const voi } } - e.flags |= Map::Enemy::Flag::DEFEATED; if (experience != 0.0) { - for (size_t x = 0; x < l->max_clients; x++) { - auto lc = l->clients[x]; - if (!lc) { - continue; + if (c->floor != e.floor) { + if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + send_text_message_printf(c, "$C5E-%hX %s\n$C4FLOOR Y:%02" PRIX32 " E:%02hhX", + cmd.enemy_index.load(), name_for_enum(e.type), c->floor, e.floor); } - if (!((e.flags >> x) & 1)) { - if (lc->config.check_flag(Client::Flag::DEBUG_ENABLED)) { - send_text_message_printf(lc, "$C5E-%hX %s\n$C4NOHIT", cmd.enemy_index.load(), name_for_enum(e.type)); - } - continue; // Player did not hit this enemy - } - if (lc->floor != e.floor) { - if (lc->config.check_flag(Client::Flag::DEBUG_ENABLED)) { - send_text_message_printf(lc, "$C5E-%hX %s\n$C4FLOOR Y:%02" PRIX32 " E:%02hhX", - cmd.enemy_index.load(), name_for_enum(e.type), lc->floor, e.floor); - } - continue; - } - + } else { // In PSOBB, Sega decided to add a 30% EXP boost for Episode 2. They could // have done something reasonable, like edit the BattleParamEntry files so // the monsters would all give more EXP, but they did something far lazier // instead: they just stuck an if statement in the client's EXP request // function. We, unfortunately, have to do the same here. - bool is_killer = (e.last_hit_by_client_id == lc->lobby_client_id); + bool is_killer = (e.last_hit_by_client_id == c->lobby_client_id); bool is_ep2 = (l->episode == Episode::EP2); uint32_t player_exp = experience * (is_killer ? 1.0 : 0.8) * l->base_exp_multiplier * l->challenge_exp_multiplier * (is_ep2 ? 1.3 : 1.0); - if (lc->config.check_flag(Client::Flag::DEBUG_ENABLED)) { - send_text_message_printf(lc, "$C5+%" PRIu32 " E-%hX %s", player_exp, cmd.enemy_index.load(), name_for_enum(e.type)); + if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + send_text_message_printf( + c, "$C5+%" PRIu32 " E-%hX %s%s", + player_exp, + cmd.enemy_index.load(), + (!cmd.is_killer == !is_killer) ? "" : "$C6!K$C5 ", + name_for_enum(e.type)); } - if (lc->game_data.character()->disp.stats.level < 199) { - add_player_exp(lc, player_exp); + if (c->game_data.character()->disp.stats.level < 199) { + add_player_exp(c, player_exp); } } } @@ -2963,7 +2953,7 @@ subcommand_handler_t subcommand_handlers[0x100] = { /* 6xC5 */ on_medical_center_bb, /* 6xC6 */ on_steal_exp_bb, /* 6xC7 */ on_charge_attack_bb, - /* 6xC8 */ on_enemy_killed_bb, + /* 6xC8 */ on_enemy_exp_request_bb, /* 6xC9 */ on_meseta_reward_request_bb, /* 6xCA */ on_item_reward_request_bb, /* 6xCB */ nullptr,