diff --git a/src/ItemData.cc b/src/ItemData.cc index 8174c2ea..ab42465a 100644 --- a/src/ItemData.cc +++ b/src/ItemData.cc @@ -567,6 +567,26 @@ bool ItemData::is_s_rank_weapon() const { return false; } +EquipSlot ItemData::default_equip_slot() const { + switch (this->data1[0]) { + case 0x00: + return EquipSlot::WEAPON; + case 0x01: + switch (this->data1[1]) { + case 0x01: + return EquipSlot::ARMOR; + case 0x02: + return EquipSlot::SHIELD; + case 0x03: + return EquipSlot::UNIT_1; + } + break; + case 0x02: + return EquipSlot::MAG; + } + throw runtime_error("item cannot be equipped"); +} + bool ItemData::can_be_equipped_in_slot(EquipSlot slot) const { switch (slot) { case EquipSlot::MAG: diff --git a/src/ItemData.hh b/src/ItemData.hh index a4b4640a..a1979c55 100644 --- a/src/ItemData.hh +++ b/src/ItemData.hh @@ -11,6 +11,13 @@ constexpr uint32_t MESETA_IDENTIFIER = 0x040000; class ItemParameterTable; enum class EquipSlot { + // When equipping items through the Item Pack pause menu, the client sends + // UNKNOWN for the slot. The receiving client (and server, in our case) have + // to analyze the item being equipped and put it in the appropriate slot in + // this case. See ItemData::default_equip_slot() for this computation. + UNKNOWN = 0x00, + // When equipping items through the quick menu or Equip pause menu, the client + // sends one of the slots below. MAG = 0x01, ARMOR = 0x02, SHIELD = 0x03, @@ -163,6 +170,7 @@ struct ItemData { // 0x14 bytes bool has_bonuses() const; bool is_s_rank_weapon() const; + EquipSlot default_equip_slot() const; bool can_be_equipped_in_slot(EquipSlot slot) const; bool empty() const; diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index a2c10872..d9d82980 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -570,6 +570,10 @@ void PlayerInventory::equip_item(uint32_t item_id, EquipSlot slot) { size_t index = this->find_item(item_id); auto& item = this->items[index]; + if (slot == EquipSlot::UNKNOWN) { + slot = item.data.default_equip_slot(); + } + if (!item.data.can_be_equipped_in_slot(slot)) { throw runtime_error("incorrect item type for equip slot"); } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 730d90fd..fbfb60f7 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1155,11 +1155,10 @@ static void on_equip_item(shared_ptr c, uint8_t command, uint8_t flag, c auto l = c->require_lobby(); if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) { - if (l->base_version == GameVersion::BB) { - EquipSlot slot = static_cast(cmd.equip_slot.load()); - auto p = c->game_data.character(); - p->inventory.equip_item(cmd.item_id, slot); - } + EquipSlot slot = static_cast(cmd.equip_slot.load()); + auto p = c->game_data.character(); + p->inventory.equip_item(cmd.item_id, slot); + c->log.info("Equipped item %08" PRIX32, cmd.item_id.load()); } else if (l->base_version == GameVersion::BB) { throw logic_error("item tracking not enabled in BB game"); } @@ -1176,10 +1175,9 @@ static void on_unequip_item(shared_ptr c, uint8_t command, uint8_t flag, auto l = c->require_lobby(); if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) { - if (l->base_version == GameVersion::BB) { - auto p = c->game_data.character(); - p->inventory.unequip_item(cmd.item_id); - } + auto p = c->game_data.character(); + p->inventory.unequip_item(cmd.item_id); + c->log.info("Unequipped item %08" PRIX32, cmd.item_id.load()); } else if (l->base_version == GameVersion::BB) { throw logic_error("item tracking not enabled in BB game"); }