diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 0809ac43..4d308028 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -2231,7 +2231,9 @@ struct TrainerListData { class TrainerListParser { public: - static bool parse(network::Packet& packet, TrainerListData& data); + // isClassic: Classic 1.12 per-spell layout has no profDialog/profButton fields + // (reqLevel immediately follows cost), plus a trailing unk uint32 per entry. + static bool parse(network::Packet& packet, TrainerListData& data, bool isClassic = false); }; class TrainerBuySpellPacket { diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index a22aa7c2..01679430 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -15012,7 +15012,8 @@ void GameHandler::handleListInventory(network::Packet& packet) { // ============================================================ void GameHandler::handleTrainerList(network::Packet& packet) { - if (!TrainerListParser::parse(packet, currentTrainerList_)) return; + const bool isClassic = isClassicLikeExpansion(); + if (!TrainerListParser::parse(packet, currentTrainerList_, isClassic)) return; trainerWindowOpen_ = true; gossipWindowOpen = false; diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 275ad5b8..c88f3750 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -3833,7 +3833,11 @@ bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data // Trainer // ============================================================ -bool TrainerListParser::parse(network::Packet& packet, TrainerListData& data) { +bool TrainerListParser::parse(network::Packet& packet, TrainerListData& data, bool isClassic) { + // WotLK per-entry: spellId(4) + state(1) + cost(4) + profDialog(4) + profButton(4) + + // reqLevel(1) + reqSkill(4) + reqSkillValue(4) + chainĂ—3(12) = 38 bytes + // Classic per-entry: spellId(4) + state(1) + cost(4) + reqLevel(1) + + // reqSkill(4) + reqSkillValue(4) + chainĂ—3(12) + unk(4) = 34 bytes data = TrainerListData{}; data.trainerGuid = packet.readUInt64(); data.trainerType = packet.readUInt32(); @@ -3847,23 +3851,35 @@ bool TrainerListParser::parse(network::Packet& packet, TrainerListData& data) { data.spells.reserve(spellCount); for (uint32_t i = 0; i < spellCount; ++i) { TrainerSpell spell; - spell.spellId = packet.readUInt32(); - spell.state = packet.readUInt8(); - spell.spellCost = packet.readUInt32(); - spell.profDialog = packet.readUInt32(); - spell.profButton = packet.readUInt32(); - spell.reqLevel = packet.readUInt8(); - spell.reqSkill = packet.readUInt32(); + spell.spellId = packet.readUInt32(); + spell.state = packet.readUInt8(); + spell.spellCost = packet.readUInt32(); + if (isClassic) { + // Classic 1.12: reqLevel immediately after cost; no profDialog/profButton + spell.profDialog = 0; + spell.profButton = 0; + spell.reqLevel = packet.readUInt8(); + } else { + // TBC / WotLK: profDialog + profButton before reqLevel + spell.profDialog = packet.readUInt32(); + spell.profButton = packet.readUInt32(); + spell.reqLevel = packet.readUInt8(); + } + spell.reqSkill = packet.readUInt32(); spell.reqSkillValue = packet.readUInt32(); - spell.chainNode1 = packet.readUInt32(); - spell.chainNode2 = packet.readUInt32(); - spell.chainNode3 = packet.readUInt32(); + spell.chainNode1 = packet.readUInt32(); + spell.chainNode2 = packet.readUInt32(); + spell.chainNode3 = packet.readUInt32(); + if (isClassic) { + packet.readUInt32(); // trailing unk / sort index + } data.spells.push_back(spell); } data.greeting = packet.readString(); - LOG_INFO("Trainer list: ", spellCount, " spells, type=", data.trainerType, + LOG_INFO("Trainer list (", isClassic ? "Classic" : "TBC/WotLK", "): ", + spellCount, " spells, type=", data.trainerType, ", greeting=\"", data.greeting, "\""); return true; }