Fix vanilla spell cast and use-item packet formats for Turtle/Classic

SpellCastTargets target mask is uint16 in vanilla 1.12.x, not uint32
like WotLK. The 2 extra bytes corrupted every spell packet. Also add
classic CMSG_USE_ITEM builder (bag+slot+spellIndex+targets only, no
spellId/itemGuid/glyphIndex/castFlags fields that WotLK added).
This commit is contained in:
Kelsi 2026-02-14 21:29:44 -08:00
parent eb931ce0fc
commit 0d4eff65d0
3 changed files with 27 additions and 5 deletions

View file

@ -50,6 +50,11 @@ public:
return CastSpellPacket::build(spellId, targetGuid, castCount); return CastSpellPacket::build(spellId, targetGuid, castCount);
} }
/** Build CMSG_USE_ITEM (WotLK default: bag + slot + castCount + spellId + itemGuid + glyphIndex + castFlags + targets) */
virtual network::Packet buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid) {
return UseItemPacket::build(bagIndex, slotIndex, itemGuid);
}
// --- Character Enumeration --- // --- Character Enumeration ---
/** Parse SMSG_CHAR_ENUM */ /** Parse SMSG_CHAR_ENUM */
@ -238,6 +243,7 @@ public:
const MovementInfo& info, const MovementInfo& info,
uint64_t playerGuid = 0) override; uint64_t playerGuid = 0) override;
network::Packet buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t castCount) override; network::Packet buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t castCount) override;
network::Packet buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid) override;
bool parseCastFailed(network::Packet& packet, CastFailedData& data) override; bool parseCastFailed(network::Packet& packet, CastFailedData& data) override;
bool parseMessageChat(network::Packet& packet, MessageChatData& data) override; bool parseMessageChat(network::Packet& packet, MessageChatData& data) override;
bool parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) override; bool parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) override;

View file

@ -7626,7 +7626,9 @@ void GameHandler::useItemBySlot(int backpackIndex) {
} }
if (itemGuid != 0 && state == WorldState::IN_WORLD && socket) { if (itemGuid != 0 && state == WorldState::IN_WORLD && socket) {
// WoW inventory: equipment 0-18, bags 19-22, backpack 23-38 // WoW inventory: equipment 0-18, bags 19-22, backpack 23-38
auto packet = UseItemPacket::build(0xFF, static_cast<uint8_t>(23 + backpackIndex), itemGuid); auto packet = packetParsers_
? packetParsers_->buildUseItem(0xFF, static_cast<uint8_t>(23 + backpackIndex), itemGuid)
: UseItemPacket::build(0xFF, static_cast<uint8_t>(23 + backpackIndex), itemGuid);
socket->send(packet); socket->send(packet);
} else if (itemGuid == 0) { } else if (itemGuid == 0) {
LOG_WARNING("Use item failed: missing item GUID for slot ", backpackIndex); LOG_WARNING("Use item failed: missing item GUID for slot ", backpackIndex);

View file

@ -260,16 +260,16 @@ network::Packet ClassicPacketParsers::buildMovementPacket(LogicalOpcode opcode,
// ============================================================================ // ============================================================================
// Classic buildCastSpell // Classic buildCastSpell
// Vanilla 1.12.x: NO castCount prefix, NO castFlags byte // Vanilla 1.12.x: NO castCount prefix, NO castFlags byte
// Format: uint32 spellId + uint32 targetFlags + [PackedGuid if unit target] // Format: uint32 spellId + uint16 targetFlags + [PackedGuid if unit target]
// ============================================================================ // ============================================================================
network::Packet ClassicPacketParsers::buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t /*castCount*/) { network::Packet ClassicPacketParsers::buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t /*castCount*/) {
network::Packet packet(wireOpcode(LogicalOpcode::CMSG_CAST_SPELL)); network::Packet packet(wireOpcode(LogicalOpcode::CMSG_CAST_SPELL));
packet.writeUInt32(spellId); packet.writeUInt32(spellId);
// SpellCastTargets — vanilla/CMaNGOS uses uint32 target mask (same as WotLK) // SpellCastTargets — vanilla/CMaNGOS uses uint16 target mask (WotLK uses uint32)
if (targetGuid != 0) { if (targetGuid != 0) {
packet.writeUInt32(0x02); // TARGET_FLAG_UNIT packet.writeUInt16(0x02); // TARGET_FLAG_UNIT
// Write packed GUID // Write packed GUID
uint8_t mask = 0; uint8_t mask = 0;
@ -289,12 +289,26 @@ network::Packet ClassicPacketParsers::buildCastSpell(uint32_t spellId, uint64_t
packet.writeUInt8(bytes[i]); packet.writeUInt8(bytes[i]);
} }
} else { } else {
packet.writeUInt32(0x00); // TARGET_FLAG_SELF packet.writeUInt16(0x00); // TARGET_FLAG_SELF
} }
return packet; return packet;
} }
// ============================================================================
// Classic CMSG_USE_ITEM
// Vanilla 1.12.x: bag(u8) + slot(u8) + spellIndex(u8) + SpellCastTargets(u16)
// NO spellId, itemGuid, glyphIndex, or castFlags fields (those are WotLK)
// ============================================================================
network::Packet ClassicPacketParsers::buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t /*itemGuid*/) {
network::Packet packet(wireOpcode(LogicalOpcode::CMSG_USE_ITEM));
packet.writeUInt8(bagIndex);
packet.writeUInt8(slotIndex);
packet.writeUInt8(0); // spell_index (which item spell to trigger, usually 0)
packet.writeUInt16(0x0000); // SpellCastTargets: TARGET_FLAG_SELF
return packet;
}
// ============================================================================ // ============================================================================
// Classic SMSG_CAST_FAILED: no castCount byte (added in TBC/WotLK) // Classic SMSG_CAST_FAILED: no castCount byte (added in TBC/WotLK)
// Format: spellId(u32) + result(u8) // Format: spellId(u32) + result(u8)