From d4bc880018c65c5aca620cd64d87ac0207e97aa9 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sat, 7 Jun 2025 09:53:56 -0700 Subject: [PATCH] make $killcount work for units too --- src/ChatCommands.cc | 50 ++++++++++++++++++++-------------- src/Client.cc | 4 +-- src/HTTPServer.cc | 4 +-- src/ItemNameIndex.cc | 35 +++++++++++++----------- src/ItemNameIndex.hh | 7 ++++- src/Main.cc | 2 +- src/PlayerInventory.hh | 4 +++ src/ProxyCommands.cc | 2 +- src/RareItemSet.cc | 2 +- src/ReceiveCommands.cc | 2 +- src/ReceiveSubcommands.cc | 56 +++++++++++++++++++-------------------- src/ServerState.cc | 6 ++--- src/ServerState.hh | 2 +- src/ShellCommands.cc | 2 +- 14 files changed, 101 insertions(+), 77 deletions(-) diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index e19269c6..903f4e84 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -1302,7 +1302,7 @@ ChatCommandDefinition cc_item( } } - string name = s->describe_item(a.c->version(), item, true); + string name = s->describe_item(a.c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES); send_text_message(a.c, "$C7Item created:\n" + name); co_return; }); @@ -1364,28 +1364,38 @@ ChatCommandDefinition cc_killcount( a.check_is_proxy(false); auto p = a.c->character(); - size_t item_index; - try { - item_index = p->inventory.find_equipped_item(EquipSlot::WEAPON); - } catch (const out_of_range&) { - throw precondition_failed("No weapon equipped"); + vector item_indexes; + for (size_t z = 0; z < p->inventory.num_items; z++) { + const auto& item = p->inventory.items[z]; + if (item.is_equipped() && item.data.has_kill_count()) { + item_indexes.emplace_back(z); + } } - const auto& item = p->inventory.items.at(item_index); - if (!item.data.has_kill_count()) { - throw precondition_failed("Weapon does not\nhave a kill count"); - } + if (item_indexes.empty()) { + throw precondition_failed("No equipped items\nhave kill counts"); - // Kill counts are only accurate on the server side at all times on BB. On - // other versions, we update the server's view of the client's inventory - // during games, but we can't track kills because the client doesn't inform - // the server whether it counted a kill for any individual enemy. So, on - // non-BB versions, the kill count is accurate at all times in the lobby - // (since kills can't occur there), or at the beginning of a game. - if ((a.c->version() == Version::BB_V4) || !a.c->require_lobby()->is_game()) { - send_text_message_fmt(a.c, "{} kills", item.data.get_kill_count()); } else { - send_text_message_fmt(a.c, "{} kills as of\ngame join", item.data.get_kill_count()); + // Kill counts are only accurate on the server side at all times on BB. + // On other versions, we update the server's view of the client's + // inventory during games, but we can't track kills because the client + // doesn't inform the server whether it counted a kill for any + // individual enemy. So, on non-BB versions, the kill count is accurate + // at all times in the lobby (since kills can't occur there), or at the + // beginning of a game. + if ((a.c->version() == Version::BB_V4) || !a.c->require_lobby()->is_game()) { + send_text_message(a.c, "As of now:"); + } else { + send_text_message(a.c, "As of game join:"); + } + + auto s = a.c->require_server_state(); + for (size_t z : item_indexes) { + const auto& item = p->inventory.items[z]; + string name = s->describe_item( + a.c->version(), item.data, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES | ItemNameIndex::Flag::NAME_ONLY); + send_text_message_fmt(a.c, "{}$C7: {} kills", name, item.data.get_kill_count()); + } } co_return; }); @@ -2768,7 +2778,7 @@ ChatCommandDefinition cc_what( throw precondition_failed("$C4No items are near you"); } else { auto s = a.c->require_server_state(); - string name = s->describe_item(a.c->version(), nearest_fi->data, true); + string name = s->describe_item(a.c->version(), nearest_fi->data, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES); send_text_message(a.c, name); } co_return; diff --git a/src/Client.cc b/src/Client.cc index 9d217b33..03481927 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -1058,7 +1058,7 @@ void Client::print_inventory() const { for (size_t x = 0; x < p->inventory.num_items; x++) { const auto& item = p->inventory.items[x]; auto hex = item.data.hex(); - auto name = s->describe_item(this->version(), item.data, false); + auto name = s->describe_item(this->version(), item.data); this->log.info_f("[PlayerInventory] {:2}: [+{:08X}] {} ({})", x, item.flags, hex, name); } } @@ -1072,7 +1072,7 @@ void Client::print_bank() const { const auto& item = bank.items[x]; const char* present_token = item.present ? "" : " (missing present flag)"; auto hex = item.data.hex(); - auto name = s->describe_item(this->version(), item.data, false); + auto name = s->describe_item(this->version(), item.data); this->log.info_f("[PlayerBank] {:3}: {} ({}) (x{}){}", x, hex, name, item.amount, present_token); } } diff --git a/src/HTTPServer.cc b/src/HTTPServer.cc index 583a8d6b..9d52b966 100644 --- a/src/HTTPServer.cc +++ b/src/HTTPServer.cc @@ -193,7 +193,7 @@ std::shared_ptr HTTPServer::generate_client_json( {"ItemID", item.data.id.load()}, }); if (item_name_index) { - item_dict.emplace("Description", item_name_index->describe_item(item.data, false)); + item_dict.emplace("Description", item_name_index->describe_item(item.data)); } items_json.emplace_back(std::move(item_dict)); } @@ -431,7 +431,7 @@ std::shared_ptr HTTPServer::generate_lobby_json( {"ItemID", item->data.id.load()}, }); if (item_name_index) { - item_dict.emplace("Description", item_name_index->describe_item(item->data, false)); + item_dict.emplace("Description", item_name_index->describe_item(item->data)); } floor_items_json.emplace_back(std::move(item_dict)); } diff --git a/src/ItemNameIndex.cc b/src/ItemNameIndex.cc index b98ca31b..8684e538 100644 --- a/src/ItemNameIndex.cc +++ b/src/ItemNameIndex.cc @@ -97,7 +97,10 @@ const array name_for_s_rank_special = { "King\'s", }; -std::string ItemNameIndex::describe_item(const ItemData& item, bool include_color_escapes, bool hide_mag_stats) const { +std::string ItemNameIndex::describe_item(const ItemData& item, uint8_t flags) const { + bool include_color_escapes = flags & ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES; + bool name_only = flags & ItemNameIndex::Flag::NAME_ONLY; + if (item.data1[0] == 0x04) { return std::format("{}{} Meseta", include_color_escapes ? "$C7" : "", item.data2d); } @@ -108,13 +111,14 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo bool is_unidentified = false; if ((item.data1[0] == 0x00) && (item.data1[4] != 0x00) && !item.is_s_rank_weapon()) { is_unidentified = item.data1[4] & 0x80; - bool is_present = item.data1[4] & 0x40; uint8_t special_id = item.data1[4] & 0x3F; - if (is_present) { - ret_tokens.emplace_back("Wrapped"); - } - if (is_unidentified) { - ret_tokens.emplace_back("????"); + if (!name_only) { + if (item.data1[4] & 0x40) { + ret_tokens.emplace_back("Wrapped"); + } + if (is_unidentified) { + ret_tokens.emplace_back("????"); + } } if (special_id) { try { @@ -124,7 +128,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo } } } - if ((item.data1[0] == 0x00) && (item.data1[2] != 0x00) && item.is_s_rank_weapon()) { + if (!name_only && (item.data1[0] == 0x00) && (item.data1[2] != 0x00) && item.is_s_rank_weapon()) { try { ret_tokens.emplace_back(name_for_s_rank_special.at(item.data1[2])); } catch (const out_of_range&) { @@ -135,9 +139,10 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo // Armors, shields, and units (0x01) can be wrapped, as can mags (0x02) and // non-stackable tools (0x03). However, each of these item classes has its // flags in a different location. - if (((item.data1[0] == 0x01) && (item.data1[4] & 0x40)) || - ((item.data1[0] == 0x02) && (item.data2[2] & 0x40)) || - ((item.data1[0] == 0x03) && !item.is_stackable(*this->limits) && (item.data1[3] & 0x40))) { + if (!name_only && + (((item.data1[0] == 0x01) && (item.data1[4] & 0x40)) || + ((item.data1[0] == 0x02) && (item.data2[2] & 0x40)) || + ((item.data1[0] == 0x03) && !item.is_stackable(*this->limits) && (item.data1[3] & 0x40)))) { ret_tokens.emplace_back("Wrapped"); } @@ -168,7 +173,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo if (item.data1[0] == 0x00) { // For weapons, add the grind and bonuses, or S-rank name if applicable - if (item.data1[3] > 0) { + if (!name_only && item.data1[3] > 0) { ret_tokens.emplace_back(std::format("+{}", item.data1[3])); } @@ -210,7 +215,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo } } - } else { // Not S-rank (extended name bits not set) + } else if (!name_only) { // Not S-rank (extended name bits not set) parray bonuses(0); for (size_t x = 0; x < 3; x++) { uint8_t which = item.data1[6 + 2 * x]; @@ -258,7 +263,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo ret_tokens.emplace_back(std::format("!MD:{:04X}", modifier)); } - } else { // Armor/shields + } else if (!name_only) { // Armor/shields if (item.data1[5] > 0) { if (item.data1[5] == 1) { ret_tokens.emplace_back("(1 slot)"); @@ -276,7 +281,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo } } - } else if (!hide_mag_stats && (item.data1[0] == 0x02)) { + } else if (!name_only && (item.data1[0] == 0x02)) { // For mags, add tons of info ret_tokens.emplace_back(std::format("LV{}", item.data1[2])); diff --git a/src/ItemNameIndex.hh b/src/ItemNameIndex.hh index ecbf7f53..e7b8673a 100644 --- a/src/ItemNameIndex.hh +++ b/src/ItemNameIndex.hh @@ -38,7 +38,12 @@ public: inline bool exists(const ItemData& item) const { return this->primary_identifier_index.count(item.primary_identifier()); } - std::string describe_item(const ItemData& item, bool include_color_escapes = false, bool hide_mag_stats = false) const; + + enum Flag : uint8_t { + INCLUDE_PSO_COLOR_ESCAPES = 0x01, + NAME_ONLY = 0x02, + }; + std::string describe_item(const ItemData& item, uint8_t flags = 0) const; ItemData parse_item_description(const std::string& description) const; void print_table(FILE* stream) const; diff --git a/src/Main.cc b/src/Main.cc index 18656722..5b1c4e5a 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -2158,7 +2158,7 @@ Action a_describe_item( } string desc = name_index->describe_item(item); - string desc_colored = name_index->describe_item(item, true); + string desc_colored = name_index->describe_item(item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES); phosg::log_info_f("Data (decoded): {:02X}{:02X}{:02X}{:02X} {:02X}{:02X}{:02X}{:02X} {:02X}{:02X}{:02X}{:02X} -------- {:02X}{:02X}{:02X}{:02X}", item.data1[0], item.data1[1], item.data1[2], item.data1[3], diff --git a/src/PlayerInventory.hh b/src/PlayerInventory.hh index 82cf57f0..4237595f 100644 --- a/src/PlayerInventory.hh +++ b/src/PlayerInventory.hh @@ -77,6 +77,10 @@ struct PlayerInventoryItemT { ret.data.id.store_raw(phosg::bswap32(ret.data.id.load_raw())); return ret; } + + bool is_equipped() const { + return (this->flags & 8); + } } __attribute__((packed)); using PlayerInventoryItem = PlayerInventoryItemT; using PlayerInventoryItemBE = PlayerInventoryItemT; diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 3dd8c6f9..d8c36d63 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -884,7 +884,7 @@ static asio::awaitable SC_6x60_6xA2(shared_ptr c, Channel c->log.info_f("No item was created"); } else { auto s = c->require_server_state(); - string name = s->describe_item(c->version(), res.item, false); + string name = s->describe_item(c->version(), res.item); c->log.info_f("Entity {:04X} (area {:02X}) created item {}", cmd.entity_index, cmd.effective_area, name); res.item.id = c->proxy_session->next_item_id++; c->log.info_f("Creating item {:08X} at {:02X}:{:g},{:g} for all clients", diff --git a/src/RareItemSet.cc b/src/RareItemSet.cc index eb4c6006..e92e4ca3 100644 --- a/src/RareItemSet.cc +++ b/src/RareItemSet.cc @@ -722,7 +722,7 @@ string RareItemSet::serialize_html( } string hex = example_item.short_hex(); - string desc = name_index->describe_item(example_item, false, true); + string desc = name_index->describe_item(example_item, ItemNameIndex::Flag::NAME_ONLY); tokens.emplace_back(std::format("{}", hex, desc)); float denom = static_cast(frac.second) / static_cast(frac.first); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index ba56a73c..72cd20f8 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4088,7 +4088,7 @@ static asio::awaitable on_DF_BB(shared_ptr c, Channel::Message& ms award_state.rank_award_flags |= cmd.rank_bitmask; p->add_item(cmd.item, *s->item_stack_limits(c->version())); l->on_item_id_generated_externally(cmd.item.id); - string desc = s->describe_item(Version::BB_V4, cmd.item, false); + string desc = s->describe_item(Version::BB_V4, cmd.item); l->log.info_f("(Challenge mode) Item awarded to player {}: {}", c->lobby_client_id, desc); break; } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 9b05b719..cff1c093 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1947,7 +1947,7 @@ static asio::awaitable on_player_drop_item(shared_ptr c, Subcomman if (l->log.should_log(phosg::LogLevel::L_INFO)) { auto s = c->require_server_state(); - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} dropped item {:08X} ({}) at {}:({:g}, {:g})", cmd.header.client_id, cmd.item_id, name, cmd.floor, cmd.pos.x, cmd.pos.z); c->print_inventory(); @@ -1985,7 +1985,7 @@ static asio::awaitable on_create_inventory_item_t(shared_ptr c, Su } if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} created inventory item {:08X} ({}) in inventory of NPC {:02X}; ignoring", c->lobby_client_id, item.id, name, cmd.header.client_id); } @@ -1993,7 +1993,7 @@ static asio::awaitable on_create_inventory_item_t(shared_ptr c, Su c->character()->add_item(item, *s->item_stack_limits(c->version())); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} created inventory item {:08X} ({})", c->lobby_client_id, item.id, name); c->print_inventory(); } @@ -2036,7 +2036,7 @@ static void on_drop_partial_stack_t(shared_ptr c, SubcommandMessage& msg if (l->log.should_log(phosg::LogLevel::L_INFO)) { auto s = c->require_server_state(); - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} split stack to create floor item {:08X} ({}) at {}:({:g},{:g})", cmd.header.client_id, item.id, name, cmd.floor, cmd.pos.x, cmd.pos.z); c->print_inventory(); @@ -2091,7 +2091,7 @@ static asio::awaitable on_drop_partial_stack_bb(shared_ptr c, Subc if (l->log.should_log(phosg::LogLevel::L_INFO)) { auto s = c->require_server_state(); - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} split stack {:08X} (removed: {}) at {}:({:g}, {:g})", cmd.header.client_id, cmd.item_id, name, cmd.floor, cmd.pos.x, cmd.pos.z); c->print_inventory(); @@ -2124,7 +2124,7 @@ static asio::awaitable on_buy_shop_item(shared_ptr c, SubcommandMe p->remove_meseta(price, c->version() != Version::BB_V4); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} bought item {:08X} ({}) from shop ({} Meseta)", cmd.header.client_id, item.id, name, price); c->print_inventory(); @@ -2156,7 +2156,7 @@ void send_item_notification_if_needed(shared_ptr c, const ItemData& item } if (should_notify) { - string name = s->describe_item(c->version(), item, true); + string name = s->describe_item(c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES); const char* rare_header = (should_include_rare_header ? "$C6Rare item dropped:\n" : ""); send_text_message_fmt(c, "{}{}", rare_header, name); } @@ -2199,7 +2199,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr c, SubcommandMessage& l->on_item_id_generated_externally(item.id); l->add_item(cmd.item.floor, item, cmd.item.pos, obj_st, ene_st, should_notify ? 0x100F : 0x000F); - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} (leader) created floor item {:08X} ({}){} at {}:({:g}, {:g})", l->leader_id, item.id, @@ -2279,7 +2279,7 @@ static asio::awaitable on_pick_up_item_generic( if (l->log.should_log(phosg::LogLevel::L_INFO)) { auto s = c->require_server_state(); - auto name = s->describe_item(c->version(), fi->data, false); + auto name = s->describe_item(c->version(), fi->data); l->log.info_f("Player {} picked up {:08X} ({})", client_id, item_id, name); c->print_inventory(); } @@ -2313,8 +2313,8 @@ static asio::awaitable on_pick_up_item_generic( if (should_send_game_notif || should_send_global_notif) { string p_name = p->disp.name.decode(); - string desc_ingame = s->describe_item(c->version(), fi->data, true); - string desc_http = s->describe_item(c->version(), fi->data, false); + string desc_ingame = s->describe_item(c->version(), fi->data, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES); + string desc_http = s->describe_item(c->version(), fi->data); if (s->http_server) { auto message = make_shared(phosg::JSON::dict({ @@ -2407,7 +2407,7 @@ static asio::awaitable on_use_item(shared_ptr c, SubcommandMessage // Note: We do this weird scoping thing because player_use_item will // likely delete the item, which will break the reference here. const auto& item = p->inventory.items[index].data; - name = s->describe_item(c->version(), item, false); + name = s->describe_item(c->version(), item); } player_use_item(c, index, l->rand_crypt); @@ -2438,9 +2438,9 @@ static asio::awaitable on_feed_mag(shared_ptr c, SubcommandMessage // Note: We do this weird scoping thing because player_feed_mag will // likely delete the items, which will break the references here. const auto& fed_item = p->inventory.items[fed_index].data; - fed_name = s->describe_item(c->version(), fed_item, false); + fed_name = s->describe_item(c->version(), fed_item); const auto& mag_item = p->inventory.items[mag_index].data; - mag_name = s->describe_item(c->version(), mag_item, false); + mag_name = s->describe_item(c->version(), mag_item); } player_feed_mag(c, mag_index, fed_index); @@ -2699,7 +2699,7 @@ static asio::awaitable on_ep3_private_word_select_bb_bank_action( send_destroy_item_to_lobby(c, cmd.item_id, cmd.item_amount, true); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - string name = s->describe_item(Version::BB_V4, item, false); + string name = s->describe_item(Version::BB_V4, item); l->log.info_f("Player {} deposited item {:08X} (x{}) ({}) in the bank", c->lobby_client_id, cmd.item_id, cmd.item_amount, name); c->print_inventory(); @@ -2729,7 +2729,7 @@ static asio::awaitable on_ep3_private_word_select_bb_bank_action( send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - string name = s->describe_item(Version::BB_V4, item, false); + string name = s->describe_item(Version::BB_V4, item); l->log.info_f("Player {} withdrew item {:08X} (x{}) ({}) from the bank", c->lobby_client_id, item.id, cmd.item_amount, name); c->print_inventory(); @@ -3024,7 +3024,7 @@ static asio::awaitable on_entity_drop_item_request(shared_ptr c, S if (res.item.empty()) { l->log.info_f("No item was created"); } else { - string name = s->describe_item(c->version(), res.item, false); + string name = s->describe_item(c->version(), res.item); l->log.info_f("Entity {:04X} (area {:02X}) created item {}", cmd.entity_index, cmd.effective_area, name); if (drop_mode == Lobby::DropMode::SERVER_DUPLICATE) { for (const auto& lc : l->clients) { @@ -3062,7 +3062,7 @@ static asio::awaitable on_entity_drop_item_request(shared_ptr c, S if (res.item.empty()) { l->log.info_f("No item was created for {}", lc->channel->name); } else { - string name = s->describe_item(lc->version(), res.item, false); + string name = s->describe_item(lc->version(), res.item); l->log.info_f("Entity {:04X} (area {:02X}) created item {}", cmd.entity_index, cmd.effective_area, name); res.item.id = l->generate_item_id(0xFF); l->log.info_f("Creating item {:08X} at {:02X}:{:g},{:g} for {}", @@ -4045,7 +4045,7 @@ static asio::awaitable on_item_reward_request_bb(shared_ptr c, Sub c->character()->add_item(item, limits); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} created inventory item {:08X} ({}) via quest command", c->lobby_client_id, item.id, name); c->print_inventory(); @@ -4053,7 +4053,7 @@ static asio::awaitable on_item_reward_request_bb(shared_ptr c, Sub } catch (const out_of_range&) { if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} attempted to create inventory item {:08X} ({}) via quest command, but it cannot be placed in their inventory", c->lobby_client_id, item.id, name); } @@ -4084,7 +4084,7 @@ asio::awaitable on_transfer_item_via_mail_message_bb(shared_ptr c, auto item = p->remove_item(cmd.item_id, cmd.amount, limits); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} sent inventory item {}:{:08X} ({}) x{} to player {:08X}", c->lobby_client_id, cmd.header.client_id, cmd.item_id, name, cmd.amount, cmd.target_guild_card_number); c->print_inventory(); @@ -4151,7 +4151,7 @@ static asio::awaitable on_exchange_item_for_team_points_bb(shared_ptrteam_index->add_member_points(c->login->account->account_id, points); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} exchanged inventory item {}:{:08X} ({}) for {} team points", c->lobby_client_id, cmd.header.client_id, cmd.item_id, name, points); c->print_inventory(); @@ -4184,7 +4184,7 @@ static asio::awaitable on_destroy_inventory_item(shared_ptr c, Sub auto item = p->remove_item(cmd.item_id, cmd.amount, *s->item_stack_limits(c->version())); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} destroyed inventory item {}:{:08X} ({})", c->lobby_client_id, cmd.header.client_id, cmd.item_id, name); c->print_inventory(); @@ -4236,7 +4236,7 @@ static asio::awaitable on_destroy_floor_item(shared_ptr c, Subcomm c->lobby_client_id, cmd.item_id); } else { - auto name = s->describe_item(c->version(), fi->data, false); + auto name = s->describe_item(c->version(), fi->data); l->log.info_f("Player {} destroyed floor item {:08X} ({})", c->lobby_client_id, cmd.item_id, name); // Only forward to players for whom the item was visible @@ -4342,7 +4342,7 @@ static asio::awaitable on_sell_item_at_shop_bb(shared_ptr c, Subco p->add_meseta(price); if (l->log.should_log(phosg::LogLevel::L_INFO)) { - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} sold inventory item {:08X} ({}) for {} Meseta", c->lobby_client_id, cmd.item_id, name, price); c->print_inventory(); @@ -4385,7 +4385,7 @@ static asio::awaitable on_buy_shop_item_bb(shared_ptr c, Subcomman if (l->log.should_log(phosg::LogLevel::L_INFO)) { auto s = c->require_server_state(); - auto name = s->describe_item(c->version(), item, false); + auto name = s->describe_item(c->version(), item); l->log.info_f("Player {} purchased item {:08X} ({}) for {} meseta", c->lobby_client_id, item.id, name, price); c->print_inventory(); @@ -5048,12 +5048,12 @@ static asio::awaitable on_quest_F960_result_bb(shared_ptr c, Subco p->add_item(item, *s->item_stack_limits(c->version())); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); if (c->log.should_log(phosg::LogLevel::L_INFO)) { - string name = s->describe_item(c->version(), item, false); + string name = s->describe_item(c->version(), item); c->log.info_f("Awarded item {}", name); } } catch (const out_of_range&) { if (c->log.should_log(phosg::LogLevel::L_INFO)) { - string name = s->describe_item(c->version(), item, false); + string name = s->describe_item(c->version(), item); c->log.info_f("Attempted to award item {}, but inventory was full", name); } } diff --git a/src/ServerState.cc b/src/ServerState.cc index 29f22c95..a4f5fb35 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -501,13 +501,13 @@ shared_ptr ServerState::item_name_index(Version version) co return ret; } -string ServerState::describe_item(Version version, const ItemData& item, bool include_color_codes) const { +string ServerState::describe_item(Version version, const ItemData& item, uint8_t flags) const { if (is_v1(version)) { ItemData encoded = item; encoded.encode_for_version(version, this->item_parameter_table(version)); - return this->item_name_index(version)->describe_item(encoded, include_color_codes); + return this->item_name_index(version)->describe_item(encoded, flags); } else { - return this->item_name_index(version)->describe_item(item, include_color_codes); + return this->item_name_index(version)->describe_item(item, flags); } } diff --git a/src/ServerState.hh b/src/ServerState.hh index 8ae12b21..ddcc4b63 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -351,7 +351,7 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr item_stack_limits(Version version) const; std::shared_ptr item_name_index_opt(Version version) const; // Returns null if missing std::shared_ptr item_name_index(Version version) const; // Throws if missing - std::string describe_item(Version version, const ItemData& item, bool include_color_codes) const; + std::string describe_item(Version version, const ItemData& item, uint8_t flags = 0) const; ItemData parse_item_description(Version version, const std::string& description) const; const std::vector& public_lobby_search_order(Version version, bool is_client_customization) const; diff --git a/src/ShellCommands.cc b/src/ShellCommands.cc index 9121a8ca..0af5bbc3 100644 --- a/src/ShellCommands.cc +++ b/src/ShellCommands.cc @@ -1072,7 +1072,7 @@ ShellCommand c_create_item( send_drop_stacked_item_to_channel(args.s, c->channel, item, c->floor, c->pos); send_drop_stacked_item_to_channel(args.s, c->proxy_session->server_channel, item, c->floor, c->pos); - string name = args.s->describe_item(c->version(), item, true); + string name = args.s->describe_item(c->version(), item, ItemNameIndex::Flag::INCLUDE_PSO_COLOR_ESCAPES); send_text_message(c->channel, "$C7Item created:\n" + name); co_return deque{}; });