From 61003b509afe2697294c97a7ab0cc2214294ec16 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 18 Aug 2024 11:00:57 -0700 Subject: [PATCH] add $killcount command --- README.md | 1 + src/ChatCommands.cc | 30 ++++++++++++++++++++++++++++++ src/ItemNameIndex.cc | 10 ++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2158af92..d472c6b7 100644 --- a/README.md +++ b/README.md @@ -506,6 +506,7 @@ Some commands only work on the game server and not on the proxy server. The chat * `$si` (game server only): Show basic information about the server. * `$ping`: Show round-trip ping time from the server to you. On the proxy server, show the ping time from you to the proxy and from the proxy to the server. * `$matcount` (game server only): Show how many of each type of material you've used. + * `$killcount` (game server only): Show the kill count on your currently-equipped weapon. If you're in a game and not on BB, the value is only accurate at the time the item enters the game. * `$itemnotifs `: Enable item drop notification messages. If the game has private drops enabled, you will only see a notification if the dropped item is visible to you; you won't be notified of other players' drops. The modes are: * `off`: No notifications are shown. * `rare`: You are notified when a rare item drops. diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 5f89de15..8675703a 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -724,6 +724,35 @@ static void server_command_show_material_counts(shared_ptr c, const std: } } +static void server_command_show_kill_count(shared_ptr c, const std::string&) { + auto p = c->character(); + size_t item_index; + try { + item_index = p->inventory.find_equipped_item(EquipSlot::WEAPON); + } catch (const out_of_range&) { + send_text_message(c, "No weapon equipped"); + return; + } + + const auto& item = p->inventory.items.at(item_index); + if (!item.data.has_kill_count()) { + send_text_message(c, "Weapon does not\nhave a kill count"); + return; + } + + // 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 ((c->version() == Version::BB_V4) || !c->require_lobby()->is_game()) { + send_text_message_printf(c, "%hu kills", item.data.get_kill_count()); + } else { + send_text_message_printf(c, "%hu kills as of\ngame join", item.data.get_kill_count()); + } +} + static void server_command_auction(shared_ptr c, const std::string&) { check_account_flag(c, Account::Flag::DEBUG); auto l = c->require_lobby(); @@ -2536,6 +2565,7 @@ static const unordered_map chat_commands({ {"$itemnotifs", {server_command_item_notifs, proxy_command_item_notifs}}, {"$i", {server_command_item, proxy_command_item}}, {"$kick", {server_command_kick, nullptr}}, + {"$killcount", {server_command_show_kill_count, nullptr}}, {"$li", {server_command_lobby_info, proxy_command_lobby_info}}, {"$ln", {server_command_lobby_type, proxy_command_lobby_type}}, {"$loadchar", {server_command_loadchar, nullptr}}, diff --git a/src/ItemNameIndex.cc b/src/ItemNameIndex.cc index d4e45be1..305db5e8 100644 --- a/src/ItemNameIndex.cc +++ b/src/ItemNameIndex.cc @@ -480,7 +480,8 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript ret.data1[2] = (primary_identifier >> 8) & 0xFF; if (ret.data1[0] == 0x00) { - // Weapons: add special, grind and percentages (or name, if S-rank) + // Weapons: add special, grind and percentages (or name, if S-rank) and + // kill count if unsealable ret.data1[4] = weapon_special | (is_wrapped ? 0x40 : 0x00) | (is_unidentified ? 0x80 : 0x00); auto tokens = phosg::split(desc, ' '); @@ -516,13 +517,14 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript if (p_tokens.size() > 5) { throw runtime_error("invalid bonuses token"); } + uint8_t max_bonuses = this->item_parameter_table->is_unsealable_item(ret) ? 2 : 3; uint8_t bonus_index = 0; for (size_t z = 0; z < p_tokens.size(); z++) { int8_t bonus_value = stol(p_tokens[z], nullptr, 10); if (bonus_value == 0) { continue; } - if (bonus_index >= 3) { + if (bonus_index >= max_bonuses) { throw runtime_error("weapon has too many bonuses"); } ret.data1[6 + (2 * bonus_index)] = z + 1; @@ -532,6 +534,10 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript } } + if (this->item_parameter_table->is_unsealable_item(ret)) { + ret.set_kill_count(0); + } + } else if (ret.data1[0] == 0x01) { if (ret.data1[1] == 0x03) { // Unit static const unordered_map modifiers({