Implement SMSG_SET_PROFICIENCY: track weapon/armor proficiency bitmasks

- Parse uint8 itemClass + uint32 subClassMask from SMSG_SET_PROFICIENCY
- Store weaponProficiency_ (itemClass=2) and armorProficiency_ (itemClass=4)
- Expose getWeaponProficiency(), getArmorProficiency(), canUseWeaponSubclass(n),
  canUseArmorSubclass(n) on GameHandler for use by equipment UI
- Enables future equipment slot validation (grey out non-proficient items)
This commit is contained in:
Kelsi 2026-03-09 20:23:38 -07:00
parent 926bcbb50e
commit 5024e8cb32
2 changed files with 24 additions and 2 deletions

View file

@ -457,6 +457,15 @@ public:
bool hasPet() const { return petGuid_ != 0; }
uint64_t getPetGuid() const { return petGuid_; }
const std::unordered_set<uint32_t>& 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> transportManager_; // Transport movement manager
std::unordered_set<uint32_t> knownSpells;
std::unordered_map<uint32_t, float> 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;

View file

@ -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