implement BB EXP requests properly
This commit is contained in:
@@ -4199,8 +4199,8 @@ struct G_AttackFinished_6x46 {
|
|||||||
le_uint16_t unknown_a1 = 0;
|
le_uint16_t unknown_a1 = 0;
|
||||||
le_uint16_t unknown_a2 = 0;
|
le_uint16_t unknown_a2 = 0;
|
||||||
} __packed__;
|
} __packed__;
|
||||||
// The client mauy send a shorter command if not all of these are used.
|
// The client may send a shorter command if not all of these are used.
|
||||||
parray<TargetEntry, 11> targets;
|
parray<TargetEntry, 10> targets;
|
||||||
} __packed__;
|
} __packed__;
|
||||||
|
|
||||||
// 6x47: Cast technique
|
// 6x47: Cast technique
|
||||||
@@ -5574,13 +5574,13 @@ struct G_ChargeAttack_BB_6xC7 {
|
|||||||
le_uint32_t meseta_amount = 0;
|
le_uint32_t meseta_amount = 0;
|
||||||
} __packed__;
|
} __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;
|
G_EnemyIDHeader header;
|
||||||
le_uint16_t enemy_index = 0;
|
le_uint16_t enemy_index = 0;
|
||||||
le_uint16_t killer_client_id = 0;
|
le_uint16_t requesting_client_id = 0;
|
||||||
uint8_t unknown_a1 = 0;
|
uint8_t is_killer = 0;
|
||||||
parray<uint8_t, 3> unused;
|
parray<uint8_t, 3> unused;
|
||||||
} __packed__;
|
} __packed__;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -71,13 +71,13 @@ Map::Enemy::Enemy(size_t source_index, uint8_t floor, EnemyType type)
|
|||||||
: source_index(source_index),
|
: source_index(source_index),
|
||||||
type(type),
|
type(type),
|
||||||
floor(floor),
|
floor(floor),
|
||||||
flags(0),
|
state_flags(0),
|
||||||
last_hit_by_client_id(0) {
|
last_hit_by_client_id(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
string Map::Enemy::str() const {
|
string Map::Enemy::str() const {
|
||||||
return string_printf("[Map::Enemy source %zX %s flags=%02hhX last_hit_by_client_id=%hu]",
|
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<const ItemNameIndex> name_index) const {
|
string Map::Object::str(shared_ptr<const ItemNameIndex> name_index) const {
|
||||||
|
|||||||
+6
-7
@@ -216,17 +216,16 @@ struct Map {
|
|||||||
|
|
||||||
struct Enemy {
|
struct Enemy {
|
||||||
enum Flag {
|
enum Flag {
|
||||||
HIT_BY_PLAYER0 = 0x01,
|
EXP_REQUESTED_BY_PLAYER0 = 0x01,
|
||||||
HIT_BY_PLAYER1 = 0x02,
|
EXP_REQUESTED_BY_PLAYER1 = 0x02,
|
||||||
HIT_BY_PLAYER2 = 0x04,
|
EXP_REQUESTED_BY_PLAYER2 = 0x04,
|
||||||
HIT_BY_PLAYER3 = 0x08,
|
EXP_REQUESTED_BY_PLAYER3 = 0x08,
|
||||||
DEFEATED = 0x10,
|
ITEM_DROPPED = 0x10,
|
||||||
ITEM_DROPPED = 0x20,
|
|
||||||
};
|
};
|
||||||
size_t source_index;
|
size_t source_index;
|
||||||
EnemyType type;
|
EnemyType type;
|
||||||
uint8_t floor;
|
uint8_t floor;
|
||||||
uint8_t flags;
|
uint8_t state_flags;
|
||||||
uint8_t last_hit_by_client_id;
|
uint8_t last_hit_by_client_id;
|
||||||
|
|
||||||
Enemy(size_t source_index, uint8_t floor, EnemyType type);
|
Enemy(size_t source_index, uint8_t floor, EnemyType type);
|
||||||
|
|||||||
+27
-37
@@ -1815,10 +1815,6 @@ static void on_enemy_hit(shared_ptr<Client> c, uint8_t command, uint8_t, const v
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& enemy = l->map->enemies[cmd.enemy_index];
|
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;
|
enemy.last_hit_by_client_id = c->lobby_client_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1945,7 +1941,7 @@ static void on_steal_exp_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*
|
|||||||
add_player_exp(c, stolen_exp);
|
add_player_exp(c, stolen_exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
static void on_enemy_exp_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||||
auto s = c->require_server_state();
|
auto s = c->require_server_state();
|
||||||
auto l = c->require_lobby();
|
auto l = c->require_lobby();
|
||||||
|
|
||||||
@@ -1953,7 +1949,7 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t, uint8_t, const voi
|
|||||||
throw runtime_error("BB-only command sent in non-BB game");
|
throw runtime_error("BB-only command sent in non-BB game");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& cmd = check_size_t<G_EnemyKilled_BB_6xC8>(data, size);
|
const auto& cmd = check_size_t<G_EnemyEXPRequest_BB_6xC8>(data, size);
|
||||||
|
|
||||||
if (!l->is_game()) {
|
if (!l->is_game()) {
|
||||||
throw runtime_error("client should not kill enemies outside of games");
|
throw runtime_error("client should not kill enemies outside of games");
|
||||||
@@ -1965,16 +1961,19 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t, uint8_t, const voi
|
|||||||
send_text_message(c, "$C6Missing enemy killed");
|
send_text_message(c, "$C6Missing enemy killed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (c->lobby_client_id > 3) {
|
||||||
|
throw runtime_error("client ID is too large");
|
||||||
|
}
|
||||||
|
|
||||||
auto& e = l->map->enemies[cmd.enemy_index];
|
auto& e = l->map->enemies[cmd.enemy_index];
|
||||||
string e_str = e.str();
|
string e_str = e.str();
|
||||||
c->log.info("Enemy killed: E-%hX => %s", cmd.enemy_index.load(), e_str.c_str());
|
c->log.info("EXP requested for 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)) {
|
uint8_t state_flag = Map::Enemy::Flag::EXP_REQUESTED_BY_PLAYER0 << c->lobby_client_id;
|
||||||
send_text_message_printf(c, "$C5E-%hX __DEFEATED__", cmd.enemy_index.load());
|
if (e.state_flags & state_flag) {
|
||||||
}
|
throw runtime_error("duplicate EXP request");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
e.state_flags |= state_flag;
|
||||||
|
|
||||||
double experience = 0.0;
|
double experience = 0.0;
|
||||||
try {
|
try {
|
||||||
@@ -1989,44 +1988,35 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t, uint8_t, const voi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.flags |= Map::Enemy::Flag::DEFEATED;
|
|
||||||
if (experience != 0.0) {
|
if (experience != 0.0) {
|
||||||
for (size_t x = 0; x < l->max_clients; x++) {
|
if (c->floor != e.floor) {
|
||||||
auto lc = l->clients[x];
|
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||||
if (!lc) {
|
send_text_message_printf(c, "$C5E-%hX %s\n$C4FLOOR Y:%02" PRIX32 " E:%02hhX",
|
||||||
continue;
|
cmd.enemy_index.load(), name_for_enum(e.type), c->floor, e.floor);
|
||||||
}
|
}
|
||||||
if (!((e.flags >> x) & 1)) {
|
} else {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In PSOBB, Sega decided to add a 30% EXP boost for Episode 2. They could
|
// 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
|
// have done something reasonable, like edit the BattleParamEntry files so
|
||||||
// the monsters would all give more EXP, but they did something far lazier
|
// 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
|
// instead: they just stuck an if statement in the client's EXP request
|
||||||
// function. We, unfortunately, have to do the same here.
|
// 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);
|
bool is_ep2 = (l->episode == Episode::EP2);
|
||||||
uint32_t player_exp = experience *
|
uint32_t player_exp = experience *
|
||||||
(is_killer ? 1.0 : 0.8) *
|
(is_killer ? 1.0 : 0.8) *
|
||||||
l->base_exp_multiplier *
|
l->base_exp_multiplier *
|
||||||
l->challenge_exp_multiplier *
|
l->challenge_exp_multiplier *
|
||||||
(is_ep2 ? 1.3 : 1.0);
|
(is_ep2 ? 1.3 : 1.0);
|
||||||
if (lc->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
if (c->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));
|
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) {
|
if (c->game_data.character()->disp.stats.level < 199) {
|
||||||
add_player_exp(lc, player_exp);
|
add_player_exp(c, player_exp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2963,7 +2953,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* 6xC5 */ on_medical_center_bb,
|
/* 6xC5 */ on_medical_center_bb,
|
||||||
/* 6xC6 */ on_steal_exp_bb,
|
/* 6xC6 */ on_steal_exp_bb,
|
||||||
/* 6xC7 */ on_charge_attack_bb,
|
/* 6xC7 */ on_charge_attack_bb,
|
||||||
/* 6xC8 */ on_enemy_killed_bb,
|
/* 6xC8 */ on_enemy_exp_request_bb,
|
||||||
/* 6xC9 */ on_meseta_reward_request_bb,
|
/* 6xC9 */ on_meseta_reward_request_bb,
|
||||||
/* 6xCA */ on_item_reward_request_bb,
|
/* 6xCA */ on_item_reward_request_bb,
|
||||||
/* 6xCB */ nullptr,
|
/* 6xCB */ nullptr,
|
||||||
|
|||||||
Reference in New Issue
Block a user