diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index b360b48c..5ae90f5b 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -5510,9 +5510,9 @@ struct G_ExchangeItemForTeamPoints_BB_6xCC { // 6xCE: Accept master transfer (BB) // Same format as 6xC1 -// 6xCF: Restart battle (BB) +// 6xCF: Start battle (BB) -struct G_RestartBattle_BB_6xCF { +struct G_StartBattle_BB_6xCF { G_UnusedHeader header; BattleRules rules; } __packed__; diff --git a/src/LevelTable.cc b/src/LevelTable.cc index b3391475..91107106 100644 --- a/src/LevelTable.cc +++ b/src/LevelTable.cc @@ -10,6 +10,7 @@ using namespace std; void PlayerStats::reset_to_base(uint8_t char_class, shared_ptr level_table) { this->level = 0; + this->experience = 0; this->char_stats = level_table->base_stats_for_class(char_class); } diff --git a/src/Lobby.cc b/src/Lobby.cc index aa43b3ae..d80883ca 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -372,6 +372,15 @@ void Lobby::on_item_id_generated_externally(uint32_t item_id) { } } +void Lobby::assign_inventory_item_ids(shared_ptr c) { + auto p = c->game_data.character(); + for (size_t z = 0; z < p->inventory.num_items; z++) { + p->inventory.items[z].data.id = this->generate_item_id(c->lobby_client_id); + } + c->log.info("Assigned item IDs"); + p->print_inventory(stderr, c->version(), c->require_server_state()->item_name_index); +} + unordered_map> Lobby::clients_by_serial_number() const { unordered_map> ret; for (auto c : this->clients) { diff --git a/src/Lobby.hh b/src/Lobby.hh index f4ed8eda..05035456 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -191,6 +191,7 @@ struct Lobby : public std::enable_shared_from_this { ItemData remove_item(uint32_t item_id); uint32_t generate_item_id(uint8_t client_id); void on_item_id_generated_externally(uint32_t item_id); + void assign_inventory_item_ids(std::shared_ptr c); static uint8_t game_event_for_lobby_event(uint8_t lobby_event); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index bf791ae9..c9fa391c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1848,24 +1848,6 @@ void set_lobby_quest(shared_ptr l, shared_ptr q) { break; } - if (vq->battle_rules) { - lc->game_data.create_battle_overlay(vq->battle_rules, s->level_table); - lc->log.info("Created battle overlay"); - } else if (vq->challenge_template_index >= 0) { - lc->game_data.create_challenge_overlay(lc->version(), vq->challenge_template_index, s->level_table); - lc->log.info("Created challenge overlay"); - } - - // If an overlay was created, item IDs need to be assigned - if (lc->game_data.has_overlay()) { - auto overlay = lc->game_data.character(); - for (size_t z = 0; z < overlay->inventory.num_items; z++) { - overlay->inventory.items[z].data.id = l->generate_item_id(client_id); - } - lc->log.info("Assigned overlay item IDs"); - overlay->print_inventory(stderr, lc->version(), s->item_name_index); - } - string bin_filename = vq->bin_filename(); string dat_filename = vq->dat_filename(); string xb_filename = vq->xb_filename(); @@ -2626,7 +2608,9 @@ static void on_AC_V3_BB(shared_ptr c, uint16_t, uint32_t, string& data) (l->base_version == GameVersion::BB) && l->map && l->quest) { + auto vq = l->quest->version(QuestScriptVersion::BB_V4, c->language()); + auto dat_contents = prs_decompress(*vq->dat_contents); l->map->clear(); l->map->add_enemies_and_objects_from_quest_data( @@ -2637,15 +2621,36 @@ static void on_AC_V3_BB(shared_ptr c, uint16_t, uint32_t, string& data) dat_contents.size(), l->random_seed, (l->mode == GameMode::CHALLENGE) ? Map::NO_RARE_ENEMIES : Map::DEFAULT_RARE_ENEMIES); - l->log.info("Replaced enemies list with quest layout (%zu entries)", - l->map->enemies.size()); + l->item_creator->clear_destroyed_entities(); + + l->log.info("Replaced enemies list with quest layout (%zu entries)", l->map->enemies.size()); for (size_t z = 0; z < l->map->enemies.size(); z++) { string e_str = l->map->enemies[z].str(); l->log.info("(Entry %zX) %s", z, e_str.c_str()); } + + auto s = l->require_server_state(); for (auto& lc : l->clients) { - if (lc) { - send_rare_enemy_index_list(c, l->map->rare_enemy_indexes); + if (!lc) { + continue; + } + + send_rare_enemy_index_list(c, l->map->rare_enemy_indexes); + + // On non-BB versions, overlays are created when the quest starts because + // the server is not informed when the clients have replaced their player + // data. On BB, this is instead done in the 6xCF handler (for battle) or + // the 02DF handler (for challenge). + if (l->base_version != GameVersion::BB) { + lc->game_data.delete_overlay(); + if (vq->battle_rules) { + lc->game_data.create_battle_overlay(vq->battle_rules, s->level_table); + lc->log.info("Created battle overlay"); + } else if (vq->challenge_template_index >= 0) { + lc->game_data.create_challenge_overlay(lc->version(), vq->challenge_template_index, s->level_table); + lc->log.info("Created challenge overlay"); + l->assign_inventory_item_ids(lc); + } } } } @@ -3319,10 +3324,9 @@ static void on_DF_BB(shared_ptr c, uint16_t command, uint32_t, string& d if (l->quest->challenge_template_index != static_cast(cmd.template_index)) { throw runtime_error("challenge template index in quest metadata does not match index sent by client"); } - // Do nothing: we've already created the player overlay by the time this - // opcode is run on the client. We can't easily move the overlay creation - // here, since non-BB versions do not send anything when they create the - // overlay, so we have to do it when the quest loads instead. + c->game_data.create_challenge_overlay(c->version(), l->quest->challenge_template_index, s->level_table); + c->log.info("Created challenge overlay"); + l->assign_inventory_item_ids(c); break; } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index b10a99b5..6cbb6973 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -2057,8 +2057,9 @@ static void on_battle_restart_bb(shared_ptr c, uint8_t, uint8_t, const v if (l->is_game() && (l->base_version == GameVersion::BB) && (l->mode == GameMode::BATTLE) && - l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { - const auto& cmd = check_size_t(data, size); + l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) && + l->leader_id == c->lobby_client_id) { + const auto& cmd = check_size_t(data, size); shared_ptr new_rules(new BattleRules(cmd.rules)); if (l->item_creator) { @@ -2430,7 +2431,7 @@ subcommand_handler_t subcommand_handlers[0x100] = { /* 6x98 */ nullptr, /* 6x99 */ nullptr, /* 6x9A */ on_forward_check_size_game, - /* 6x9B */ nullptr, + /* 6x9B */ on_forward_check_size_game, /* 6x9C */ on_forward_check_size_game, /* 6x9D */ nullptr, /* 6x9E */ nullptr,