From d31fb5b08469fbcd743895e6e9a4b97abe9db674 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 17 May 2026 07:59:32 -0700 Subject: [PATCH] fix battle level up on BB --- src/CommandFormats.hh | 2 +- src/ReceiveSubcommands.cc | 63 ++++++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 95ea0426..8d70cf97 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -5231,7 +5231,7 @@ struct G_UpdateEntityStat_6x9A { // Used in battle mode if the rules specify that techniques should level up upon character death. struct G_LevelUpAllTechniques_6x9B { - G_UnusedHeader header; + G_ClientIDHeader header; uint8_t num_levels = 0; parray unused; } __packed_ws__(G_LevelUpAllTechniques_6x9B, 8); diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 056aa93a..6a3c0e33 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -4693,22 +4693,71 @@ static asio::awaitable on_battle_level_up_bb(shared_ptr c, Subcomm if (!l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { throw runtime_error("6xD0 command sent during free play"); } + if (!l->quest) { + throw runtime_error("6x9B command sent without quest loaded"); + } + if (!l->quest->meta.battle_rules) { + throw runtime_error("6x9B command sent without battle quest loaded"); + } const auto& cmd = msg.check_size_t(); + if (cmd.num_levels != l->quest->meta.battle_rules->death_level_up) { + throw runtime_error("client requested incorrect level count"); + } + auto lc = l->clients.at(cmd.header.client_id); if (lc) { auto s = c->require_server_state(); auto lp = lc->character_file(); uint32_t target_level = min(lp->disp.stats.level + cmd.num_levels, 199); uint32_t before_exp = lp->disp.stats.exp; - int32_t exp_delta = lp->disp.stats.exp - before_exp; - if (exp_delta > 0) { - s->level_table(lc->version())->advance_to_level(lp->disp.stats, target_level, lp->disp.visual.char_class); - if (lc->version() == Version::BB_V4) { - send_give_experience(lc, exp_delta, 0xFFFF); - send_level_up(lc); + s->level_table(lc->version())->advance_to_level(lp->disp.stats, target_level, lp->disp.visual.char_class); + if ((lp->disp.stats.exp > before_exp) && (lc->version() == Version::BB_V4)) { + send_give_experience(lc, lp->disp.stats.exp - before_exp, 0xFFFF); + send_level_up(lc); + } + } + co_return; +} + +static asio::awaitable on_battle_tech_level_up(shared_ptr c, SubcommandMessage& msg) { + auto l = c->require_lobby(); + if (!l->is_game()) { + throw runtime_error("6x9B command sent in non-game lobby"); + } + if (l->mode != GameMode::BATTLE) { + throw runtime_error("6x9B command sent during free play"); + } + if (!l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { + throw runtime_error("6x9B command sent during free play"); + } + if (!l->quest) { + throw runtime_error("6x9B command sent without quest loaded"); + } + if (!l->quest->meta.battle_rules) { + throw runtime_error("6x9B command sent without battle quest loaded"); + } + + const auto& cmd = msg.check_size_t(); + if (cmd.num_levels != l->quest->meta.battle_rules->death_tech_level_up) { + throw runtime_error("client requested incorrect technique level count"); + } + + auto lc = l->clients.at(cmd.header.client_id); + if (lc) { + auto s = c->require_server_state(); + auto lp = lc->character_file(); + auto pmt = s->item_parameter_table(lc->version()); + for (uint8_t tech_num = 0; tech_num < 0x13; tech_num++) { + size_t level = lp->get_technique_level(tech_num); + if (level != 0xFF) { + size_t new_level = std::min( + level + cmd.num_levels, pmt->get_max_tech_level(lp->disp.visual.char_class, tech_num)); + lp->set_technique_level(tech_num, new_level); } } + + forward_subcommand(c, msg); } co_return; } @@ -5646,7 +5695,7 @@ const vector subcommand_definitions{ /* 6x98 */ {NONE, NONE, 0x98, on_forward_check_game}, /* 6x99 */ {NONE, NONE, 0x99, on_forward_check_game}, /* 6x9A */ {NONE, NONE, 0x9A, on_forward_check_game_client}, - /* 6x9B */ {NONE, NONE, 0x9B, on_forward_check_game}, + /* 6x9B */ {NONE, NONE, 0x9B, on_battle_tech_level_up}, /* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_status_effect_flags_ultimate}, /* 6x9D */ {NONE, NONE, 0x9D, on_forward_check_game}, /* 6x9E */ {NONE, NONE, 0x9E, forward_subcommand_m},