diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 3c34882e..53029471 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -457,6 +457,15 @@ public: bool hasPet() const { return petGuid_ != 0; } uint64_t getPetGuid() const { return petGuid_; } const std::unordered_set& getKnownSpells() const { return knownSpells; } + + // Player proficiency bitmasks (from SMSG_SET_PROFICIENCY) + // itemClass 2 = Weapon (subClassMask bits: 0=Axe1H,1=Axe2H,2=Bow,3=Gun,4=Mace1H,5=Mace2H,6=Polearm,7=Sword1H,8=Sword2H,10=Staff,13=Fist,14=Misc,15=Dagger,16=Thrown,17=Crossbow,18=Wand,19=Fishing) + // itemClass 4 = Armor (subClassMask bits: 1=Cloth,2=Leather,3=Mail,4=Plate,6=Shield) + uint32_t getWeaponProficiency() const { return weaponProficiency_; } + uint32_t getArmorProficiency() const { return armorProficiency_; } + bool canUseWeaponSubclass(uint32_t subClass) const { return (weaponProficiency_ >> subClass) & 1u; } + bool canUseArmorSubclass(uint32_t subClass) const { return (armorProficiency_ >> subClass) & 1u; } + bool isCasting() const { return casting; } bool isGameObjectInteractionCasting() const { return casting && currentCastSpellId == 0 && pendingGameObjectInteractGuid_ != 0; @@ -1687,6 +1696,8 @@ private: std::unique_ptr transportManager_; // Transport movement manager std::unordered_set knownSpells; std::unordered_map spellCooldowns; // spellId -> remaining seconds + uint32_t weaponProficiency_ = 0; // bitmask from SMSG_SET_PROFICIENCY itemClass=2 + uint32_t armorProficiency_ = 0; // bitmask from SMSG_SET_PROFICIENCY itemClass=4 uint8_t castCount = 0; bool casting = false; uint32_t currentCastSpellId = 0; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 894afc4d..1c4f0e70 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -3118,9 +3118,20 @@ void GameHandler::handlePacket(network::Packet& packet) { packet.setReadPos(packet.getSize()); break; } - case Opcode::SMSG_SET_PROFICIENCY: - packet.setReadPos(packet.getSize()); + case Opcode::SMSG_SET_PROFICIENCY: { + // uint8 itemClass + uint32 itemSubClassMask + if (packet.getSize() - packet.getReadPos() < 5) break; + uint8_t itemClass = packet.readUInt8(); + uint32_t mask = packet.readUInt32(); + if (itemClass == 2) { // Weapon + weaponProficiency_ = mask; + LOG_DEBUG("SMSG_SET_PROFICIENCY: weapon mask=0x", std::hex, mask, std::dec); + } else if (itemClass == 4) { // Armor + armorProficiency_ = mask; + LOG_DEBUG("SMSG_SET_PROFICIENCY: armor mask=0x", std::hex, mask, std::dec); + } break; + } case Opcode::SMSG_ACTION_BUTTONS: { // uint8 mode (0=initial, 1=update) + 144 × uint32 packed buttons