From 85d0bac5cba2d5768a9271cb9c4d65826645e246 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Wed, 6 Dec 2023 09:46:57 -0800 Subject: [PATCH] assign bank item IDs at game join time --- src/Lobby.cc | 13 ++++++++++--- src/Lobby.hh | 2 +- src/PlayerSubordinates.cc | 9 ++++++++- src/PlayerSubordinates.hh | 3 ++- src/ReceiveCommands.cc | 4 ++-- src/ReceiveSubcommands.cc | 9 ++++++++- src/SaveFileFormats.cc | 12 ++++++++++++ src/SaveFileFormats.hh | 1 + 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/Lobby.cc b/src/Lobby.cc index b9291dfd..93482500 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -214,7 +214,7 @@ void Lobby::add_client(shared_ptr c, ssize_t required_client_id) { // If the lobby is a game and item tracking is enabled, assign the inventory's // item IDs if (this->is_game() && this->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) { - this->assign_inventory_item_ids(c); + this->assign_inventory_and_bank_item_ids(c); } // If the lobby is recording a battle record, add the player join event @@ -383,13 +383,20 @@ void Lobby::on_item_id_generated_externally(uint32_t item_id) { } } -void Lobby::assign_inventory_item_ids(shared_ptr c) { +void Lobby::assign_inventory_and_bank_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"); + c->log.info("Assigned inventory item IDs"); p->print_inventory(stderr, c->version(), c->require_server_state()->item_name_index); + if (p->bank.num_items) { + p->bank.assign_ids(0x99000000 + (c->lobby_client_id << 20)); + c->log.info("Assigned bank item IDs"); + p->print_bank(stderr, c->version(), c->require_server_state()->item_name_index); + } else { + c->log.info("Bank is empty"); + } } unordered_map> Lobby::clients_by_serial_number() const { diff --git a/src/Lobby.hh b/src/Lobby.hh index a840b611..d056e435 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -191,7 +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); + void assign_inventory_and_bank_item_ids(std::shared_ptr c); QuestIndex::IncludeCondition quest_include_condition() const; diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index 70e31fef..60f65737 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -479,7 +479,8 @@ void PlayerBank::add_item(const ItemData& item) { this->num_items++; } -ItemData PlayerBank::remove_item_by_index(size_t index, uint32_t amount) { +ItemData PlayerBank::remove_item(uint32_t item_id, uint32_t amount) { + size_t index = this->find_item(item_id); auto& bank_item = this->items[index]; ItemData ret; @@ -680,6 +681,12 @@ void PlayerBank::sort() { std::sort(this->items.data(), this->items.data() + this->num_items); } +void PlayerBank::assign_ids(uint32_t base_id) { + for (size_t z = 0; z < this->num_items; z++) { + this->items[z].data.id = base_id + z; + } +} + BattleRules::BattleRules(const JSON& json) { static const JSON empty_list = JSON::list(); diff --git a/src/PlayerSubordinates.hh b/src/PlayerSubordinates.hh index c94169f3..bdb7fe78 100644 --- a/src/PlayerSubordinates.hh +++ b/src/PlayerSubordinates.hh @@ -101,10 +101,11 @@ struct PlayerBank { /* 12C8 */ void add_item(const ItemData& item); - ItemData remove_item_by_index(size_t index, uint32_t amount); + ItemData remove_item(uint32_t item_id, uint32_t amount); size_t find_item(uint32_t item_id); void sort(); + void assign_ids(uint32_t base_id); } __attribute__((packed)); struct PlayerDispDataBB; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index c841c92c..b2aeec83 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1899,7 +1899,7 @@ static void on_quest_loaded(shared_ptr l) { } else if (l->quest->challenge_template_index >= 0) { lc->game_data.create_challenge_overlay(lc->version(), l->quest->challenge_template_index, s->level_table); lc->log.info("Created challenge overlay"); - l->assign_inventory_item_ids(lc); + l->assign_inventory_and_bank_item_ids(lc); } } } @@ -3441,7 +3441,7 @@ static void on_DF_BB(shared_ptr c, uint16_t command, uint32_t, string& d if (lc) { lc->game_data.create_challenge_overlay(lc->version(), l->quest->challenge_template_index, s->level_table); lc->log.info("Created challenge overlay"); - l->assign_inventory_item_ids(lc); + l->assign_inventory_and_bank_item_ids(lc); } } break; diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index db4a86f9..60800108 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1471,6 +1471,13 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr c, uint } else { // Deposit item auto item = p->remove_item(cmd.item_id, cmd.item_amount, c->version() != Version::BB_V4); + // If a stack was split, the bank item retains the same item ID as the + // inventory item. This is annoying but doesn't cause any problems + // because we always generate a new item ID when withdrawing from the + // bank, so there's no chance of conflict later. + if (item.id == 0xFFFFFFFF) { + item.id = cmd.item_id; + } bank.add_item(item); send_destroy_item(c, cmd.item_id, cmd.item_amount, true); @@ -1496,7 +1503,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr c, uint } } else { // Take item - auto item = bank.remove_item_by_index(cmd.item_index, cmd.item_amount); + auto item = bank.remove_item(cmd.item_id, cmd.item_amount); item.id = l->generate_item_id(c->lobby_client_id); p->add_item(item); send_create_inventory_item(c, item); diff --git a/src/SaveFileFormats.cc b/src/SaveFileFormats.cc index f88b1432..fff2df31 100644 --- a/src/SaveFileFormats.cc +++ b/src/SaveFileFormats.cc @@ -584,6 +584,18 @@ void PSOBBCharacterFile::print_inventory(FILE* stream, Version version, shared_p } } +void PSOBBCharacterFile::print_bank(FILE* stream, Version version, shared_ptr name_index) const { + fprintf(stream, "[PlayerBank] Meseta: %" PRIu32 "\n", this->bank.meseta.load()); + fprintf(stream, "[PlayerBank] %" PRIu32 " items\n", this->bank.num_items.load()); + for (size_t x = 0; x < this->bank.num_items; x++) { + const auto& item = this->bank.items[x]; + const char* present_token = item.present ? "" : " (missing present flag)"; + auto name = name_index->describe_item(version, item.data); + auto hex = item.data.hex(); + fprintf(stream, "[PlayerBank] %3zu: %s (%s) (x%hu) %s\n", x, hex.c_str(), name.c_str(), item.amount.load(), present_token); + } +} + const array PSOBBCharacterFile::DEFAULT_SYMBOL_CHATS = { DefaultSymbolChatEntry{"\tEHello", 0x28, {0xFFFF, 0x000D, 0xFFFF, 0xFFFF}, {SymbolChat::FacePart{0x05, 0x18, 0x1D, 0x00}, {0x05, 0x28, 0x1D, 0x01}, {0x36, 0x20, 0x2A, 0x00}, {0x3C, 0x00, 0x32, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}}}, DefaultSymbolChatEntry{"\tEGood-bye", 0x74, {0x0476, 0x000C, 0xFFFF, 0xFFFF}, {SymbolChat::FacePart{0x06, 0x15, 0x14, 0x00}, {0x06, 0x2B, 0x14, 0x01}, {0x05, 0x18, 0x1F, 0x00}, {0x05, 0x28, 0x1F, 0x01}, {0x36, 0x20, 0x2A, 0x00}, {0x3C, 0x00, 0x32, 0x00}, {0xFF, 0x00, 0x00, 0x00}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}, {0xFF, 0x00, 0x00, 0x02}}}, diff --git a/src/SaveFileFormats.hh b/src/SaveFileFormats.hh index 4df96502..bb6c0133 100644 --- a/src/SaveFileFormats.hh +++ b/src/SaveFileFormats.hh @@ -254,6 +254,7 @@ struct PSOBBCharacterFile { void clear_all_material_usage(); void print_inventory(FILE* stream, Version version, std::shared_ptr name_index) const; + void print_bank(FILE* stream, Version version, std::shared_ptr name_index) const; } __attribute__((packed)); struct PSOBBGuildCardFile {