2026-02-02 12:24:50 -08:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include "game/world_packets.hpp"
|
|
|
|
|
|
#include "game/character.hpp"
|
2026-02-12 22:56:36 -08:00
|
|
|
|
#include "game/opcode_table.hpp"
|
|
|
|
|
|
#include "game/update_field_table.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
|
#include "game/inventory.hpp"
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
#include "game/spell_defines.hpp"
|
|
|
|
|
|
#include "game/group_defines.hpp"
|
2026-02-08 00:59:40 -08:00
|
|
|
|
#include <glm/glm.hpp>
|
2026-02-02 12:24:50 -08:00
|
|
|
|
#include <memory>
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <vector>
|
2026-02-04 11:31:08 -08:00
|
|
|
|
#include <deque>
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
#include <array>
|
2026-02-02 12:24:50 -08:00
|
|
|
|
#include <functional>
|
|
|
|
|
|
#include <cstdint>
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
#include <unordered_set>
|
2026-02-07 14:21:50 -08:00
|
|
|
|
#include <map>
|
2026-02-12 00:04:53 -08:00
|
|
|
|
#include <optional>
|
2026-02-12 00:45:24 -08:00
|
|
|
|
#include <algorithm>
|
2026-02-20 02:19:17 -08:00
|
|
|
|
#include <chrono>
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
2026-02-10 21:29:10 -08:00
|
|
|
|
namespace wowee::game {
|
|
|
|
|
|
class TransportManager;
|
2026-02-12 02:09:15 -08:00
|
|
|
|
class WardenCrypto;
|
2026-02-14 02:00:15 -08:00
|
|
|
|
class WardenMemory;
|
2026-02-14 19:20:32 -08:00
|
|
|
|
class WardenModule;
|
2026-02-12 02:43:20 -08:00
|
|
|
|
class WardenModuleManager;
|
2026-02-12 22:56:36 -08:00
|
|
|
|
class PacketParsers;
|
2026-02-10 21:29:10 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
namespace wowee {
|
|
|
|
|
|
namespace network { class WorldSocket; class Packet; }
|
|
|
|
|
|
|
|
|
|
|
|
namespace game {
|
|
|
|
|
|
|
2026-02-07 14:21:50 -08:00
|
|
|
|
struct PlayerSkill {
|
|
|
|
|
|
uint32_t skillId = 0;
|
|
|
|
|
|
uint16_t value = 0;
|
|
|
|
|
|
uint16_t maxValue = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-06 20:10:10 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Quest giver status values (WoW 3.3.5a)
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum class QuestGiverStatus : uint8_t {
|
|
|
|
|
|
NONE = 0,
|
|
|
|
|
|
UNAVAILABLE = 1,
|
|
|
|
|
|
INCOMPLETE = 5, // ? (gray)
|
|
|
|
|
|
REWARD_REP = 6,
|
|
|
|
|
|
AVAILABLE_LOW = 7, // ! (gray, low-level)
|
|
|
|
|
|
AVAILABLE = 8, // ! (yellow)
|
|
|
|
|
|
REWARD = 10 // ? (yellow)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-10 05:46:03 -07:00
|
|
|
|
/**
|
|
|
|
|
|
* A single contact list entry (friend, ignore, or mute).
|
|
|
|
|
|
*/
|
|
|
|
|
|
struct ContactEntry {
|
|
|
|
|
|
uint64_t guid = 0;
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
std::string note;
|
|
|
|
|
|
uint32_t flags = 0; // 0x1=friend, 0x2=ignore, 0x4=mute
|
|
|
|
|
|
uint8_t status = 0; // 0=offline, 1=online, 2=AFK, 3=DND
|
|
|
|
|
|
uint32_t areaId = 0;
|
|
|
|
|
|
uint32_t level = 0;
|
|
|
|
|
|
uint32_t classId = 0;
|
|
|
|
|
|
|
|
|
|
|
|
bool isFriend() const { return (flags & 0x1) != 0; }
|
|
|
|
|
|
bool isIgnored() const { return (flags & 0x2) != 0; }
|
|
|
|
|
|
bool isOnline() const { return status != 0; }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* World connection state
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum class WorldState {
|
|
|
|
|
|
DISCONNECTED, // Not connected
|
|
|
|
|
|
CONNECTING, // TCP connection in progress
|
|
|
|
|
|
CONNECTED, // Connected, waiting for challenge
|
|
|
|
|
|
CHALLENGE_RECEIVED, // Received SMSG_AUTH_CHALLENGE
|
|
|
|
|
|
AUTH_SENT, // Sent CMSG_AUTH_SESSION, encryption initialized
|
|
|
|
|
|
AUTHENTICATED, // Received SMSG_AUTH_RESPONSE success
|
|
|
|
|
|
READY, // Ready for character/world operations
|
|
|
|
|
|
CHAR_LIST_REQUESTED, // CMSG_CHAR_ENUM sent
|
|
|
|
|
|
CHAR_LIST_RECEIVED, // SMSG_CHAR_ENUM received
|
|
|
|
|
|
ENTERING_WORLD, // CMSG_PLAYER_LOGIN sent
|
|
|
|
|
|
IN_WORLD, // In game world
|
|
|
|
|
|
FAILED // Connection or authentication failed
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* World connection callbacks
|
|
|
|
|
|
*/
|
|
|
|
|
|
using WorldConnectSuccessCallback = std::function<void()>;
|
|
|
|
|
|
using WorldConnectFailureCallback = std::function<void(const std::string& reason)>;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* GameHandler - Manages world server connection and game protocol
|
|
|
|
|
|
*
|
|
|
|
|
|
* Handles:
|
|
|
|
|
|
* - Connection to world server
|
|
|
|
|
|
* - Authentication with session key from auth server
|
|
|
|
|
|
* - RC4 header encryption
|
|
|
|
|
|
* - Character enumeration
|
|
|
|
|
|
* - World entry
|
|
|
|
|
|
* - Game packets
|
|
|
|
|
|
*/
|
|
|
|
|
|
class GameHandler {
|
|
|
|
|
|
public:
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
|
// Talent data structures (must be public for use in templates)
|
|
|
|
|
|
struct TalentEntry {
|
|
|
|
|
|
uint32_t talentId = 0;
|
|
|
|
|
|
uint32_t tabId = 0; // Which talent tree
|
|
|
|
|
|
uint8_t row = 0; // Tier (0-10)
|
|
|
|
|
|
uint8_t column = 0; // Column (0-3)
|
|
|
|
|
|
uint32_t rankSpells[5] = {}; // Spell IDs for ranks 1-5
|
|
|
|
|
|
uint32_t prereqTalent[3] = {}; // Required talents
|
|
|
|
|
|
uint8_t prereqRank[3] = {}; // Required ranks
|
|
|
|
|
|
uint8_t maxRank = 0; // Number of ranks (1-5)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct TalentTabEntry {
|
|
|
|
|
|
uint32_t tabId = 0;
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
uint32_t classMask = 0; // Which classes can use this tab
|
|
|
|
|
|
uint8_t orderIndex = 0; // Display order (0-2)
|
|
|
|
|
|
std::string backgroundFile; // Texture path
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
GameHandler();
|
|
|
|
|
|
~GameHandler();
|
|
|
|
|
|
|
2026-02-12 22:56:36 -08:00
|
|
|
|
/** Access the active opcode table (wire ↔ logical mapping). */
|
|
|
|
|
|
const OpcodeTable& getOpcodeTable() const { return opcodeTable_; }
|
|
|
|
|
|
OpcodeTable& getOpcodeTable() { return opcodeTable_; }
|
|
|
|
|
|
const UpdateFieldTable& getUpdateFieldTable() const { return updateFieldTable_; }
|
|
|
|
|
|
UpdateFieldTable& getUpdateFieldTable() { return updateFieldTable_; }
|
|
|
|
|
|
PacketParsers* getPacketParsers() { return packetParsers_.get(); }
|
|
|
|
|
|
void setPacketParsers(std::unique_ptr<PacketParsers> parsers);
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Connect to world server
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param host World server hostname/IP
|
|
|
|
|
|
* @param port World server port (default 8085)
|
|
|
|
|
|
* @param sessionKey 40-byte session key from auth server
|
|
|
|
|
|
* @param accountName Account name (will be uppercased)
|
|
|
|
|
|
* @param build Client build number (default 12340 for 3.3.5a)
|
|
|
|
|
|
* @return true if connection initiated
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool connect(const std::string& host,
|
|
|
|
|
|
uint16_t port,
|
|
|
|
|
|
const std::vector<uint8_t>& sessionKey,
|
|
|
|
|
|
const std::string& accountName,
|
2026-02-13 01:51:49 -08:00
|
|
|
|
uint32_t build = 12340,
|
|
|
|
|
|
uint32_t realmId = 0);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Disconnect from world server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void disconnect();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Check if connected to world server
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool isConnected() const;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get current connection state
|
|
|
|
|
|
*/
|
|
|
|
|
|
WorldState getState() const { return state; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Request character list from server
|
|
|
|
|
|
* Must be called when state is READY or AUTHENTICATED
|
|
|
|
|
|
*/
|
|
|
|
|
|
void requestCharacterList();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get list of characters (available after CHAR_LIST_RECEIVED state)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const std::vector<Character>& getCharacters() const { return characters; }
|
|
|
|
|
|
|
2026-02-05 14:13:48 -08:00
|
|
|
|
void createCharacter(const CharCreateData& data);
|
2026-02-06 03:24:46 -08:00
|
|
|
|
void deleteCharacter(uint64_t characterGuid);
|
2026-02-05 14:13:48 -08:00
|
|
|
|
|
|
|
|
|
|
using CharCreateCallback = std::function<void(bool success, const std::string& message)>;
|
|
|
|
|
|
void setCharCreateCallback(CharCreateCallback cb) { charCreateCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-06 03:24:46 -08:00
|
|
|
|
using CharDeleteCallback = std::function<void(bool success)>;
|
|
|
|
|
|
void setCharDeleteCallback(CharDeleteCallback cb) { charDeleteCallback_ = std::move(cb); }
|
2026-02-06 18:34:45 -08:00
|
|
|
|
uint8_t getLastCharDeleteResult() const { return lastCharDeleteResult_; }
|
2026-02-06 03:24:46 -08:00
|
|
|
|
|
2026-02-17 13:59:29 -08:00
|
|
|
|
using CharLoginFailCallback = std::function<void(const std::string& reason)>;
|
|
|
|
|
|
void setCharLoginFailCallback(CharLoginFailCallback cb) { charLoginFailCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Select and log in with a character
|
|
|
|
|
|
* @param characterGuid GUID of character to log in with
|
|
|
|
|
|
*/
|
|
|
|
|
|
void selectCharacter(uint64_t characterGuid);
|
2026-02-05 14:35:12 -08:00
|
|
|
|
void setActiveCharacterGuid(uint64_t guid) { activeCharacterGuid_ = guid; }
|
|
|
|
|
|
uint64_t getActiveCharacterGuid() const { return activeCharacterGuid_; }
|
|
|
|
|
|
const Character* getActiveCharacter() const;
|
|
|
|
|
|
const Character* getFirstCharacter() const;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get current player movement info
|
|
|
|
|
|
*/
|
|
|
|
|
|
const MovementInfo& getMovementInfo() const { return movementInfo; }
|
2026-02-08 03:24:12 -08:00
|
|
|
|
uint32_t getCurrentMapId() const { return currentMapId_; }
|
2026-02-08 03:39:02 -08:00
|
|
|
|
bool getHomeBind(uint32_t& mapId, glm::vec3& pos) const {
|
|
|
|
|
|
if (!hasHomeBind_) return false;
|
|
|
|
|
|
mapId = homeBindMapId_;
|
|
|
|
|
|
pos = homeBindPos_;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Send a movement packet
|
2026-02-20 02:50:59 -08:00
|
|
|
|
* @param opcode Movement opcode (MSG_MOVE_START_FORWARD, etc.)
|
2026-02-02 12:24:50 -08:00
|
|
|
|
*/
|
|
|
|
|
|
void sendMovement(Opcode opcode);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update player position
|
|
|
|
|
|
* @param x X coordinate
|
|
|
|
|
|
* @param y Y coordinate
|
|
|
|
|
|
* @param z Z coordinate
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setPosition(float x, float y, float z);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update player orientation
|
|
|
|
|
|
* @param orientation Facing direction in radians
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setOrientation(float orientation);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Get entity manager (for accessing entities in view)
|
|
|
|
|
|
*/
|
|
|
|
|
|
EntityManager& getEntityManager() { return entityManager; }
|
|
|
|
|
|
const EntityManager& getEntityManager() const { return entityManager; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Send a chat message
|
|
|
|
|
|
* @param type Chat type (SAY, YELL, WHISPER, etc.)
|
|
|
|
|
|
* @param message Message text
|
|
|
|
|
|
* @param target Target name (for whispers, empty otherwise)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendChatMessage(ChatType type, const std::string& message, const std::string& target = "");
|
2026-02-14 14:30:09 -08:00
|
|
|
|
void sendTextEmote(uint32_t textEmoteId, uint64_t targetGuid = 0);
|
|
|
|
|
|
void joinChannel(const std::string& channelName, const std::string& password = "");
|
|
|
|
|
|
void leaveChannel(const std::string& channelName);
|
|
|
|
|
|
const std::vector<std::string>& getJoinedChannels() const { return joinedChannels_; }
|
|
|
|
|
|
std::string getChannelByIndex(int index) const;
|
2026-02-14 18:27:59 -08:00
|
|
|
|
int getChannelIndex(const std::string& channelName) const;
|
|
|
|
|
|
|
|
|
|
|
|
// Chat auto-join settings (set by UI before autoJoinDefaultChannels)
|
|
|
|
|
|
struct ChatAutoJoin {
|
|
|
|
|
|
bool general = true;
|
|
|
|
|
|
bool trade = true;
|
|
|
|
|
|
bool localDefense = true;
|
|
|
|
|
|
bool lfg = true;
|
2026-02-16 21:16:25 -08:00
|
|
|
|
bool local = true;
|
2026-02-14 18:27:59 -08:00
|
|
|
|
};
|
|
|
|
|
|
ChatAutoJoin chatAutoJoin;
|
2026-02-14 14:30:09 -08:00
|
|
|
|
|
|
|
|
|
|
// Chat bubble callback: (senderGuid, message, isYell)
|
|
|
|
|
|
using ChatBubbleCallback = std::function<void(uint64_t, const std::string&, bool)>;
|
|
|
|
|
|
void setChatBubbleCallback(ChatBubbleCallback cb) { chatBubbleCallback_ = std::move(cb); }
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
2026-02-14 15:11:43 -08:00
|
|
|
|
// Emote animation callback: (entityGuid, animationId)
|
|
|
|
|
|
using EmoteAnimCallback = std::function<void(uint64_t, uint32_t)>;
|
|
|
|
|
|
void setEmoteAnimCallback(EmoteAnimCallback cb) { emoteAnimCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Get chat history (recent messages)
|
|
|
|
|
|
* @param maxMessages Maximum number of messages to return (0 = all)
|
|
|
|
|
|
* @return Vector of chat messages
|
|
|
|
|
|
*/
|
2026-02-04 11:31:08 -08:00
|
|
|
|
const std::deque<MessageChatData>& getChatHistory() const { return chatHistory; }
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Add a locally-generated chat message (e.g., emote feedback)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void addLocalChatMessage(const MessageChatData& msg);
|
|
|
|
|
|
|
2026-02-05 14:01:26 -08:00
|
|
|
|
// Money (copper)
|
|
|
|
|
|
uint64_t getMoneyCopper() const { return playerMoneyCopper_; }
|
|
|
|
|
|
|
2026-02-19 17:45:09 -08:00
|
|
|
|
// Server-authoritative armor (UNIT_FIELD_RESISTANCES[0])
|
|
|
|
|
|
int32_t getArmorRating() const { return playerArmorRating_; }
|
|
|
|
|
|
|
2026-03-10 23:08:15 -07:00
|
|
|
|
// Server-authoritative primary stats (UNIT_FIELD_STAT0-4: STR, AGI, STA, INT, SPI).
|
|
|
|
|
|
// Returns -1 if the server hasn't sent the value yet.
|
|
|
|
|
|
int32_t getPlayerStat(int idx) const {
|
|
|
|
|
|
if (idx < 0 || idx > 4) return -1;
|
|
|
|
|
|
return playerStats_[idx];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
// Inventory
|
|
|
|
|
|
Inventory& getInventory() { return inventory; }
|
|
|
|
|
|
const Inventory& getInventory() const { return inventory; }
|
2026-02-06 03:13:42 -08:00
|
|
|
|
bool consumeOnlineEquipmentDirty() { bool d = onlineEquipDirty_; onlineEquipDirty_ = false; return d; }
|
2026-03-04 19:47:01 -08:00
|
|
|
|
void resetEquipmentDirtyTracking() { lastEquipDisplayIds_ = {}; onlineEquipDirty_ = true; }
|
2026-02-12 14:55:27 -08:00
|
|
|
|
void unequipToBackpack(EquipSlot equipSlot);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
|
|
// Targeting
|
|
|
|
|
|
void setTarget(uint64_t guid);
|
|
|
|
|
|
void clearTarget();
|
|
|
|
|
|
uint64_t getTargetGuid() const { return targetGuid; }
|
|
|
|
|
|
std::shared_ptr<Entity> getTarget() const;
|
|
|
|
|
|
bool hasTarget() const { return targetGuid != 0; }
|
|
|
|
|
|
void tabTarget(float playerX, float playerY, float playerZ);
|
|
|
|
|
|
|
2026-02-07 13:44:36 -08:00
|
|
|
|
// Focus targeting
|
|
|
|
|
|
void setFocus(uint64_t guid);
|
|
|
|
|
|
void clearFocus();
|
|
|
|
|
|
uint64_t getFocusGuid() const { return focusGuid; }
|
|
|
|
|
|
std::shared_ptr<Entity> getFocus() const;
|
|
|
|
|
|
bool hasFocus() const { return focusGuid != 0; }
|
|
|
|
|
|
|
|
|
|
|
|
// Advanced targeting
|
|
|
|
|
|
void targetLastTarget();
|
|
|
|
|
|
void targetEnemy(bool reverse = false);
|
|
|
|
|
|
void targetFriend(bool reverse = false);
|
|
|
|
|
|
|
2026-02-07 12:37:13 -08:00
|
|
|
|
// Inspection
|
|
|
|
|
|
void inspectTarget();
|
|
|
|
|
|
|
2026-02-07 12:43:32 -08:00
|
|
|
|
// Server info commands
|
|
|
|
|
|
void queryServerTime();
|
|
|
|
|
|
void requestPlayedTime();
|
|
|
|
|
|
void queryWho(const std::string& playerName = "");
|
|
|
|
|
|
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
|
// Social commands
|
|
|
|
|
|
void addFriend(const std::string& playerName, const std::string& note = "");
|
|
|
|
|
|
void removeFriend(const std::string& playerName);
|
|
|
|
|
|
void setFriendNote(const std::string& playerName, const std::string& note);
|
2026-02-07 12:58:11 -08:00
|
|
|
|
void addIgnore(const std::string& playerName);
|
|
|
|
|
|
void removeIgnore(const std::string& playerName);
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
|
|
|
|
|
|
|
// Random roll
|
|
|
|
|
|
void randomRoll(uint32_t minRoll = 1, uint32_t maxRoll = 100);
|
|
|
|
|
|
|
2026-03-10 21:12:28 -07:00
|
|
|
|
// Battleground queue slot (public so UI can read invite details)
|
|
|
|
|
|
struct BgQueueSlot {
|
|
|
|
|
|
uint32_t queueSlot = 0;
|
|
|
|
|
|
uint32_t bgTypeId = 0;
|
|
|
|
|
|
uint8_t arenaType = 0;
|
|
|
|
|
|
uint32_t statusId = 0; // 0=none, 1=wait_queue, 2=wait_join, 3=in_progress
|
|
|
|
|
|
uint32_t inviteTimeout = 80;
|
|
|
|
|
|
std::chrono::steady_clock::time_point inviteReceivedTime{};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-26 17:56:11 -08:00
|
|
|
|
// Battleground
|
|
|
|
|
|
bool hasPendingBgInvite() const;
|
|
|
|
|
|
void acceptBattlefield(uint32_t queueSlot = 0xFFFFFFFF);
|
2026-03-10 21:12:28 -07:00
|
|
|
|
void declineBattlefield(uint32_t queueSlot = 0xFFFFFFFF);
|
|
|
|
|
|
const std::array<BgQueueSlot, 3>& getBgQueues() const { return bgQueues_; }
|
2026-02-26 17:56:11 -08:00
|
|
|
|
|
2026-03-10 21:19:42 -07:00
|
|
|
|
// Network latency (milliseconds, updated each PONG response)
|
|
|
|
|
|
uint32_t getLatencyMs() const { return lastLatency; }
|
|
|
|
|
|
|
2026-02-07 12:58:11 -08:00
|
|
|
|
// Logout commands
|
|
|
|
|
|
void requestLogout();
|
|
|
|
|
|
void cancelLogout();
|
|
|
|
|
|
|
|
|
|
|
|
// Stand state
|
|
|
|
|
|
void setStandState(uint8_t state); // 0=stand, 1=sit, 2=sit_chair, 3=sleep, 4=sit_low_chair, 5=sit_medium_chair, 6=sit_high_chair, 7=dead, 8=kneel, 9=submerged
|
Implement SMSG_STANDSTATE_UPDATE and SMSG_ITEM_PUSH_RESULT handlers
SMSG_STANDSTATE_UPDATE:
- Parse uint8 stand state from server confirmation packet
- Store in standState_ member (0=stand, 7=dead, 8=kneel, etc.)
- Expose getStandState(), isSitting(), isDead(), isKneeling() accessors
SMSG_ITEM_PUSH_RESULT:
- Parse full WotLK 3.3.5a payload: guid, received, created, showInChat,
bagSlot, itemSlot, itemId, suffixFactor, randomPropertyId, count, totalCount
- Show "Received: <name> x<count>" chat notification when showInChat=1
- Queue item info lookup via queryItemInfo so name resolves asap
2026-03-09 12:58:52 -07:00
|
|
|
|
uint8_t getStandState() const { return standState_; }
|
|
|
|
|
|
bool isSitting() const { return standState_ >= 1 && standState_ <= 6; }
|
|
|
|
|
|
bool isDead() const { return standState_ == 7; }
|
|
|
|
|
|
bool isKneeling() const { return standState_ == 8; }
|
2026-02-07 12:58:11 -08:00
|
|
|
|
|
2026-02-07 13:03:21 -08:00
|
|
|
|
// Display toggles
|
|
|
|
|
|
void toggleHelm();
|
|
|
|
|
|
void toggleCloak();
|
2026-03-12 00:13:48 -07:00
|
|
|
|
bool isHelmVisible() const { return helmVisible_; }
|
|
|
|
|
|
bool isCloakVisible() const { return cloakVisible_; }
|
2026-02-07 13:03:21 -08:00
|
|
|
|
|
|
|
|
|
|
// Follow/Assist
|
|
|
|
|
|
void followTarget();
|
|
|
|
|
|
void assistTarget();
|
|
|
|
|
|
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
|
// PvP
|
|
|
|
|
|
void togglePvp();
|
|
|
|
|
|
|
2026-03-11 23:00:03 -07:00
|
|
|
|
// Minimap ping (Ctrl+click on minimap; wowX/wowY in canonical WoW coords)
|
|
|
|
|
|
void sendMinimapPing(float wowX, float wowY);
|
|
|
|
|
|
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
|
// Guild commands
|
|
|
|
|
|
void requestGuildInfo();
|
|
|
|
|
|
void requestGuildRoster();
|
|
|
|
|
|
void setGuildMotd(const std::string& motd);
|
|
|
|
|
|
void promoteGuildMember(const std::string& playerName);
|
|
|
|
|
|
void demoteGuildMember(const std::string& playerName);
|
|
|
|
|
|
void leaveGuild();
|
|
|
|
|
|
void inviteToGuild(const std::string& playerName);
|
2026-02-13 21:39:48 -08:00
|
|
|
|
void kickGuildMember(const std::string& playerName);
|
2026-02-16 20:16:14 -08:00
|
|
|
|
void disbandGuild();
|
|
|
|
|
|
void setGuildLeader(const std::string& name);
|
|
|
|
|
|
void setGuildPublicNote(const std::string& name, const std::string& note);
|
|
|
|
|
|
void setGuildOfficerNote(const std::string& name, const std::string& note);
|
2026-02-13 21:39:48 -08:00
|
|
|
|
void acceptGuildInvite();
|
|
|
|
|
|
void declineGuildInvite();
|
|
|
|
|
|
void queryGuildInfo(uint32_t guildId);
|
2026-02-25 14:44:44 -08:00
|
|
|
|
void createGuild(const std::string& guildName);
|
|
|
|
|
|
void addGuildRank(const std::string& rankName);
|
|
|
|
|
|
void deleteGuildRank();
|
|
|
|
|
|
void requestPetitionShowlist(uint64_t npcGuid);
|
|
|
|
|
|
void buyPetition(uint64_t npcGuid, const std::string& guildName);
|
2026-02-13 21:39:48 -08:00
|
|
|
|
|
|
|
|
|
|
// Guild state accessors
|
2026-02-14 15:05:18 -08:00
|
|
|
|
bool isInGuild() const {
|
|
|
|
|
|
if (!guildName_.empty()) return true;
|
|
|
|
|
|
const Character* ch = getActiveCharacter();
|
|
|
|
|
|
return ch && ch->hasGuild();
|
|
|
|
|
|
}
|
2026-02-13 21:39:48 -08:00
|
|
|
|
const std::string& getGuildName() const { return guildName_; }
|
|
|
|
|
|
const GuildRosterData& getGuildRoster() const { return guildRoster_; }
|
|
|
|
|
|
bool hasGuildRoster() const { return hasGuildRoster_; }
|
2026-02-16 20:16:14 -08:00
|
|
|
|
const std::vector<std::string>& getGuildRankNames() const { return guildRankNames_; }
|
2026-02-13 21:39:48 -08:00
|
|
|
|
bool hasPendingGuildInvite() const { return pendingGuildInvite_; }
|
|
|
|
|
|
const std::string& getPendingGuildInviterName() const { return pendingGuildInviterName_; }
|
|
|
|
|
|
const std::string& getPendingGuildInviteGuildName() const { return pendingGuildInviteGuildName_; }
|
2026-02-25 14:44:44 -08:00
|
|
|
|
const GuildInfoData& getGuildInfoData() const { return guildInfoData_; }
|
|
|
|
|
|
const GuildQueryResponseData& getGuildQueryData() const { return guildQueryData_; }
|
|
|
|
|
|
bool hasGuildInfoData() const { return guildInfoData_.isValid(); }
|
|
|
|
|
|
bool hasPetitionShowlist() const { return showPetitionDialog_; }
|
|
|
|
|
|
void clearPetitionDialog() { showPetitionDialog_ = false; }
|
|
|
|
|
|
uint32_t getPetitionCost() const { return petitionCost_; }
|
|
|
|
|
|
uint64_t getPetitionNpcGuid() const { return petitionNpcGuid_; }
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
|
|
|
|
|
|
|
// Ready check
|
|
|
|
|
|
void initiateReadyCheck();
|
|
|
|
|
|
void respondToReadyCheck(bool ready);
|
2026-03-09 14:48:30 -07:00
|
|
|
|
bool hasPendingReadyCheck() const { return pendingReadyCheck_; }
|
|
|
|
|
|
void dismissReadyCheck() { pendingReadyCheck_ = false; }
|
|
|
|
|
|
const std::string& getReadyCheckInitiator() const { return readyCheckInitiator_; }
|
Add Tier 3 commands: guild management, PvP, ready check, and duel forfeit
- Guild commands: /ginfo, /groster, /gmotd, /gpromote, /gdemote, /gquit, /ginvite
- PvP toggle: /pvp to toggle PvP flag
- Ready check system: /readycheck, /ready, /notready for group coordination
- Duel forfeit: /yield, /forfeit, /surrender to cancel active duels
2026-02-07 13:09:12 -08:00
|
|
|
|
|
|
|
|
|
|
// Duel
|
|
|
|
|
|
void forfeitDuel();
|
|
|
|
|
|
|
2026-02-07 13:17:01 -08:00
|
|
|
|
// AFK/DND status
|
|
|
|
|
|
void toggleAfk(const std::string& message = "");
|
|
|
|
|
|
void toggleDnd(const std::string& message = "");
|
2026-03-11 23:21:27 -07:00
|
|
|
|
bool isAfk() const { return afkStatus_; }
|
|
|
|
|
|
bool isDnd() const { return dndStatus_; }
|
2026-02-07 13:17:01 -08:00
|
|
|
|
void replyToLastWhisper(const std::string& message);
|
|
|
|
|
|
std::string getLastWhisperSender() const { return lastWhisperSender_; }
|
|
|
|
|
|
void setLastWhisperSender(const std::string& name) { lastWhisperSender_ = name; }
|
|
|
|
|
|
|
2026-02-07 13:28:46 -08:00
|
|
|
|
// Party/Raid management
|
|
|
|
|
|
void uninvitePlayer(const std::string& playerName);
|
|
|
|
|
|
void leaveParty();
|
|
|
|
|
|
void setMainTank(uint64_t targetGuid);
|
|
|
|
|
|
void setMainAssist(uint64_t targetGuid);
|
|
|
|
|
|
void clearMainTank();
|
|
|
|
|
|
void clearMainAssist();
|
|
|
|
|
|
void requestRaidInfo();
|
|
|
|
|
|
|
2026-02-07 13:36:50 -08:00
|
|
|
|
// Combat and Trade
|
|
|
|
|
|
void proposeDuel(uint64_t targetGuid);
|
|
|
|
|
|
void initiateTrade(uint64_t targetGuid);
|
|
|
|
|
|
void stopCasting();
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 1: Name queries ----
|
|
|
|
|
|
void queryPlayerName(uint64_t guid);
|
|
|
|
|
|
void queryCreatureInfo(uint32_t entry, uint64_t guid);
|
2026-02-08 00:59:40 -08:00
|
|
|
|
void queryGameObjectInfo(uint32_t entry, uint64_t guid);
|
2026-02-14 20:20:43 -08:00
|
|
|
|
const GameObjectQueryResponseData* getCachedGameObjectInfo(uint32_t entry) const {
|
|
|
|
|
|
auto it = gameObjectInfoCache_.find(entry);
|
|
|
|
|
|
return (it != gameObjectInfoCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
|
}
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
std::string getCachedPlayerName(uint64_t guid) const;
|
|
|
|
|
|
std::string getCachedCreatureName(uint32_t entry) const;
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Phase 2: Combat ----
|
|
|
|
|
|
void startAutoAttack(uint64_t targetGuid);
|
|
|
|
|
|
void stopAutoAttack();
|
|
|
|
|
|
bool isAutoAttacking() const { return autoAttacking; }
|
2026-02-20 03:38:12 -08:00
|
|
|
|
bool hasAutoAttackIntent() const { return autoAttackRequested_; }
|
|
|
|
|
|
bool isInCombat() const { return autoAttacking || !hostileAttackers_.empty(); }
|
|
|
|
|
|
bool isInCombatWith(uint64_t guid) const {
|
|
|
|
|
|
return guid != 0 &&
|
|
|
|
|
|
((autoAttacking && autoAttackTarget == guid) ||
|
|
|
|
|
|
(hostileAttackers_.count(guid) > 0));
|
|
|
|
|
|
}
|
|
|
|
|
|
uint64_t getAutoAttackTargetGuid() const { return autoAttackTarget; }
|
2026-02-06 18:34:45 -08:00
|
|
|
|
bool isAggressiveTowardPlayer(uint64_t guid) const { return hostileAttackers_.count(guid) > 0; }
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
const std::vector<CombatTextEntry>& getCombatText() const { return combatText; }
|
|
|
|
|
|
void updateCombatText(float deltaTime);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Phase 3: Spells ----
|
|
|
|
|
|
void castSpell(uint32_t spellId, uint64_t targetGuid = 0);
|
|
|
|
|
|
void cancelCast();
|
|
|
|
|
|
void cancelAura(uint32_t spellId);
|
2026-02-26 10:41:29 -08:00
|
|
|
|
void dismissPet();
|
|
|
|
|
|
bool hasPet() const { return petGuid_ != 0; }
|
|
|
|
|
|
uint64_t getPetGuid() const { return petGuid_; }
|
2026-03-09 22:53:09 -07:00
|
|
|
|
|
|
|
|
|
|
// ---- Pet state (populated by SMSG_PET_SPELLS / SMSG_PET_MODE) ----
|
|
|
|
|
|
// 10 action bar slots; each entry is a packed uint32:
|
|
|
|
|
|
// bits 0-23 = spell ID (or 0 for empty)
|
|
|
|
|
|
// bits 24-31 = action type (0x00=cast, 0xC0=autocast on, 0x40=autocast off)
|
|
|
|
|
|
static constexpr int PET_ACTION_BAR_SLOTS = 10;
|
|
|
|
|
|
uint32_t getPetActionSlot(int idx) const {
|
|
|
|
|
|
if (idx < 0 || idx >= PET_ACTION_BAR_SLOTS) return 0;
|
|
|
|
|
|
return petActionSlots_[idx];
|
|
|
|
|
|
}
|
|
|
|
|
|
// Pet command/react state from SMSG_PET_MODE or SMSG_PET_SPELLS
|
|
|
|
|
|
uint8_t getPetCommand() const { return petCommand_; } // 0=stay,1=follow,2=attack,3=dismiss
|
|
|
|
|
|
uint8_t getPetReact() const { return petReact_; } // 0=passive,1=defensive,2=aggressive
|
|
|
|
|
|
// Spells the pet knows (from SMSG_PET_SPELLS spell list)
|
|
|
|
|
|
const std::vector<uint32_t>& getPetSpells() const { return petSpellList_; }
|
|
|
|
|
|
// Pet autocast set (spellIds that have autocast enabled)
|
|
|
|
|
|
bool isPetSpellAutocast(uint32_t spellId) const {
|
|
|
|
|
|
return petAutocastSpells_.count(spellId) != 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Send CMSG_PET_ACTION to issue a pet command
|
|
|
|
|
|
void sendPetAction(uint32_t action, uint64_t targetGuid = 0);
|
2026-02-17 15:13:54 -08:00
|
|
|
|
const std::unordered_set<uint32_t>& getKnownSpells() const { return knownSpells; }
|
2026-03-09 20:23:38 -07:00
|
|
|
|
|
|
|
|
|
|
// 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; }
|
|
|
|
|
|
|
2026-03-09 20:36:20 -07:00
|
|
|
|
// Minimap pings from party members
|
|
|
|
|
|
struct MinimapPing {
|
|
|
|
|
|
uint64_t senderGuid = 0;
|
|
|
|
|
|
float wowX = 0.0f; // canonical WoW X (north)
|
|
|
|
|
|
float wowY = 0.0f; // canonical WoW Y (west)
|
|
|
|
|
|
float age = 0.0f; // seconds since received
|
|
|
|
|
|
static constexpr float LIFETIME = 5.0f;
|
|
|
|
|
|
bool isExpired() const { return age >= LIFETIME; }
|
|
|
|
|
|
};
|
|
|
|
|
|
const std::vector<MinimapPing>& getMinimapPings() const { return minimapPings_; }
|
|
|
|
|
|
void tickMinimapPings(float dt) {
|
|
|
|
|
|
for (auto& p : minimapPings_) p.age += dt;
|
|
|
|
|
|
minimapPings_.erase(
|
|
|
|
|
|
std::remove_if(minimapPings_.begin(), minimapPings_.end(),
|
|
|
|
|
|
[](const MinimapPing& p){ return p.isExpired(); }),
|
|
|
|
|
|
minimapPings_.end());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
bool isCasting() const { return casting; }
|
2026-02-19 03:31:49 -08:00
|
|
|
|
bool isGameObjectInteractionCasting() const {
|
|
|
|
|
|
return casting && currentCastSpellId == 0 && pendingGameObjectInteractGuid_ != 0;
|
|
|
|
|
|
}
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
uint32_t getCurrentCastSpellId() const { return currentCastSpellId; }
|
|
|
|
|
|
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
|
|
|
|
|
float getCastTimeRemaining() const { return castTimeRemaining; }
|
|
|
|
|
|
|
2026-03-09 23:13:30 -07:00
|
|
|
|
// Unit cast state (tracked per GUID for target frame + boss frames)
|
|
|
|
|
|
struct UnitCastState {
|
|
|
|
|
|
bool casting = false;
|
|
|
|
|
|
uint32_t spellId = 0;
|
|
|
|
|
|
float timeRemaining = 0.0f;
|
|
|
|
|
|
float timeTotal = 0.0f;
|
|
|
|
|
|
};
|
|
|
|
|
|
// Returns cast state for any unit by GUID (empty/non-casting if not found)
|
|
|
|
|
|
const UnitCastState* getUnitCastState(uint64_t guid) const {
|
|
|
|
|
|
auto it = unitCastStates_.find(guid);
|
|
|
|
|
|
return (it != unitCastStates_.end() && it->second.casting) ? &it->second : nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Convenience helpers for the current target
|
|
|
|
|
|
bool isTargetCasting() const { return getUnitCastState(targetGuid) != nullptr; }
|
|
|
|
|
|
uint32_t getTargetCastSpellId() const {
|
|
|
|
|
|
auto* s = getUnitCastState(targetGuid);
|
|
|
|
|
|
return s ? s->spellId : 0;
|
|
|
|
|
|
}
|
2026-03-09 23:06:40 -07:00
|
|
|
|
float getTargetCastProgress() const {
|
2026-03-09 23:13:30 -07:00
|
|
|
|
auto* s = getUnitCastState(targetGuid);
|
|
|
|
|
|
return (s && s->timeTotal > 0.0f)
|
|
|
|
|
|
? (s->timeTotal - s->timeRemaining) / s->timeTotal : 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
float getTargetCastTimeRemaining() const {
|
|
|
|
|
|
auto* s = getUnitCastState(targetGuid);
|
|
|
|
|
|
return s ? s->timeRemaining : 0.0f;
|
2026-03-09 23:06:40 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
|
// Talents
|
|
|
|
|
|
uint8_t getActiveTalentSpec() const { return activeTalentSpec_; }
|
|
|
|
|
|
uint8_t getUnspentTalentPoints() const { return unspentTalentPoints_[activeTalentSpec_]; }
|
|
|
|
|
|
uint8_t getUnspentTalentPoints(uint8_t spec) const { return spec < 2 ? unspentTalentPoints_[spec] : 0; }
|
|
|
|
|
|
const std::unordered_map<uint32_t, uint8_t>& getLearnedTalents() const { return learnedTalents_[activeTalentSpec_]; }
|
|
|
|
|
|
const std::unordered_map<uint32_t, uint8_t>& getLearnedTalents(uint8_t spec) const {
|
|
|
|
|
|
static std::unordered_map<uint32_t, uint8_t> empty;
|
|
|
|
|
|
return spec < 2 ? learnedTalents_[spec] : empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
uint8_t getTalentRank(uint32_t talentId) const {
|
|
|
|
|
|
auto it = learnedTalents_[activeTalentSpec_].find(talentId);
|
|
|
|
|
|
return (it != learnedTalents_[activeTalentSpec_].end()) ? it->second : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
void learnTalent(uint32_t talentId, uint32_t requestedRank);
|
|
|
|
|
|
void switchTalentSpec(uint8_t newSpec);
|
|
|
|
|
|
|
|
|
|
|
|
// Talent DBC access
|
|
|
|
|
|
const TalentEntry* getTalentEntry(uint32_t talentId) const {
|
|
|
|
|
|
auto it = talentCache_.find(talentId);
|
|
|
|
|
|
return (it != talentCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
const TalentTabEntry* getTalentTabEntry(uint32_t tabId) const {
|
|
|
|
|
|
auto it = talentTabCache_.find(tabId);
|
|
|
|
|
|
return (it != talentTabCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
const std::unordered_map<uint32_t, TalentEntry>& getAllTalents() const { return talentCache_; }
|
|
|
|
|
|
const std::unordered_map<uint32_t, TalentTabEntry>& getAllTalentTabs() const { return talentTabCache_; }
|
|
|
|
|
|
void loadTalentDbc();
|
|
|
|
|
|
|
2026-03-10 15:56:41 -07:00
|
|
|
|
// Action bar — 4 bars × 12 slots = 48 total
|
|
|
|
|
|
// Bar 0 (slots 0-11): main bottom bar (1-0, -, =)
|
|
|
|
|
|
// Bar 1 (slots 12-23): second bar above main (Shift+1 ... Shift+=)
|
|
|
|
|
|
// Bar 2 (slots 24-35): right side vertical bar
|
|
|
|
|
|
// Bar 3 (slots 36-47): left side vertical bar
|
2026-03-10 06:04:43 -07:00
|
|
|
|
static constexpr int SLOTS_PER_BAR = 12;
|
2026-03-10 15:56:41 -07:00
|
|
|
|
static constexpr int ACTION_BARS = 4;
|
|
|
|
|
|
static constexpr int ACTION_BAR_SLOTS = SLOTS_PER_BAR * ACTION_BARS; // 48
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
std::array<ActionBarSlot, ACTION_BAR_SLOTS>& getActionBar() { return actionBar; }
|
|
|
|
|
|
const std::array<ActionBarSlot, ACTION_BAR_SLOTS>& getActionBar() const { return actionBar; }
|
|
|
|
|
|
void setActionBarSlot(int slot, ActionBarSlot::Type type, uint32_t id);
|
|
|
|
|
|
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
|
void saveCharacterConfig();
|
|
|
|
|
|
void loadCharacterConfig();
|
|
|
|
|
|
static std::string getCharacterConfigDir();
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// Auras
|
|
|
|
|
|
const std::vector<AuraSlot>& getPlayerAuras() const { return playerAuras; }
|
|
|
|
|
|
const std::vector<AuraSlot>& getTargetAuras() const { return targetAuras; }
|
|
|
|
|
|
|
2026-03-09 15:32:11 -07:00
|
|
|
|
// Completed quests (populated from SMSG_QUERY_QUESTS_COMPLETED_RESPONSE)
|
|
|
|
|
|
bool isQuestCompleted(uint32_t questId) const { return completedQuests_.count(questId) > 0; }
|
|
|
|
|
|
const std::unordered_set<uint32_t>& getCompletedQuests() const { return completedQuests_; }
|
|
|
|
|
|
|
2026-02-07 00:00:06 -08:00
|
|
|
|
// NPC death callback (for animations)
|
|
|
|
|
|
using NpcDeathCallback = std::function<void(uint64_t guid)>;
|
|
|
|
|
|
void setNpcDeathCallback(NpcDeathCallback cb) { npcDeathCallback_ = std::move(cb); }
|
|
|
|
|
|
|
Add comprehensive NPC voice system with interaction and combat sounds
Implements full NPC voice interaction system supporting 6 different sound categories
for all playable races/genders. System loads ~450+ voice clips from MPQ archives.
Voice Categories:
- Greeting: Play on NPC right-click interaction
- Farewell: Play when closing gossip/dialog windows
- Vendor: Play when opening merchant/vendor windows
- Pissed: Play after clicking NPC 5+ times (spam protection)
- Aggro: Play when NPC enters combat with player
- Flee: Play when NPC is fleeing (ready for low-health triggers)
Features:
- Race/gender detection from NPC display IDs via CreatureDisplayInfoExtra.dbc
- Intelligent click tracking for pissed sounds
- Combat sounds use player character vocal files for humanoid NPCs
- Cooldown system prevents voice spam (2s default, combat sounds bypass)
- Generic fallback voices for unsupported NPC types
- 3D positional audio support
Voice Support:
- All playable races: Human, Dwarf, Gnome, Night Elf, Orc, Tauren, Troll, Undead
- Male and female variants for each race
- StandardNPC sounds for social interactions
- Character vocal sounds for combat
Technical Changes:
- Refactored NpcVoiceManager to support multiple sound categories
- Added callbacks: NpcFarewell, NpcVendor, NpcAggro
- Extended voice loading to parse both StandardNPC and Character vocal paths
- Integrated with GameHandler for gossip, vendor, and combat events
- Added detailed voice detection logging for debugging
Also includes:
- Sound manifest files added to docs/ for reference
- Blacksmith hammer pitch increased to 1.6x (was 1.4x)
- Blacksmith volume reduced 30% to 0.25 (was 0.35)
2026-02-09 16:03:51 -08:00
|
|
|
|
using NpcAggroCallback = std::function<void(uint64_t guid, const glm::vec3& position)>;
|
|
|
|
|
|
void setNpcAggroCallback(NpcAggroCallback cb) { npcAggroCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 00:00:06 -08:00
|
|
|
|
// NPC respawn callback (health 0 → >0, resets animation to idle)
|
|
|
|
|
|
using NpcRespawnCallback = std::function<void(uint64_t guid)>;
|
|
|
|
|
|
void setNpcRespawnCallback(NpcRespawnCallback cb) { npcRespawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-10 09:46:46 -07:00
|
|
|
|
// Stand state animation callback — fired when SMSG_STANDSTATE_UPDATE confirms a new state
|
|
|
|
|
|
// standState: 0=stand, 1-6=sit variants, 7=dead, 8=kneel
|
|
|
|
|
|
using StandStateCallback = std::function<void(uint8_t standState)>;
|
|
|
|
|
|
void setStandStateCallback(StandStateCallback cb) { standStateCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-10 09:57:24 -07:00
|
|
|
|
// Ghost state callback — fired when player enters or leaves ghost (spirit) form
|
|
|
|
|
|
using GhostStateCallback = std::function<void(bool isGhost)>;
|
|
|
|
|
|
void setGhostStateCallback(GhostStateCallback cb) { ghostStateCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-05 14:01:26 -08:00
|
|
|
|
// Melee swing callback (for driving animation/SFX)
|
|
|
|
|
|
using MeleeSwingCallback = std::function<void()>;
|
|
|
|
|
|
void setMeleeSwingCallback(MeleeSwingCallback cb) { meleeSwingCallback_ = std::move(cb); }
|
2026-02-05 12:01:03 -08:00
|
|
|
|
|
2026-03-10 09:42:17 -07:00
|
|
|
|
// Spell cast animation callbacks — true=start cast/channel, false=finish/cancel
|
|
|
|
|
|
// guid: caster (may be player or another unit), isChannel: channel vs regular cast
|
|
|
|
|
|
using SpellCastAnimCallback = std::function<void(uint64_t guid, bool start, bool isChannel)>;
|
|
|
|
|
|
void setSpellCastAnimCallback(SpellCastAnimCallback cb) { spellCastAnimCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-10 10:55:23 -07:00
|
|
|
|
// Unit animation hint: signal jump (animId=38) for other players/NPCs
|
2026-03-10 10:30:50 -07:00
|
|
|
|
using UnitAnimHintCallback = std::function<void(uint64_t guid, uint32_t animId)>;
|
|
|
|
|
|
void setUnitAnimHintCallback(UnitAnimHintCallback cb) { unitAnimHintCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-10 10:55:23 -07:00
|
|
|
|
// Unit move-flags callback: fired on every MSG_MOVE_* for other players with the raw flags field.
|
|
|
|
|
|
// Drives Walk(4) vs Run(5) selection and swim state initialization from heartbeat packets.
|
|
|
|
|
|
using UnitMoveFlagsCallback = std::function<void(uint64_t guid, uint32_t moveFlags)>;
|
|
|
|
|
|
void setUnitMoveFlagsCallback(UnitMoveFlagsCallback cb) { unitMoveFlagsCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 00:00:06 -08:00
|
|
|
|
// NPC swing callback (plays attack animation on NPC)
|
|
|
|
|
|
using NpcSwingCallback = std::function<void(uint64_t guid)>;
|
|
|
|
|
|
void setNpcSwingCallback(NpcSwingCallback cb) { npcSwingCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-09 01:29:44 -08:00
|
|
|
|
// NPC greeting callback (plays voice line when NPC is clicked)
|
|
|
|
|
|
using NpcGreetingCallback = std::function<void(uint64_t guid, const glm::vec3& position)>;
|
|
|
|
|
|
void setNpcGreetingCallback(NpcGreetingCallback cb) { npcGreetingCallback_ = std::move(cb); }
|
|
|
|
|
|
|
Add comprehensive NPC voice system with interaction and combat sounds
Implements full NPC voice interaction system supporting 6 different sound categories
for all playable races/genders. System loads ~450+ voice clips from MPQ archives.
Voice Categories:
- Greeting: Play on NPC right-click interaction
- Farewell: Play when closing gossip/dialog windows
- Vendor: Play when opening merchant/vendor windows
- Pissed: Play after clicking NPC 5+ times (spam protection)
- Aggro: Play when NPC enters combat with player
- Flee: Play when NPC is fleeing (ready for low-health triggers)
Features:
- Race/gender detection from NPC display IDs via CreatureDisplayInfoExtra.dbc
- Intelligent click tracking for pissed sounds
- Combat sounds use player character vocal files for humanoid NPCs
- Cooldown system prevents voice spam (2s default, combat sounds bypass)
- Generic fallback voices for unsupported NPC types
- 3D positional audio support
Voice Support:
- All playable races: Human, Dwarf, Gnome, Night Elf, Orc, Tauren, Troll, Undead
- Male and female variants for each race
- StandardNPC sounds for social interactions
- Character vocal sounds for combat
Technical Changes:
- Refactored NpcVoiceManager to support multiple sound categories
- Added callbacks: NpcFarewell, NpcVendor, NpcAggro
- Extended voice loading to parse both StandardNPC and Character vocal paths
- Integrated with GameHandler for gossip, vendor, and combat events
- Added detailed voice detection logging for debugging
Also includes:
- Sound manifest files added to docs/ for reference
- Blacksmith hammer pitch increased to 1.6x (was 1.4x)
- Blacksmith volume reduced 30% to 0.25 (was 0.35)
2026-02-09 16:03:51 -08:00
|
|
|
|
using NpcFarewellCallback = std::function<void(uint64_t guid, const glm::vec3& position)>;
|
|
|
|
|
|
void setNpcFarewellCallback(NpcFarewellCallback cb) { npcFarewellCallback_ = std::move(cb); }
|
|
|
|
|
|
|
|
|
|
|
|
using NpcVendorCallback = std::function<void(uint64_t guid, const glm::vec3& position)>;
|
|
|
|
|
|
void setNpcVendorCallback(NpcVendorCallback cb) { npcVendorCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 00:00:06 -08:00
|
|
|
|
// XP tracking
|
2026-02-05 12:07:58 -08:00
|
|
|
|
uint32_t getPlayerXp() const { return playerXp_; }
|
|
|
|
|
|
uint32_t getPlayerNextLevelXp() const { return playerNextLevelXp_; }
|
2026-03-10 07:35:30 -07:00
|
|
|
|
uint32_t getPlayerRestedXp() const { return playerRestedXp_; }
|
|
|
|
|
|
bool isPlayerResting() const { return isResting_; }
|
2026-02-06 23:52:16 -08:00
|
|
|
|
uint32_t getPlayerLevel() const { return serverPlayerLevel_; }
|
2026-02-11 18:25:04 -08:00
|
|
|
|
const std::vector<uint32_t>& getPlayerExploredZoneMasks() const { return playerExploredZones_; }
|
|
|
|
|
|
bool hasPlayerExploredZoneMasks() const { return hasPlayerExploredZones_; }
|
2026-02-06 16:53:09 -08:00
|
|
|
|
static uint32_t killXp(uint32_t playerLevel, uint32_t victimLevel);
|
2026-02-05 12:07:58 -08:00
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
|
// Server time (for deterministic moon phases, etc.)
|
|
|
|
|
|
float getGameTime() const { return gameTime_; }
|
|
|
|
|
|
float getTimeSpeed() const { return timeSpeed_; }
|
|
|
|
|
|
|
2026-02-17 17:59:41 -08:00
|
|
|
|
// Weather state (updated by SMSG_WEATHER)
|
|
|
|
|
|
// weatherType: 0=clear, 1=rain, 2=snow, 3=storm/fog
|
|
|
|
|
|
uint32_t getWeatherType() const { return weatherType_; }
|
|
|
|
|
|
float getWeatherIntensity() const { return weatherIntensity_; }
|
|
|
|
|
|
bool isRaining() const { return weatherType_ == 1 && weatherIntensity_ > 0.05f; }
|
|
|
|
|
|
bool isSnowing() const { return weatherType_ == 2 && weatherIntensity_ > 0.05f; }
|
2026-03-09 14:57:46 -07:00
|
|
|
|
uint32_t getOverrideLightId() const { return overrideLightId_; }
|
|
|
|
|
|
uint32_t getOverrideLightTransMs() const { return overrideLightTransMs_; }
|
2026-02-17 17:59:41 -08:00
|
|
|
|
|
2026-02-07 14:21:50 -08:00
|
|
|
|
// Player skills
|
|
|
|
|
|
const std::map<uint32_t, PlayerSkill>& getPlayerSkills() const { return playerSkills_; }
|
|
|
|
|
|
const std::string& getSkillName(uint32_t skillId) const;
|
|
|
|
|
|
uint32_t getSkillCategory(uint32_t skillId) const;
|
|
|
|
|
|
|
2026-02-05 21:28:21 -08:00
|
|
|
|
// World entry callback (online mode - triggered when entering world)
|
2026-03-10 08:35:36 -07:00
|
|
|
|
// Parameters: mapId, x, y, z (canonical WoW coords), isInitialEntry=true on first login or reconnect
|
|
|
|
|
|
using WorldEntryCallback = std::function<void(uint32_t mapId, float x, float y, float z, bool isInitialEntry)>;
|
2026-02-05 21:28:21 -08:00
|
|
|
|
void setWorldEntryCallback(WorldEntryCallback cb) { worldEntryCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-10 12:28:11 -07:00
|
|
|
|
// Knockback callback: called when server sends SMSG_MOVE_KNOCK_BACK for the player.
|
|
|
|
|
|
// Parameters: vcos, vsin (render-space direction), hspeed, vspeed (raw from packet).
|
|
|
|
|
|
using KnockBackCallback = std::function<void(float vcos, float vsin, float hspeed, float vspeed)>;
|
|
|
|
|
|
void setKnockBackCallback(KnockBackCallback cb) { knockBackCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
|
// Unstuck callback (resets player Z to floor height)
|
|
|
|
|
|
using UnstuckCallback = std::function<void()>;
|
|
|
|
|
|
void setUnstuckCallback(UnstuckCallback cb) { unstuckCallback_ = std::move(cb); }
|
|
|
|
|
|
void unstuck();
|
2026-02-08 03:24:12 -08:00
|
|
|
|
void setUnstuckGyCallback(UnstuckCallback cb) { unstuckGyCallback_ = std::move(cb); }
|
|
|
|
|
|
void unstuckGy();
|
2026-03-07 22:03:28 -08:00
|
|
|
|
void setUnstuckHearthCallback(UnstuckCallback cb) { unstuckHearthCallback_ = std::move(cb); }
|
|
|
|
|
|
void unstuckHearth();
|
2026-02-08 03:32:00 -08:00
|
|
|
|
using BindPointCallback = std::function<void(uint32_t mapId, float x, float y, float z)>;
|
|
|
|
|
|
void setBindPointCallback(BindPointCallback cb) { bindPointCallback_ = std::move(cb); }
|
2026-02-07 16:59:20 -08:00
|
|
|
|
|
2026-03-09 21:57:42 -07:00
|
|
|
|
// Called when the player starts casting Hearthstone so terrain at the bind
|
|
|
|
|
|
// point can be pre-loaded during the cast time.
|
|
|
|
|
|
// Parameters: mapId and canonical (x, y, z) of the bind location.
|
|
|
|
|
|
using HearthstonePreloadCallback = std::function<void(uint32_t mapId, float x, float y, float z)>;
|
|
|
|
|
|
void setHearthstonePreloadCallback(HearthstonePreloadCallback cb) { hearthstonePreloadCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-05 21:55:52 -08:00
|
|
|
|
// Creature spawn callback (online mode - triggered when creature enters view)
|
feat: propagate OBJECT_FIELD_SCALE_X through creature and GO spawn pipeline
Reads OBJECT_FIELD_SCALE_X (field 4, cross-expansion) from CREATE_OBJECT
update fields and passes it through the full creature and game object spawn
chain: game_handler callbacks → pending spawn structs → async load results
→ createInstance() calls. This gives boss giants, gnomes, children, and
other non-unit-scale NPCs correct visual size, and ensures scaled GOs
(e.g. large treasure chests, oversized plants) render at the server-specified
scale rather than always at 1.0.
- Added OBJECT_FIELD_SCALE_X to UF enum and all expansion update_fields.json
- Added float scale to CreatureSpawnCallback and GameObjectSpawnCallback
- Propagated scale through PendingCreatureSpawn, PreparedCreatureModel,
PendingGameObjectSpawn, PreparedGameObjectWMO
- Used scale in charRenderer/m2Renderer/wmoRenderer createInstance() calls
- Sanity-clamped raw float to [0.01, 100.0] range before use
2026-03-10 22:45:47 -07:00
|
|
|
|
// Parameters: guid, displayId, x, y, z (canonical), orientation, scale (OBJECT_FIELD_SCALE_X)
|
|
|
|
|
|
using CreatureSpawnCallback = std::function<void(uint64_t guid, uint32_t displayId, float x, float y, float z, float orientation, float scale)>;
|
2026-02-05 21:55:52 -08:00
|
|
|
|
void setCreatureSpawnCallback(CreatureSpawnCallback cb) { creatureSpawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
|
|
|
|
|
// Creature despawn callback (online mode - triggered when creature leaves view)
|
|
|
|
|
|
using CreatureDespawnCallback = std::function<void(uint64_t guid)>;
|
|
|
|
|
|
void setCreatureDespawnCallback(CreatureDespawnCallback cb) { creatureDespawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-13 19:40:54 -08:00
|
|
|
|
// Player spawn callback (online mode - triggered when a player enters view).
|
|
|
|
|
|
// Players need appearance data so the renderer can build the right body/hair textures.
|
|
|
|
|
|
using PlayerSpawnCallback = std::function<void(uint64_t guid,
|
|
|
|
|
|
uint32_t displayId,
|
|
|
|
|
|
uint8_t raceId,
|
|
|
|
|
|
uint8_t genderId,
|
|
|
|
|
|
uint32_t appearanceBytes,
|
|
|
|
|
|
uint8_t facialFeatures,
|
|
|
|
|
|
float x, float y, float z, float orientation)>;
|
|
|
|
|
|
void setPlayerSpawnCallback(PlayerSpawnCallback cb) { playerSpawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
|
|
|
|
|
using PlayerDespawnCallback = std::function<void(uint64_t guid)>;
|
|
|
|
|
|
void setPlayerDespawnCallback(PlayerDespawnCallback cb) { playerDespawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-13 20:10:19 -08:00
|
|
|
|
// Online player equipment visuals callback.
|
|
|
|
|
|
// Sends a best-effort view of equipped items for players in view using ItemDisplayInfo IDs.
|
|
|
|
|
|
// Arrays are indexed by EquipSlot (0..18). Values are 0 when unknown/unavailable.
|
|
|
|
|
|
using PlayerEquipmentCallback = std::function<void(uint64_t guid,
|
|
|
|
|
|
const std::array<uint32_t, 19>& displayInfoIds,
|
|
|
|
|
|
const std::array<uint8_t, 19>& inventoryTypes)>;
|
|
|
|
|
|
void setPlayerEquipmentCallback(PlayerEquipmentCallback cb) { playerEquipmentCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 19:44:03 -08:00
|
|
|
|
// GameObject spawn callback (online mode - triggered when gameobject enters view)
|
feat: propagate OBJECT_FIELD_SCALE_X through creature and GO spawn pipeline
Reads OBJECT_FIELD_SCALE_X (field 4, cross-expansion) from CREATE_OBJECT
update fields and passes it through the full creature and game object spawn
chain: game_handler callbacks → pending spawn structs → async load results
→ createInstance() calls. This gives boss giants, gnomes, children, and
other non-unit-scale NPCs correct visual size, and ensures scaled GOs
(e.g. large treasure chests, oversized plants) render at the server-specified
scale rather than always at 1.0.
- Added OBJECT_FIELD_SCALE_X to UF enum and all expansion update_fields.json
- Added float scale to CreatureSpawnCallback and GameObjectSpawnCallback
- Propagated scale through PendingCreatureSpawn, PreparedCreatureModel,
PendingGameObjectSpawn, PreparedGameObjectWMO
- Used scale in charRenderer/m2Renderer/wmoRenderer createInstance() calls
- Sanity-clamped raw float to [0.01, 100.0] range before use
2026-03-10 22:45:47 -07:00
|
|
|
|
// Parameters: guid, entry, displayId, x, y, z (canonical), orientation, scale (OBJECT_FIELD_SCALE_X)
|
|
|
|
|
|
using GameObjectSpawnCallback = std::function<void(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation, float scale)>;
|
2026-02-07 19:44:03 -08:00
|
|
|
|
void setGameObjectSpawnCallback(GameObjectSpawnCallback cb) { gameObjectSpawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-12 00:04:53 -08:00
|
|
|
|
// GameObject move callback (online mode - triggered when gameobject position updates)
|
|
|
|
|
|
// Parameters: guid, x, y, z (canonical), orientation
|
|
|
|
|
|
using GameObjectMoveCallback = std::function<void(uint64_t guid, float x, float y, float z, float orientation)>;
|
|
|
|
|
|
void setGameObjectMoveCallback(GameObjectMoveCallback cb) { gameObjectMoveCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 19:44:03 -08:00
|
|
|
|
// GameObject despawn callback (online mode - triggered when gameobject leaves view)
|
|
|
|
|
|
using GameObjectDespawnCallback = std::function<void(uint64_t guid)>;
|
|
|
|
|
|
void setGameObjectDespawnCallback(GameObjectDespawnCallback cb) { gameObjectDespawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-23 05:39:02 -08:00
|
|
|
|
using GameObjectCustomAnimCallback = std::function<void(uint64_t guid, uint32_t animId)>;
|
|
|
|
|
|
void setGameObjectCustomAnimCallback(GameObjectCustomAnimCallback cb) { gameObjectCustomAnimCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-06 14:24:38 -08:00
|
|
|
|
// Faction hostility map (populated from FactionTemplate.dbc by Application)
|
|
|
|
|
|
void setFactionHostileMap(std::unordered_map<uint32_t, bool> map) { factionHostileMap_ = std::move(map); }
|
|
|
|
|
|
|
2026-02-06 13:47:03 -08:00
|
|
|
|
// Creature move callback (online mode - triggered by SMSG_MONSTER_MOVE)
|
|
|
|
|
|
// Parameters: guid, x, y, z (canonical), duration_ms (0 = instant)
|
|
|
|
|
|
using CreatureMoveCallback = std::function<void(uint64_t guid, float x, float y, float z, uint32_t durationMs)>;
|
|
|
|
|
|
void setCreatureMoveCallback(CreatureMoveCallback cb) { creatureMoveCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-08 00:59:40 -08:00
|
|
|
|
// Transport move callback (online mode - triggered when transport position updates)
|
|
|
|
|
|
// Parameters: guid, x, y, z (canonical), orientation
|
|
|
|
|
|
using TransportMoveCallback = std::function<void(uint64_t guid, float x, float y, float z, float orientation)>;
|
|
|
|
|
|
void setTransportMoveCallback(TransportMoveCallback cb) { transportMoveCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-11 00:54:38 -08:00
|
|
|
|
// Transport spawn callback (online mode - triggered when transport GameObject is first detected)
|
|
|
|
|
|
// Parameters: guid, entry, displayId, x, y, z (canonical), orientation
|
|
|
|
|
|
using TransportSpawnCallback = std::function<void(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation)>;
|
|
|
|
|
|
void setTransportSpawnCallback(TransportSpawnCallback cb) { transportSpawnCallback_ = std::move(cb); }
|
|
|
|
|
|
|
|
|
|
|
|
// Notify that a transport has been spawned (called after WMO instance creation)
|
|
|
|
|
|
void notifyTransportSpawned(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation) {
|
|
|
|
|
|
if (transportSpawnCallback_) {
|
|
|
|
|
|
transportSpawnCallback_(guid, entry, displayId, x, y, z, orientation);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-08 00:59:40 -08:00
|
|
|
|
// Transport state for player-on-transport
|
|
|
|
|
|
bool isOnTransport() const { return playerTransportGuid_ != 0; }
|
|
|
|
|
|
uint64_t getPlayerTransportGuid() const { return playerTransportGuid_; }
|
|
|
|
|
|
glm::vec3 getPlayerTransportOffset() const { return playerTransportOffset_; }
|
2026-02-11 00:54:38 -08:00
|
|
|
|
|
|
|
|
|
|
// Check if a GUID is a known transport
|
|
|
|
|
|
bool isTransportGuid(uint64_t guid) const { return transportGuids_.count(guid) > 0; }
|
2026-02-12 00:04:53 -08:00
|
|
|
|
bool hasServerTransportUpdate(uint64_t guid) const { return serverUpdatedTransportGuids_.count(guid) > 0; }
|
2026-02-10 21:29:10 -08:00
|
|
|
|
glm::vec3 getComposedWorldPosition(); // Compose transport transform * local offset
|
|
|
|
|
|
TransportManager* getTransportManager() { return transportManager_.get(); }
|
|
|
|
|
|
void setPlayerOnTransport(uint64_t transportGuid, const glm::vec3& localOffset) {
|
2026-03-11 16:06:36 -07:00
|
|
|
|
// Validate transport is registered before attaching player
|
|
|
|
|
|
// (defer if transport not yet registered to prevent desyncs)
|
|
|
|
|
|
if (transportGuid != 0 && !isTransportGuid(transportGuid)) {
|
|
|
|
|
|
return; // Transport not yet registered; skip attachment
|
|
|
|
|
|
}
|
2026-02-10 21:29:10 -08:00
|
|
|
|
playerTransportGuid_ = transportGuid;
|
|
|
|
|
|
playerTransportOffset_ = localOffset;
|
2026-02-12 00:45:24 -08:00
|
|
|
|
playerTransportStickyGuid_ = transportGuid;
|
|
|
|
|
|
playerTransportStickyTimer_ = 8.0f;
|
|
|
|
|
|
movementInfo.transportGuid = transportGuid;
|
2026-02-10 21:29:10 -08:00
|
|
|
|
}
|
2026-03-06 23:01:11 -08:00
|
|
|
|
void setPlayerTransportOffset(const glm::vec3& offset) {
|
|
|
|
|
|
playerTransportOffset_ = offset;
|
|
|
|
|
|
}
|
2026-02-10 21:29:10 -08:00
|
|
|
|
void clearPlayerTransport() {
|
2026-02-12 00:45:24 -08:00
|
|
|
|
if (playerTransportGuid_ != 0) {
|
|
|
|
|
|
playerTransportStickyGuid_ = playerTransportGuid_;
|
|
|
|
|
|
playerTransportStickyTimer_ = std::max(playerTransportStickyTimer_, 1.5f);
|
|
|
|
|
|
}
|
2026-02-10 21:29:10 -08:00
|
|
|
|
playerTransportGuid_ = 0;
|
|
|
|
|
|
playerTransportOffset_ = glm::vec3(0.0f);
|
2026-02-12 00:45:24 -08:00
|
|
|
|
movementInfo.transportGuid = 0;
|
2026-02-10 21:29:10 -08:00
|
|
|
|
}
|
2026-02-08 00:59:40 -08:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// Cooldowns
|
|
|
|
|
|
float getSpellCooldown(uint32_t spellId) const;
|
|
|
|
|
|
|
|
|
|
|
|
// Player GUID
|
|
|
|
|
|
uint64_t getPlayerGuid() const { return playerGuid; }
|
2026-03-10 15:18:00 -07:00
|
|
|
|
|
|
|
|
|
|
// Look up a display name for any guid: checks playerNameCache then entity manager.
|
|
|
|
|
|
// Returns empty string if unknown. Used by chat display to resolve names at render time.
|
|
|
|
|
|
const std::string& lookupName(uint64_t guid) const {
|
|
|
|
|
|
static const std::string kEmpty;
|
|
|
|
|
|
auto it = playerNameCache.find(guid);
|
|
|
|
|
|
if (it != playerNameCache.end()) return it->second;
|
|
|
|
|
|
auto entity = entityManager.getEntity(guid);
|
|
|
|
|
|
if (entity) {
|
|
|
|
|
|
if (auto* unit = dynamic_cast<const Unit*>(entity.get())) {
|
|
|
|
|
|
if (!unit->getName().empty()) return unit->getName();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return kEmpty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
|
uint8_t getPlayerClass() const {
|
|
|
|
|
|
const Character* ch = getActiveCharacter();
|
|
|
|
|
|
return ch ? static_cast<uint8_t>(ch->characterClass) : 0;
|
|
|
|
|
|
}
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void setPlayerGuid(uint64_t guid) { playerGuid = guid; }
|
|
|
|
|
|
|
2026-02-06 17:27:20 -08:00
|
|
|
|
// Player death state
|
|
|
|
|
|
bool isPlayerDead() const { return playerDead_; }
|
2026-02-07 23:12:24 -08:00
|
|
|
|
bool isPlayerGhost() const { return releasedSpirit_; }
|
|
|
|
|
|
bool showDeathDialog() const { return playerDead_ && !releasedSpirit_; }
|
|
|
|
|
|
bool showResurrectDialog() const { return resurrectRequestPending_; }
|
2026-03-09 22:27:24 -07:00
|
|
|
|
const std::string& getResurrectCasterName() const { return resurrectCasterName_; }
|
2026-03-10 12:53:05 -07:00
|
|
|
|
bool showTalentWipeConfirmDialog() const { return talentWipePending_; }
|
|
|
|
|
|
uint32_t getTalentWipeCost() const { return talentWipeCost_; }
|
|
|
|
|
|
void confirmTalentWipe();
|
|
|
|
|
|
void cancelTalentWipe() { talentWipePending_ = false; }
|
2026-03-09 22:31:56 -07:00
|
|
|
|
/** True when ghost is within 40 yards of corpse position (same map). */
|
|
|
|
|
|
bool canReclaimCorpse() const;
|
2026-03-11 22:41:26 -07:00
|
|
|
|
/** Distance (yards) from ghost to corpse, or -1 if no corpse data. */
|
|
|
|
|
|
float getCorpseDistance() const {
|
|
|
|
|
|
if (corpseMapId_ == 0 || currentMapId_ != corpseMapId_) return -1.0f;
|
|
|
|
|
|
float dx = movementInfo.x - corpseX_;
|
|
|
|
|
|
float dy = movementInfo.y - corpseY_;
|
|
|
|
|
|
float dz = movementInfo.z - corpseZ_;
|
|
|
|
|
|
return std::sqrt(dx*dx + dy*dy + dz*dz);
|
|
|
|
|
|
}
|
2026-03-11 23:19:48 -07:00
|
|
|
|
/** Corpse position in canonical WoW coords (X=north, Y=west).
|
|
|
|
|
|
* Returns false if no corpse data or on a different map. */
|
|
|
|
|
|
bool getCorpseCanonicalPos(float& outX, float& outY) const {
|
|
|
|
|
|
if (corpseMapId_ == 0 || currentMapId_ != corpseMapId_) return false;
|
|
|
|
|
|
outX = corpseY_; // server Y = canonical X (north)
|
|
|
|
|
|
outY = corpseX_; // server X = canonical Y (west)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-03-09 22:31:56 -07:00
|
|
|
|
/** Send CMSG_RECLAIM_CORPSE; noop if not a ghost or not near corpse. */
|
|
|
|
|
|
void reclaimCorpse();
|
2026-02-06 17:27:20 -08:00
|
|
|
|
void releaseSpirit();
|
2026-02-07 23:12:24 -08:00
|
|
|
|
void acceptResurrect();
|
|
|
|
|
|
void declineResurrect();
|
2026-02-06 17:27:20 -08:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 4: Group ----
|
|
|
|
|
|
void inviteToGroup(const std::string& playerName);
|
|
|
|
|
|
void acceptGroupInvite();
|
|
|
|
|
|
void declineGroupInvite();
|
|
|
|
|
|
void leaveGroup();
|
|
|
|
|
|
bool isInGroup() const { return !partyData.isEmpty(); }
|
|
|
|
|
|
const GroupListData& getPartyData() const { return partyData; }
|
2026-03-10 05:46:03 -07:00
|
|
|
|
const std::vector<ContactEntry>& getContacts() const { return contacts_; }
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
bool hasPendingGroupInvite() const { return pendingGroupInvite; }
|
|
|
|
|
|
const std::string& getPendingInviterName() const { return pendingInviterName; }
|
|
|
|
|
|
|
2026-03-09 14:15:59 -07:00
|
|
|
|
// ---- Item text (books / readable items) ----
|
|
|
|
|
|
bool isItemTextOpen() const { return itemTextOpen_; }
|
|
|
|
|
|
const std::string& getItemText() const { return itemText_; }
|
|
|
|
|
|
void closeItemText() { itemTextOpen_ = false; }
|
|
|
|
|
|
void queryItemText(uint64_t itemGuid);
|
|
|
|
|
|
|
2026-03-09 14:14:15 -07:00
|
|
|
|
// ---- Shared Quest ----
|
|
|
|
|
|
bool hasPendingSharedQuest() const { return pendingSharedQuest_; }
|
|
|
|
|
|
uint32_t getSharedQuestId() const { return sharedQuestId_; }
|
|
|
|
|
|
const std::string& getSharedQuestTitle() const { return sharedQuestTitle_; }
|
|
|
|
|
|
const std::string& getSharedQuestSharerName() const { return sharedQuestSharerName_; }
|
|
|
|
|
|
void acceptSharedQuest();
|
|
|
|
|
|
void declineSharedQuest();
|
|
|
|
|
|
|
2026-03-09 14:07:50 -07:00
|
|
|
|
// ---- Summon ----
|
|
|
|
|
|
bool hasPendingSummonRequest() const { return pendingSummonRequest_; }
|
|
|
|
|
|
const std::string& getSummonerName() const { return summonerName_; }
|
|
|
|
|
|
float getSummonTimeoutSec() const { return summonTimeoutSec_; }
|
|
|
|
|
|
void acceptSummon();
|
|
|
|
|
|
void declineSummon();
|
2026-03-09 14:08:49 -07:00
|
|
|
|
void tickSummonTimeout(float dt) {
|
|
|
|
|
|
if (!pendingSummonRequest_) return;
|
|
|
|
|
|
summonTimeoutSec_ -= dt;
|
|
|
|
|
|
if (summonTimeoutSec_ <= 0.0f) {
|
|
|
|
|
|
pendingSummonRequest_ = false;
|
|
|
|
|
|
summonTimeoutSec_ = 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-09 14:07:50 -07:00
|
|
|
|
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
// ---- Trade ----
|
|
|
|
|
|
enum class TradeStatus : uint8_t {
|
|
|
|
|
|
None = 0, PendingIncoming, Open, Accepted, Complete
|
|
|
|
|
|
};
|
2026-03-11 00:44:07 -07:00
|
|
|
|
|
|
|
|
|
|
static constexpr int TRADE_SLOT_COUNT = 6; // WoW has 6 normal trade slots + slot 6 for non-trade item
|
|
|
|
|
|
|
|
|
|
|
|
struct TradeSlot {
|
|
|
|
|
|
uint32_t itemId = 0;
|
|
|
|
|
|
uint32_t displayId = 0;
|
|
|
|
|
|
uint32_t stackCount = 0;
|
|
|
|
|
|
uint64_t itemGuid = 0;
|
|
|
|
|
|
uint8_t bag = 0xFF; // 0xFF = not set
|
|
|
|
|
|
uint8_t bagSlot = 0xFF;
|
|
|
|
|
|
bool occupied = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
TradeStatus getTradeStatus() const { return tradeStatus_; }
|
|
|
|
|
|
bool hasPendingTradeRequest() const { return tradeStatus_ == TradeStatus::PendingIncoming; }
|
2026-03-11 00:44:07 -07:00
|
|
|
|
bool isTradeOpen() const { return tradeStatus_ == TradeStatus::Open || tradeStatus_ == TradeStatus::Accepted; }
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
const std::string& getTradePeerName() const { return tradePeerName_; }
|
2026-03-11 00:44:07 -07:00
|
|
|
|
|
|
|
|
|
|
// My trade slots (what I'm offering)
|
|
|
|
|
|
const std::array<TradeSlot, TRADE_SLOT_COUNT>& getMyTradeSlots() const { return myTradeSlots_; }
|
|
|
|
|
|
// Peer's trade slots (what they're offering)
|
|
|
|
|
|
const std::array<TradeSlot, TRADE_SLOT_COUNT>& getPeerTradeSlots() const { return peerTradeSlots_; }
|
|
|
|
|
|
uint64_t getMyTradeGold() const { return myTradeGold_; }
|
|
|
|
|
|
uint64_t getPeerTradeGold() const { return peerTradeGold_; }
|
|
|
|
|
|
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
void acceptTradeRequest(); // respond to incoming SMSG_TRADE_STATUS(1) with CMSG_BEGIN_TRADE
|
|
|
|
|
|
void declineTradeRequest(); // respond with CMSG_CANCEL_TRADE
|
|
|
|
|
|
void acceptTrade(); // lock in offer: CMSG_ACCEPT_TRADE
|
|
|
|
|
|
void cancelTrade(); // CMSG_CANCEL_TRADE
|
2026-03-11 00:44:07 -07:00
|
|
|
|
void setTradeItem(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot);
|
|
|
|
|
|
void clearTradeItem(uint8_t tradeSlot);
|
|
|
|
|
|
void setTradeGold(uint64_t copper);
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
|
2026-03-09 13:58:02 -07:00
|
|
|
|
// ---- Duel ----
|
|
|
|
|
|
bool hasPendingDuelRequest() const { return pendingDuelRequest_; }
|
|
|
|
|
|
const std::string& getDuelChallengerName() const { return duelChallengerName_; }
|
|
|
|
|
|
void acceptDuel();
|
|
|
|
|
|
// forfeitDuel() already declared at line ~399
|
|
|
|
|
|
|
2026-03-09 13:36:23 -07:00
|
|
|
|
// ---- Instance lockouts ----
|
|
|
|
|
|
struct InstanceLockout {
|
|
|
|
|
|
uint32_t mapId = 0;
|
|
|
|
|
|
uint32_t difficulty = 0; // 0=normal,1=heroic/10man,2=25man,3=25man heroic
|
|
|
|
|
|
uint64_t resetTime = 0; // Unix timestamp of instance reset
|
|
|
|
|
|
bool locked = false;
|
|
|
|
|
|
bool extended = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
const std::vector<InstanceLockout>& getInstanceLockouts() const { return instanceLockouts_; }
|
2026-03-09 20:05:09 -07:00
|
|
|
|
|
|
|
|
|
|
// Boss encounter unit tracking (SMSG_UPDATE_INSTANCE_ENCOUNTER_UNIT)
|
|
|
|
|
|
static constexpr uint32_t kMaxEncounterSlots = 5;
|
2026-03-09 19:54:32 -07:00
|
|
|
|
// Returns boss unit guid for the given encounter slot (0 if none)
|
|
|
|
|
|
uint64_t getEncounterUnitGuid(uint32_t slot) const {
|
|
|
|
|
|
return (slot < kMaxEncounterSlots) ? encounterUnitGuids_[slot] : 0;
|
|
|
|
|
|
}
|
2026-03-09 13:36:23 -07:00
|
|
|
|
|
2026-03-10 06:10:29 -07:00
|
|
|
|
// Raid target markers (MSG_RAID_TARGET_UPDATE)
|
|
|
|
|
|
// Icon indices 0-7: Star, Circle, Diamond, Triangle, Moon, Square, Cross, Skull
|
|
|
|
|
|
static constexpr uint32_t kRaidMarkCount = 8;
|
|
|
|
|
|
// Returns the GUID marked with the given icon (0 = no mark)
|
|
|
|
|
|
uint64_t getRaidMarkGuid(uint32_t icon) const {
|
|
|
|
|
|
return (icon < kRaidMarkCount) ? raidTargetGuids_[icon] : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Returns the raid mark icon for a given guid (0xFF = no mark)
|
|
|
|
|
|
uint8_t getEntityRaidMark(uint64_t guid) const {
|
|
|
|
|
|
if (guid == 0) return 0xFF;
|
|
|
|
|
|
for (uint32_t i = 0; i < kRaidMarkCount; ++i)
|
|
|
|
|
|
if (raidTargetGuids_[i] == guid) return static_cast<uint8_t>(i);
|
|
|
|
|
|
return 0xFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 13:30:23 -07:00
|
|
|
|
// ---- LFG / Dungeon Finder ----
|
|
|
|
|
|
enum class LfgState : uint8_t {
|
|
|
|
|
|
None = 0,
|
|
|
|
|
|
RoleCheck = 1,
|
|
|
|
|
|
Queued = 2,
|
|
|
|
|
|
Proposal = 3,
|
|
|
|
|
|
Boot = 4,
|
|
|
|
|
|
InDungeon = 5,
|
|
|
|
|
|
FinishedDungeon= 6,
|
|
|
|
|
|
RaidBrowser = 7,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// roles bitmask: 0x02=tank, 0x04=healer, 0x08=dps; pass LFGDungeonEntry ID
|
|
|
|
|
|
void lfgJoin(uint32_t dungeonId, uint8_t roles);
|
|
|
|
|
|
void lfgLeave();
|
|
|
|
|
|
void lfgAcceptProposal(uint32_t proposalId, bool accept);
|
2026-03-10 12:08:58 -07:00
|
|
|
|
void lfgSetBootVote(bool vote);
|
2026-03-09 13:30:23 -07:00
|
|
|
|
void lfgTeleport(bool toLfgDungeon = true);
|
|
|
|
|
|
LfgState getLfgState() const { return lfgState_; }
|
|
|
|
|
|
bool isLfgQueued() const { return lfgState_ == LfgState::Queued; }
|
|
|
|
|
|
bool isLfgInDungeon() const { return lfgState_ == LfgState::InDungeon; }
|
2026-03-09 13:47:07 -07:00
|
|
|
|
uint32_t getLfgDungeonId() const { return lfgDungeonId_; }
|
|
|
|
|
|
uint32_t getLfgProposalId() const { return lfgProposalId_; }
|
|
|
|
|
|
int32_t getLfgAvgWaitSec() const { return lfgAvgWaitSec_; }
|
|
|
|
|
|
uint32_t getLfgTimeInQueueMs() const { return lfgTimeInQueueMs_; }
|
2026-03-09 13:30:23 -07:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 5: Loot ----
|
|
|
|
|
|
void lootTarget(uint64_t guid);
|
|
|
|
|
|
void lootItem(uint8_t slotIndex);
|
|
|
|
|
|
void closeLoot();
|
2026-02-06 18:34:45 -08:00
|
|
|
|
void activateSpiritHealer(uint64_t npcGuid);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
bool isLootWindowOpen() const { return lootWindowOpen; }
|
|
|
|
|
|
const LootResponseData& getCurrentLoot() const { return currentLoot; }
|
2026-02-17 16:31:00 -08:00
|
|
|
|
void setAutoLoot(bool enabled) { autoLoot_ = enabled; }
|
|
|
|
|
|
bool isAutoLoot() const { return autoLoot_; }
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-03-09 14:01:27 -07:00
|
|
|
|
// Group loot roll
|
|
|
|
|
|
struct LootRollEntry {
|
|
|
|
|
|
uint64_t objectGuid = 0;
|
|
|
|
|
|
uint32_t slot = 0;
|
|
|
|
|
|
uint32_t itemId = 0;
|
|
|
|
|
|
std::string itemName;
|
|
|
|
|
|
uint8_t itemQuality = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
bool hasPendingLootRoll() const { return pendingLootRollActive_; }
|
|
|
|
|
|
const LootRollEntry& getPendingLootRoll() const { return pendingLootRoll_; }
|
|
|
|
|
|
void sendLootRoll(uint64_t objectGuid, uint32_t slot, uint8_t rollType);
|
|
|
|
|
|
// rollType: 0=need, 1=greed, 2=disenchant, 96=pass
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// NPC Gossip
|
|
|
|
|
|
void interactWithNpc(uint64_t guid);
|
2026-02-07 19:44:03 -08:00
|
|
|
|
void interactWithGameObject(uint64_t guid);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void selectGossipOption(uint32_t optionId);
|
2026-02-06 11:45:35 -08:00
|
|
|
|
void selectGossipQuest(uint32_t questId);
|
2026-02-06 11:59:51 -08:00
|
|
|
|
void acceptQuest();
|
|
|
|
|
|
void declineQuest();
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void closeGossip();
|
|
|
|
|
|
bool isGossipWindowOpen() const { return gossipWindowOpen; }
|
|
|
|
|
|
const GossipMessageData& getCurrentGossip() const { return currentGossip; }
|
2026-03-11 17:27:23 -07:00
|
|
|
|
bool isQuestDetailsOpen() {
|
|
|
|
|
|
// Check if delayed opening timer has expired
|
|
|
|
|
|
if (questDetailsOpen) return true;
|
|
|
|
|
|
if (questDetailsOpenTime != std::chrono::steady_clock::time_point{}) {
|
|
|
|
|
|
if (std::chrono::steady_clock::now() >= questDetailsOpenTime) {
|
|
|
|
|
|
questDetailsOpen = true;
|
|
|
|
|
|
questDetailsOpenTime = std::chrono::steady_clock::time_point{};
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-02-06 11:59:51 -08:00
|
|
|
|
const QuestDetailsData& getQuestDetails() const { return currentQuestDetails; }
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-03-09 14:38:45 -07:00
|
|
|
|
// Gossip / quest map POI markers (SMSG_GOSSIP_POI)
|
|
|
|
|
|
struct GossipPoi {
|
|
|
|
|
|
float x = 0.0f; // WoW canonical X (north)
|
|
|
|
|
|
float y = 0.0f; // WoW canonical Y (west)
|
|
|
|
|
|
uint32_t icon = 0; // POI icon type
|
|
|
|
|
|
uint32_t data = 0;
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
};
|
|
|
|
|
|
const std::vector<GossipPoi>& getGossipPois() const { return gossipPois_; }
|
|
|
|
|
|
void clearGossipPois() { gossipPois_.clear(); }
|
|
|
|
|
|
|
2026-02-06 21:50:15 -08:00
|
|
|
|
// Quest turn-in
|
|
|
|
|
|
bool isQuestRequestItemsOpen() const { return questRequestItemsOpen_; }
|
|
|
|
|
|
const QuestRequestItemsData& getQuestRequestItems() const { return currentQuestRequestItems_; }
|
|
|
|
|
|
void completeQuest(); // Send CMSG_QUESTGIVER_COMPLETE_QUEST
|
|
|
|
|
|
void closeQuestRequestItems();
|
|
|
|
|
|
|
2026-03-11 19:11:02 -07:00
|
|
|
|
bool isQuestOfferRewardOpen() const { return questOfferRewardOpen_; }
|
2026-02-06 21:50:15 -08:00
|
|
|
|
const QuestOfferRewardData& getQuestOfferReward() const { return currentQuestOfferReward_; }
|
|
|
|
|
|
void chooseQuestReward(uint32_t rewardIndex); // Send CMSG_QUESTGIVER_CHOOSE_REWARD
|
|
|
|
|
|
void closeQuestOfferReward();
|
|
|
|
|
|
|
2026-02-06 13:47:03 -08:00
|
|
|
|
// Quest log
|
|
|
|
|
|
struct QuestLogEntry {
|
|
|
|
|
|
uint32_t questId = 0;
|
|
|
|
|
|
std::string title;
|
|
|
|
|
|
std::string objectives;
|
|
|
|
|
|
bool complete = false;
|
2026-03-10 23:52:18 -07:00
|
|
|
|
// Objective kill counts: npcOrGoEntry -> (current, required)
|
2026-02-10 01:24:37 -08:00
|
|
|
|
std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> killCounts;
|
2026-02-18 04:06:14 -08:00
|
|
|
|
// Quest item progress: itemId -> current count
|
|
|
|
|
|
std::unordered_map<uint32_t, uint32_t> itemCounts;
|
2026-02-19 00:56:24 -08:00
|
|
|
|
// Server-authoritative quest item requirements from REQUEST_ITEMS
|
|
|
|
|
|
std::unordered_map<uint32_t, uint32_t> requiredItemCounts;
|
2026-03-10 23:52:18 -07:00
|
|
|
|
// Structured kill objectives parsed from SMSG_QUEST_QUERY_RESPONSE.
|
|
|
|
|
|
// Index 0-3 map to the server's objective slot order (packed into update fields).
|
|
|
|
|
|
// npcOrGoId != 0 => entity objective (kill NPC or interact with GO).
|
|
|
|
|
|
struct KillObjective {
|
|
|
|
|
|
int32_t npcOrGoId = 0; // negative = game-object entry
|
|
|
|
|
|
uint32_t required = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
std::array<KillObjective, 4> killObjectives{}; // zeroed by default
|
|
|
|
|
|
// Required item objectives parsed from SMSG_QUEST_QUERY_RESPONSE.
|
|
|
|
|
|
// itemId != 0 => collect items of that type.
|
|
|
|
|
|
struct ItemObjective {
|
|
|
|
|
|
uint32_t itemId = 0;
|
|
|
|
|
|
uint32_t required = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
std::array<ItemObjective, 6> itemObjectives{}; // zeroed by default
|
2026-03-11 22:30:16 -07:00
|
|
|
|
// Reward data parsed from SMSG_QUEST_QUERY_RESPONSE
|
|
|
|
|
|
int32_t rewardMoney = 0; // copper; positive=reward, negative=cost
|
|
|
|
|
|
std::array<QuestRewardItem, 4> rewardItems{}; // guaranteed reward items
|
|
|
|
|
|
std::array<QuestRewardItem, 6> rewardChoiceItems{}; // player picks one of these
|
2026-02-06 13:47:03 -08:00
|
|
|
|
};
|
|
|
|
|
|
const std::vector<QuestLogEntry>& getQuestLog() const { return questLog_; }
|
|
|
|
|
|
void abandonQuest(uint32_t questId);
|
2026-02-19 00:30:21 -08:00
|
|
|
|
bool requestQuestQuery(uint32_t questId, bool force = false);
|
2026-03-10 05:18:45 -07:00
|
|
|
|
bool isQuestTracked(uint32_t questId) const { return trackedQuestIds_.count(questId) > 0; }
|
|
|
|
|
|
void setQuestTracked(uint32_t questId, bool tracked) {
|
|
|
|
|
|
if (tracked) trackedQuestIds_.insert(questId);
|
|
|
|
|
|
else trackedQuestIds_.erase(questId);
|
|
|
|
|
|
}
|
|
|
|
|
|
const std::unordered_set<uint32_t>& getTrackedQuestIds() const { return trackedQuestIds_; }
|
2026-02-19 00:56:24 -08:00
|
|
|
|
bool isQuestQueryPending(uint32_t questId) const {
|
|
|
|
|
|
return pendingQuestQueryIds_.count(questId) > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
void clearQuestQueryPending(uint32_t questId) { pendingQuestQueryIds_.erase(questId); }
|
2026-02-18 23:30:38 -08:00
|
|
|
|
const std::unordered_map<uint32_t, uint32_t>& getWorldStates() const { return worldStates_; }
|
|
|
|
|
|
std::optional<uint32_t> getWorldState(uint32_t key) const {
|
|
|
|
|
|
auto it = worldStates_.find(key);
|
|
|
|
|
|
if (it == worldStates_.end()) return std::nullopt;
|
|
|
|
|
|
return it->second;
|
|
|
|
|
|
}
|
|
|
|
|
|
uint32_t getWorldStateMapId() const { return worldStateMapId_; }
|
|
|
|
|
|
uint32_t getWorldStateZoneId() const { return worldStateZoneId_; }
|
|
|
|
|
|
|
2026-03-09 14:30:48 -07:00
|
|
|
|
// Mirror timers (0=fatigue, 1=breath, 2=feigndeath)
|
|
|
|
|
|
struct MirrorTimer {
|
|
|
|
|
|
int32_t value = 0;
|
|
|
|
|
|
int32_t maxValue = 0;
|
|
|
|
|
|
int32_t scale = 0; // +1 = counting up, -1 = counting down
|
|
|
|
|
|
bool paused = false;
|
|
|
|
|
|
bool active = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
const MirrorTimer& getMirrorTimer(int type) const {
|
|
|
|
|
|
static MirrorTimer empty;
|
|
|
|
|
|
return (type >= 0 && type < 3) ? mirrorTimers_[type] : empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Combo points
|
|
|
|
|
|
uint8_t getComboPoints() const { return comboPoints_; }
|
|
|
|
|
|
uint64_t getComboTarget() const { return comboTarget_; }
|
|
|
|
|
|
|
2026-03-09 18:28:03 -07:00
|
|
|
|
// Death Knight rune state (6 runes: 0-1=Blood, 2-3=Unholy, 4-5=Frost; may become Death=3)
|
|
|
|
|
|
enum class RuneType : uint8_t { Blood = 0, Unholy = 1, Frost = 2, Death = 3 };
|
|
|
|
|
|
struct RuneSlot {
|
|
|
|
|
|
RuneType type = RuneType::Blood;
|
|
|
|
|
|
bool ready = true; // Server-confirmed ready state
|
|
|
|
|
|
float readyFraction = 1.0f; // 0.0=depleted → 1.0=full (from server sync)
|
|
|
|
|
|
};
|
|
|
|
|
|
const std::array<RuneSlot, 6>& getPlayerRunes() const { return playerRunes_; }
|
|
|
|
|
|
|
2026-02-18 23:30:38 -08:00
|
|
|
|
struct FactionStandingInit {
|
|
|
|
|
|
uint8_t flags = 0;
|
|
|
|
|
|
int32_t standing = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
const std::vector<FactionStandingInit>& getInitialFactions() const { return initialFactions_; }
|
2026-03-09 14:48:30 -07:00
|
|
|
|
const std::unordered_map<uint32_t, int32_t>& getFactionStandings() const { return factionStandings_; }
|
2026-03-09 14:52:13 -07:00
|
|
|
|
const std::string& getFactionNamePublic(uint32_t factionId) const;
|
2026-02-18 23:30:38 -08:00
|
|
|
|
uint32_t getLastContactListMask() const { return lastContactListMask_; }
|
|
|
|
|
|
uint32_t getLastContactListCount() const { return lastContactListCount_; }
|
|
|
|
|
|
bool isServerMovementAllowed() const { return serverMovementAllowed_; }
|
2026-02-06 13:47:03 -08:00
|
|
|
|
|
2026-02-06 20:10:10 -08:00
|
|
|
|
// Quest giver status (! and ? markers)
|
|
|
|
|
|
QuestGiverStatus getQuestGiverStatus(uint64_t guid) const {
|
|
|
|
|
|
auto it = npcQuestStatus_.find(guid);
|
|
|
|
|
|
return (it != npcQuestStatus_.end()) ? it->second : QuestGiverStatus::NONE;
|
|
|
|
|
|
}
|
|
|
|
|
|
const std::unordered_map<uint64_t, QuestGiverStatus>& getNpcQuestStatuses() const { return npcQuestStatus_; }
|
|
|
|
|
|
|
2026-02-19 21:13:13 -08:00
|
|
|
|
// Charge callback — fires when player casts a charge spell toward target
|
|
|
|
|
|
// Parameters: targetGuid, targetX, targetY, targetZ (canonical WoW coordinates)
|
|
|
|
|
|
using ChargeCallback = std::function<void(uint64_t targetGuid, float x, float y, float z)>;
|
|
|
|
|
|
void setChargeCallback(ChargeCallback cb) { chargeCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-17 17:23:42 -08:00
|
|
|
|
// Level-up callback — fires when the player gains a level (newLevel > 1)
|
|
|
|
|
|
using LevelUpCallback = std::function<void(uint32_t newLevel)>;
|
|
|
|
|
|
void setLevelUpCallback(LevelUpCallback cb) { levelUpCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-19 20:36:25 -08:00
|
|
|
|
// Other player level-up callback — fires when another player gains a level
|
|
|
|
|
|
using OtherPlayerLevelUpCallback = std::function<void(uint64_t guid, uint32_t newLevel)>;
|
|
|
|
|
|
void setOtherPlayerLevelUpCallback(OtherPlayerLevelUpCallback cb) { otherPlayerLevelUpCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-09 13:53:42 -07:00
|
|
|
|
// Achievement earned callback — fires when SMSG_ACHIEVEMENT_EARNED is received
|
2026-03-10 20:53:21 -07:00
|
|
|
|
using AchievementEarnedCallback = std::function<void(uint32_t achievementId, const std::string& name)>;
|
2026-03-09 13:53:42 -07:00
|
|
|
|
void setAchievementEarnedCallback(AchievementEarnedCallback cb) { achievementEarnedCallback_ = std::move(cb); }
|
2026-03-10 20:53:21 -07:00
|
|
|
|
const std::unordered_set<uint32_t>& getEarnedAchievements() const { return earnedAchievements_; }
|
2026-03-11 22:07:44 -07:00
|
|
|
|
/// Returns the name of an achievement by ID, or empty string if unknown.
|
|
|
|
|
|
const std::string& getAchievementName(uint32_t id) const {
|
|
|
|
|
|
auto it = achievementNameCache_.find(id);
|
|
|
|
|
|
if (it != achievementNameCache_.end()) return it->second;
|
|
|
|
|
|
static const std::string kEmpty;
|
|
|
|
|
|
return kEmpty;
|
|
|
|
|
|
}
|
2026-03-09 13:53:42 -07:00
|
|
|
|
|
2026-03-09 15:46:19 -07:00
|
|
|
|
// Server-triggered music callback — fires when SMSG_PLAY_MUSIC is received.
|
|
|
|
|
|
// The soundId corresponds to a SoundEntries.dbc record. The receiver is
|
|
|
|
|
|
// responsible for looking up the file path and forwarding to MusicManager.
|
|
|
|
|
|
using PlayMusicCallback = std::function<void(uint32_t soundId)>;
|
|
|
|
|
|
void setPlayMusicCallback(PlayMusicCallback cb) { playMusicCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-09 16:11:19 -07:00
|
|
|
|
// Server-triggered 2-D sound effect callback — fires when SMSG_PLAY_SOUND is received.
|
|
|
|
|
|
// The soundId corresponds to a SoundEntries.dbc record.
|
|
|
|
|
|
using PlaySoundCallback = std::function<void(uint32_t soundId)>;
|
|
|
|
|
|
void setPlaySoundCallback(PlaySoundCallback cb) { playSoundCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-03-09 16:16:39 -07:00
|
|
|
|
// Server-triggered 3-D positional sound callback — fires for SMSG_PLAY_OBJECT_SOUND and
|
|
|
|
|
|
// SMSG_PLAY_SPELL_IMPACT. Includes sourceGuid so the receiver can look up world position.
|
|
|
|
|
|
using PlayPositionalSoundCallback = std::function<void(uint32_t soundId, uint64_t sourceGuid)>;
|
|
|
|
|
|
void setPlayPositionalSoundCallback(PlayPositionalSoundCallback cb) { playPositionalSoundCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 17:59:40 -08:00
|
|
|
|
// Mount state
|
|
|
|
|
|
using MountCallback = std::function<void(uint32_t mountDisplayId)>; // 0 = dismount
|
|
|
|
|
|
void setMountCallback(MountCallback cb) { mountCallback_ = std::move(cb); }
|
2026-02-08 21:32:38 -08:00
|
|
|
|
|
|
|
|
|
|
// Taxi terrain precaching callback
|
|
|
|
|
|
using TaxiPrecacheCallback = std::function<void(const std::vector<glm::vec3>&)>;
|
|
|
|
|
|
void setTaxiPrecacheCallback(TaxiPrecacheCallback cb) { taxiPrecacheCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-08 22:05:38 -08:00
|
|
|
|
// Taxi orientation callback (for mount rotation: yaw, pitch, roll in radians)
|
|
|
|
|
|
using TaxiOrientationCallback = std::function<void(float yaw, float pitch, float roll)>;
|
2026-02-08 22:00:33 -08:00
|
|
|
|
void setTaxiOrientationCallback(TaxiOrientationCallback cb) { taxiOrientationCallback_ = std::move(cb); }
|
|
|
|
|
|
|
|
|
|
|
|
// Callback for when taxi flight is about to start (after mounting delay, before movement begins)
|
|
|
|
|
|
using TaxiFlightStartCallback = std::function<void()>;
|
|
|
|
|
|
void setTaxiFlightStartCallback(TaxiFlightStartCallback cb) { taxiFlightStartCallback_ = std::move(cb); }
|
|
|
|
|
|
|
2026-02-07 17:59:40 -08:00
|
|
|
|
bool isMounted() const { return currentMountDisplayId_ != 0; }
|
2026-02-07 21:00:05 -08:00
|
|
|
|
bool isHostileAttacker(uint64_t guid) const { return hostileAttackers_.count(guid) > 0; }
|
2026-02-07 17:59:40 -08:00
|
|
|
|
float getServerRunSpeed() const { return serverRunSpeed_; }
|
2026-03-10 13:11:50 -07:00
|
|
|
|
float getServerWalkSpeed() const { return serverWalkSpeed_; }
|
|
|
|
|
|
float getServerSwimSpeed() const { return serverSwimSpeed_; }
|
2026-03-10 13:51:47 -07:00
|
|
|
|
float getServerSwimBackSpeed() const { return serverSwimBackSpeed_; }
|
2026-03-10 13:25:10 -07:00
|
|
|
|
float getServerFlightSpeed() const { return serverFlightSpeed_; }
|
2026-03-10 14:05:50 -07:00
|
|
|
|
float getServerFlightBackSpeed() const { return serverFlightBackSpeed_; }
|
2026-03-10 13:28:53 -07:00
|
|
|
|
float getServerRunBackSpeed() const { return serverRunBackSpeed_; }
|
2026-03-10 14:18:25 -07:00
|
|
|
|
float getServerTurnRate() const { return serverTurnRate_; }
|
2026-03-10 13:01:44 -07:00
|
|
|
|
bool isPlayerRooted() const {
|
|
|
|
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::ROOT)) != 0;
|
|
|
|
|
|
}
|
2026-03-10 13:07:34 -07:00
|
|
|
|
bool isGravityDisabled() const {
|
|
|
|
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::LEVITATING)) != 0;
|
|
|
|
|
|
}
|
2026-03-10 13:14:52 -07:00
|
|
|
|
bool isFeatherFalling() const {
|
|
|
|
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::FEATHER_FALL)) != 0;
|
|
|
|
|
|
}
|
2026-03-10 13:18:04 -07:00
|
|
|
|
bool isWaterWalking() const {
|
|
|
|
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::WATER_WALK)) != 0;
|
|
|
|
|
|
}
|
physics: implement player-controlled flying mount physics
When CAN_FLY + FLYING movement flags are both set (flying mounts, Druid
Flight Form), the CameraController now uses 3D pitch-following movement
instead of ground physics:
- Forward/back follows the camera's 3D look direction (ascend when
looking up, descend when looking down)
- Space = ascend vertically, X (while mounted) = descend
- No gravity, no grounding, no jump coyote time
- Fall-damage checks suppressed (grounded=true)
Also wire up all remaining server movement state flags to CameraController:
- Feather Fall: cap terminal velocity at -2 m/s
- Water Walk: clamp to water surface, skip swim entry
- Flying: 3D movement with no gravity
All states synced each frame from GameHandler via isPlayerFlying(),
isFeatherFalling(), isWaterWalking(), isGravityDisabled().
2026-03-10 13:23:38 -07:00
|
|
|
|
bool isPlayerFlying() const {
|
|
|
|
|
|
const uint32_t flyMask = static_cast<uint32_t>(MovementFlags::CAN_FLY) |
|
|
|
|
|
|
static_cast<uint32_t>(MovementFlags::FLYING);
|
|
|
|
|
|
return (movementInfo.flags & flyMask) == flyMask;
|
|
|
|
|
|
}
|
2026-03-10 13:39:23 -07:00
|
|
|
|
bool isHovering() const {
|
|
|
|
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::HOVER)) != 0;
|
|
|
|
|
|
}
|
2026-03-10 14:46:17 -07:00
|
|
|
|
bool isSwimming() const {
|
|
|
|
|
|
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::SWIMMING)) != 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Set the character pitch angle (radians) for movement packets (flight / swimming).
|
|
|
|
|
|
// Positive = nose up, negative = nose down.
|
|
|
|
|
|
void setMovementPitch(float radians) { movementInfo.pitch = radians; }
|
2026-02-07 17:59:40 -08:00
|
|
|
|
void dismount();
|
|
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
|
// Taxi / Flight Paths
|
|
|
|
|
|
bool isTaxiWindowOpen() const { return taxiWindowOpen_; }
|
|
|
|
|
|
void closeTaxi();
|
|
|
|
|
|
void activateTaxi(uint32_t destNodeId);
|
|
|
|
|
|
bool isOnTaxiFlight() const { return onTaxiFlight_; }
|
2026-02-11 19:28:15 -08:00
|
|
|
|
bool isTaxiMountActive() const { return taxiMountActive_; }
|
2026-02-11 21:14:35 -08:00
|
|
|
|
bool isTaxiActivationPending() const { return taxiActivatePending_; }
|
|
|
|
|
|
void forceClearTaxiAndMovementState();
|
2026-02-07 16:59:20 -08:00
|
|
|
|
const ShowTaxiNodesData& getTaxiData() const { return currentTaxiData_; }
|
|
|
|
|
|
uint32_t getTaxiCurrentNode() const { return currentTaxiData_.nearestNode; }
|
|
|
|
|
|
|
|
|
|
|
|
struct TaxiNode {
|
|
|
|
|
|
uint32_t id = 0;
|
|
|
|
|
|
uint32_t mapId = 0;
|
|
|
|
|
|
float x = 0, y = 0, z = 0;
|
|
|
|
|
|
std::string name;
|
2026-02-08 03:05:38 -08:00
|
|
|
|
uint32_t mountDisplayIdAlliance = 0;
|
|
|
|
|
|
uint32_t mountDisplayIdHorde = 0;
|
2026-02-07 16:59:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
struct TaxiPathEdge {
|
|
|
|
|
|
uint32_t pathId = 0;
|
|
|
|
|
|
uint32_t fromNode = 0, toNode = 0;
|
|
|
|
|
|
uint32_t cost = 0;
|
|
|
|
|
|
};
|
2026-02-08 21:32:38 -08:00
|
|
|
|
struct TaxiPathNode {
|
|
|
|
|
|
uint32_t id = 0;
|
|
|
|
|
|
uint32_t pathId = 0;
|
|
|
|
|
|
uint32_t nodeIndex = 0;
|
|
|
|
|
|
uint32_t mapId = 0;
|
|
|
|
|
|
float x = 0, y = 0, z = 0;
|
|
|
|
|
|
};
|
2026-02-07 16:59:20 -08:00
|
|
|
|
const std::unordered_map<uint32_t, TaxiNode>& getTaxiNodes() const { return taxiNodes_; }
|
2026-02-07 19:04:15 -08:00
|
|
|
|
uint32_t getTaxiCostTo(uint32_t destNodeId) const;
|
2026-03-09 19:30:18 -07:00
|
|
|
|
bool taxiNpcHasRoutes(uint64_t guid) const {
|
|
|
|
|
|
auto it = taxiNpcHasRoutes_.find(guid);
|
|
|
|
|
|
return it != taxiNpcHasRoutes_.end() && it->second;
|
|
|
|
|
|
}
|
2026-02-07 16:59:20 -08:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// Vendor
|
|
|
|
|
|
void openVendor(uint64_t npcGuid);
|
2026-02-06 11:59:51 -08:00
|
|
|
|
void closeVendor();
|
Fix vendor buying, improve character select, parallelize WMO culling, and optimize collision
- Fix CMSG_BUY_ITEM count field from uint8 to uint32 (server silently dropped undersized packets)
- Character select screen: remember last selected character, two-column layout with details panel, double-click to enter world, responsive window sizing
- Fix stale character data between logins by replacing static init flag with per-character GUID tracking
- Parallelize WMO visibility culling across worker threads (same pattern as M2 renderer)
- Optimize WMO collision queries with world-space group bounds early rejection in getFloorHeight, checkWallCollision, isInsideWMO, and raycastBoundingBoxes
- Reduce camera ground samples from 5 to 3 movement-aligned probes
- Add WMO interior lighting, unlit materials, vertex color multiply, and alpha blending support
2026-02-07 15:29:19 -08:00
|
|
|
|
void buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count);
|
2026-02-06 19:50:22 -08:00
|
|
|
|
void sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count);
|
2026-02-06 13:47:03 -08:00
|
|
|
|
void sellItemBySlot(int backpackIndex);
|
2026-02-17 01:00:04 -08:00
|
|
|
|
void sellItemInBag(int bagIndex, int slotIndex);
|
2026-02-19 05:28:13 -08:00
|
|
|
|
struct BuybackItem {
|
|
|
|
|
|
uint64_t itemGuid = 0;
|
|
|
|
|
|
ItemDef item;
|
|
|
|
|
|
uint32_t count = 1;
|
|
|
|
|
|
};
|
|
|
|
|
|
void buyBackItem(uint32_t buybackSlot);
|
2026-03-10 16:21:09 -07:00
|
|
|
|
void repairItem(uint64_t vendorGuid, uint64_t itemGuid);
|
|
|
|
|
|
void repairAll(uint64_t vendorGuid, bool useGuildBank = false);
|
2026-02-19 05:28:13 -08:00
|
|
|
|
const std::deque<BuybackItem>& getBuybackItems() const { return buybackItems_; }
|
2026-02-06 18:34:45 -08:00
|
|
|
|
void autoEquipItemBySlot(int backpackIndex);
|
2026-02-17 01:00:04 -08:00
|
|
|
|
void autoEquipItemInBag(int bagIndex, int slotIndex);
|
2026-02-06 18:34:45 -08:00
|
|
|
|
void useItemBySlot(int backpackIndex);
|
2026-02-17 01:00:04 -08:00
|
|
|
|
void useItemInBag(int bagIndex, int slotIndex);
|
2026-02-19 06:34:06 -08:00
|
|
|
|
void destroyItem(uint8_t bag, uint8_t slot, uint8_t count = 1);
|
2026-02-17 01:00:04 -08:00
|
|
|
|
void swapContainerItems(uint8_t srcBag, uint8_t srcSlot, uint8_t dstBag, uint8_t dstSlot);
|
2026-02-19 22:34:22 -08:00
|
|
|
|
void swapBagSlots(int srcBagIndex, int dstBagIndex);
|
2026-02-06 19:17:35 -08:00
|
|
|
|
void useItemById(uint32_t itemId);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
bool isVendorWindowOpen() const { return vendorWindowOpen; }
|
|
|
|
|
|
const ListInventoryData& getVendorItems() const { return currentVendorItems; }
|
2026-03-10 16:21:09 -07:00
|
|
|
|
void setVendorCanRepair(bool v) { currentVendorItems.canRepair = v; }
|
2026-02-08 14:33:39 -08:00
|
|
|
|
|
2026-02-15 14:00:41 -08:00
|
|
|
|
// Mail
|
|
|
|
|
|
bool isMailboxOpen() const { return mailboxOpen_; }
|
|
|
|
|
|
const std::vector<MailMessage>& getMailInbox() const { return mailInbox_; }
|
|
|
|
|
|
int getSelectedMailIndex() const { return selectedMailIndex_; }
|
|
|
|
|
|
void setSelectedMailIndex(int idx) { selectedMailIndex_ = idx; }
|
|
|
|
|
|
bool isMailComposeOpen() const { return showMailCompose_; }
|
2026-02-25 14:11:09 -08:00
|
|
|
|
void openMailCompose() { showMailCompose_ = true; clearMailAttachments(); }
|
|
|
|
|
|
void closeMailCompose() { showMailCompose_ = false; clearMailAttachments(); }
|
2026-02-16 18:46:44 -08:00
|
|
|
|
bool hasNewMail() const { return hasNewMail_; }
|
2026-02-15 14:00:41 -08:00
|
|
|
|
void closeMailbox();
|
|
|
|
|
|
void sendMail(const std::string& recipient, const std::string& subject,
|
|
|
|
|
|
const std::string& body, uint32_t money, uint32_t cod = 0);
|
2026-02-25 14:11:09 -08:00
|
|
|
|
|
|
|
|
|
|
// Mail attachments (max 12 per WotLK)
|
|
|
|
|
|
static constexpr int MAIL_MAX_ATTACHMENTS = 12;
|
|
|
|
|
|
struct MailAttachSlot {
|
|
|
|
|
|
uint64_t itemGuid = 0;
|
|
|
|
|
|
game::ItemDef item;
|
|
|
|
|
|
uint8_t srcBag = 0xFF; // source container for return
|
|
|
|
|
|
uint8_t srcSlot = 0;
|
|
|
|
|
|
bool occupied() const { return itemGuid != 0; }
|
|
|
|
|
|
};
|
|
|
|
|
|
bool attachItemFromBackpack(int backpackIndex);
|
|
|
|
|
|
bool attachItemFromBag(int bagIndex, int slotIndex);
|
|
|
|
|
|
bool detachMailAttachment(int attachIndex);
|
|
|
|
|
|
void clearMailAttachments();
|
|
|
|
|
|
const std::array<MailAttachSlot, 12>& getMailAttachments() const { return mailAttachments_; }
|
|
|
|
|
|
int getMailAttachmentCount() const;
|
2026-02-15 14:00:41 -08:00
|
|
|
|
void mailTakeMoney(uint32_t mailId);
|
|
|
|
|
|
void mailTakeItem(uint32_t mailId, uint32_t itemIndex);
|
|
|
|
|
|
void mailDelete(uint32_t mailId);
|
|
|
|
|
|
void mailMarkAsRead(uint32_t mailId);
|
|
|
|
|
|
void refreshMailList();
|
|
|
|
|
|
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
|
// Bank
|
|
|
|
|
|
void openBank(uint64_t guid);
|
|
|
|
|
|
void closeBank();
|
|
|
|
|
|
void buyBankSlot();
|
|
|
|
|
|
void depositItem(uint8_t srcBag, uint8_t srcSlot);
|
|
|
|
|
|
void withdrawItem(uint8_t srcBag, uint8_t srcSlot);
|
|
|
|
|
|
bool isBankOpen() const { return bankOpen_; }
|
|
|
|
|
|
uint64_t getBankerGuid() const { return bankerGuid_; }
|
2026-02-26 11:12:34 -08:00
|
|
|
|
int getEffectiveBankSlots() const { return effectiveBankSlots_; }
|
|
|
|
|
|
int getEffectiveBankBagSlots() const { return effectiveBankBagSlots_; }
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
|
|
|
|
|
|
|
// Guild Bank
|
|
|
|
|
|
void openGuildBank(uint64_t guid);
|
|
|
|
|
|
void closeGuildBank();
|
|
|
|
|
|
void queryGuildBankTab(uint8_t tabId);
|
|
|
|
|
|
void buyGuildBankTab();
|
|
|
|
|
|
void depositGuildBankMoney(uint32_t amount);
|
|
|
|
|
|
void withdrawGuildBankMoney(uint32_t amount);
|
|
|
|
|
|
void guildBankWithdrawItem(uint8_t tabId, uint8_t bankSlot, uint8_t destBag, uint8_t destSlot);
|
|
|
|
|
|
void guildBankDepositItem(uint8_t tabId, uint8_t bankSlot, uint8_t srcBag, uint8_t srcSlot);
|
|
|
|
|
|
bool isGuildBankOpen() const { return guildBankOpen_; }
|
|
|
|
|
|
const GuildBankData& getGuildBankData() const { return guildBankData_; }
|
|
|
|
|
|
uint8_t getGuildBankActiveTab() const { return guildBankActiveTab_; }
|
|
|
|
|
|
void setGuildBankActiveTab(uint8_t tab) { guildBankActiveTab_ = tab; }
|
|
|
|
|
|
|
|
|
|
|
|
// Auction House
|
|
|
|
|
|
void openAuctionHouse(uint64_t guid);
|
|
|
|
|
|
void closeAuctionHouse();
|
|
|
|
|
|
void auctionSearch(const std::string& name, uint8_t levelMin, uint8_t levelMax,
|
|
|
|
|
|
uint32_t quality, uint32_t itemClass, uint32_t itemSubClass,
|
|
|
|
|
|
uint32_t invTypeMask, uint8_t usableOnly, uint32_t offset = 0);
|
|
|
|
|
|
void auctionSellItem(uint64_t itemGuid, uint32_t stackCount, uint32_t bid,
|
|
|
|
|
|
uint32_t buyout, uint32_t duration);
|
|
|
|
|
|
void auctionPlaceBid(uint32_t auctionId, uint32_t amount);
|
|
|
|
|
|
void auctionBuyout(uint32_t auctionId, uint32_t buyoutPrice);
|
|
|
|
|
|
void auctionCancelItem(uint32_t auctionId);
|
|
|
|
|
|
void auctionListOwnerItems(uint32_t offset = 0);
|
|
|
|
|
|
void auctionListBidderItems(uint32_t offset = 0);
|
|
|
|
|
|
bool isAuctionHouseOpen() const { return auctionOpen_; }
|
|
|
|
|
|
uint64_t getAuctioneerGuid() const { return auctioneerGuid_; }
|
|
|
|
|
|
const AuctionListResult& getAuctionBrowseResults() const { return auctionBrowseResults_; }
|
|
|
|
|
|
const AuctionListResult& getAuctionOwnerResults() const { return auctionOwnerResults_; }
|
|
|
|
|
|
const AuctionListResult& getAuctionBidderResults() const { return auctionBidderResults_; }
|
|
|
|
|
|
int getAuctionActiveTab() const { return auctionActiveTab_; }
|
|
|
|
|
|
void setAuctionActiveTab(int tab) { auctionActiveTab_ = tab; }
|
|
|
|
|
|
float getAuctionSearchDelay() const { return auctionSearchDelayTimer_; }
|
|
|
|
|
|
|
2026-02-08 14:33:39 -08:00
|
|
|
|
// Trainer
|
|
|
|
|
|
bool isTrainerWindowOpen() const { return trainerWindowOpen_; }
|
|
|
|
|
|
const TrainerListData& getTrainerSpells() const { return currentTrainerList_; }
|
|
|
|
|
|
void trainSpell(uint32_t spellId);
|
|
|
|
|
|
void closeTrainer();
|
|
|
|
|
|
const std::string& getSpellName(uint32_t spellId) const;
|
|
|
|
|
|
const std::string& getSpellRank(uint32_t spellId) const;
|
2026-02-08 14:46:01 -08:00
|
|
|
|
const std::string& getSkillLineName(uint32_t spellId) const;
|
|
|
|
|
|
|
|
|
|
|
|
struct TrainerTab {
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
std::vector<const TrainerSpell*> spells;
|
|
|
|
|
|
};
|
|
|
|
|
|
const std::vector<TrainerTab>& getTrainerTabs() const { return trainerTabs_; }
|
2026-02-06 11:59:51 -08:00
|
|
|
|
const ItemQueryResponseData* getItemInfo(uint32_t itemId) const {
|
|
|
|
|
|
auto it = itemInfoCache_.find(itemId);
|
|
|
|
|
|
return (it != itemInfoCache_.end()) ? &it->second : nullptr;
|
|
|
|
|
|
}
|
2026-02-14 15:58:54 -08:00
|
|
|
|
// Request item info from server if not already cached/pending
|
|
|
|
|
|
void ensureItemInfo(uint32_t entry) {
|
|
|
|
|
|
if (entry == 0 || itemInfoCache_.count(entry) || pendingItemQueries_.count(entry)) return;
|
|
|
|
|
|
queryItemInfo(entry, 0);
|
|
|
|
|
|
}
|
2026-02-06 13:47:03 -08:00
|
|
|
|
uint64_t getBackpackItemGuid(int index) const {
|
|
|
|
|
|
if (index < 0 || index >= static_cast<int>(backpackSlotGuids_.size())) return 0;
|
|
|
|
|
|
return backpackSlotGuids_[index];
|
|
|
|
|
|
}
|
|
|
|
|
|
uint64_t getVendorGuid() const { return currentVendorItems.vendorGuid; }
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Set callbacks
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setOnSuccess(WorldConnectSuccessCallback callback) { onSuccess = callback; }
|
|
|
|
|
|
void setOnFailure(WorldConnectFailureCallback callback) { onFailure = callback; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Update - call regularly (e.g., each frame)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param deltaTime Time since last update in seconds
|
|
|
|
|
|
*/
|
|
|
|
|
|
void update(float deltaTime);
|
|
|
|
|
|
|
2026-02-14 19:27:35 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Reset DBC-backed caches so they reload from new expansion data.
|
|
|
|
|
|
* Called by Application when the expansion profile changes.
|
|
|
|
|
|
*/
|
|
|
|
|
|
void resetDbcCaches();
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
private:
|
2026-02-07 20:51:53 -08:00
|
|
|
|
void autoTargetAttacker(uint64_t attackerGuid);
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle incoming packet from world server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handlePacket(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_AUTH_CHALLENGE from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleAuthChallenge(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_AUTH_RESPONSE from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleAuthResponse(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_CHAR_ENUM from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleCharEnum(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-17 13:59:29 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_CHARACTER_LOGIN_FAILED from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleCharLoginFailed(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_LOGIN_VERIFY_WORLD from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleLoginVerifyWorld(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-12 02:09:15 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_CLIENTCACHE_VERSION from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleClientCacheVersion(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_TUTORIAL_FLAGS from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleTutorialFlags(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_WARDEN_DATA gate packet from server.
|
|
|
|
|
|
* We do not implement anti-cheat exchange for third-party realms.
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleWardenData(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_ACCOUNT_DATA_TIMES from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleAccountDataTimes(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_MOTD from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleMotd(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-20 00:28:51 -08:00
|
|
|
|
/** Handle SMSG_NOTIFICATION (vanilla/classic server notification string) */
|
|
|
|
|
|
void handleNotification(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_PONG from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handlePong(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_UPDATE_OBJECT from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleUpdateObject(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-05 21:55:52 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_COMPRESSED_UPDATE_OBJECT from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleCompressedUpdateObject(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_DESTROY_OBJECT from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleDestroyObject(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Handle SMSG_MESSAGECHAT from server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleMessageChat(network::Packet& packet);
|
2026-02-14 14:30:09 -08:00
|
|
|
|
void handleTextEmote(network::Packet& packet);
|
|
|
|
|
|
void handleChannelNotify(network::Packet& packet);
|
|
|
|
|
|
void autoJoinDefaultChannels();
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 1 handlers ----
|
|
|
|
|
|
void handleNameQueryResponse(network::Packet& packet);
|
|
|
|
|
|
void handleCreatureQueryResponse(network::Packet& packet);
|
2026-02-08 00:59:40 -08:00
|
|
|
|
void handleGameObjectQueryResponse(network::Packet& packet);
|
2026-02-20 23:31:30 -08:00
|
|
|
|
void handleGameObjectPageText(network::Packet& packet);
|
|
|
|
|
|
void handlePageTextQueryResponse(network::Packet& packet);
|
2026-02-06 03:11:43 -08:00
|
|
|
|
void handleItemQueryResponse(network::Packet& packet);
|
2026-02-13 20:26:55 -08:00
|
|
|
|
void handleInspectResults(network::Packet& packet);
|
2026-02-06 03:11:43 -08:00
|
|
|
|
void queryItemInfo(uint32_t entry, uint64_t guid);
|
|
|
|
|
|
void rebuildOnlineInventory();
|
2026-02-13 20:10:19 -08:00
|
|
|
|
void maybeDetectVisibleItemLayout();
|
|
|
|
|
|
void updateOtherPlayerVisibleItems(uint64_t guid, const std::map<uint16_t, uint32_t>& fields);
|
|
|
|
|
|
void emitOtherPlayerEquipment(uint64_t guid);
|
|
|
|
|
|
void emitAllOtherPlayerEquipment();
|
2026-02-06 18:34:45 -08:00
|
|
|
|
void detectInventorySlotBases(const std::map<uint16_t, uint32_t>& fields);
|
|
|
|
|
|
bool applyInventoryFields(const std::map<uint16_t, uint32_t>& fields);
|
2026-02-13 22:14:34 -08:00
|
|
|
|
void extractContainerFields(uint64_t containerGuid, const std::map<uint16_t, uint32_t>& fields);
|
2026-02-06 18:52:28 -08:00
|
|
|
|
uint64_t resolveOnlineItemGuid(uint32_t itemId) const;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
|
|
|
|
|
// ---- Phase 2 handlers ----
|
|
|
|
|
|
void handleAttackStart(network::Packet& packet);
|
|
|
|
|
|
void handleAttackStop(network::Packet& packet);
|
|
|
|
|
|
void handleAttackerStateUpdate(network::Packet& packet);
|
|
|
|
|
|
void handleSpellDamageLog(network::Packet& packet);
|
|
|
|
|
|
void handleSpellHealLog(network::Packet& packet);
|
|
|
|
|
|
|
2026-03-09 15:23:02 -07:00
|
|
|
|
// ---- Equipment set handler ----
|
|
|
|
|
|
void handleEquipmentSetList(network::Packet& packet);
|
|
|
|
|
|
void handleUpdateAuraDuration(uint8_t slot, uint32_t durationMs);
|
|
|
|
|
|
void handleSetForcedReactions(network::Packet& packet);
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 3 handlers ----
|
|
|
|
|
|
void handleInitialSpells(network::Packet& packet);
|
|
|
|
|
|
void handleCastFailed(network::Packet& packet);
|
|
|
|
|
|
void handleSpellStart(network::Packet& packet);
|
|
|
|
|
|
void handleSpellGo(network::Packet& packet);
|
|
|
|
|
|
void handleSpellCooldown(network::Packet& packet);
|
|
|
|
|
|
void handleCooldownEvent(network::Packet& packet);
|
2026-03-09 13:53:42 -07:00
|
|
|
|
void handleAchievementEarned(network::Packet& packet);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void handleAuraUpdate(network::Packet& packet, bool isAll);
|
|
|
|
|
|
void handleLearnedSpell(network::Packet& packet);
|
2026-02-10 01:24:37 -08:00
|
|
|
|
void handleSupercededSpell(network::Packet& packet);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void handleRemovedSpell(network::Packet& packet);
|
2026-02-10 01:24:37 -08:00
|
|
|
|
void handleUnlearnSpells(network::Packet& packet);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
|
// ---- Talent handlers ----
|
|
|
|
|
|
void handleTalentsInfo(network::Packet& packet);
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 4 handlers ----
|
|
|
|
|
|
void handleGroupInvite(network::Packet& packet);
|
|
|
|
|
|
void handleGroupDecline(network::Packet& packet);
|
|
|
|
|
|
void handleGroupList(network::Packet& packet);
|
|
|
|
|
|
void handleGroupUninvite(network::Packet& packet);
|
|
|
|
|
|
void handlePartyCommandResult(network::Packet& packet);
|
2026-02-26 10:25:55 -08:00
|
|
|
|
void handlePartyMemberStats(network::Packet& packet, bool isFull);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-13 21:39:48 -08:00
|
|
|
|
// ---- Guild handlers ----
|
|
|
|
|
|
void handleGuildInfo(network::Packet& packet);
|
|
|
|
|
|
void handleGuildRoster(network::Packet& packet);
|
|
|
|
|
|
void handleGuildQueryResponse(network::Packet& packet);
|
|
|
|
|
|
void handleGuildEvent(network::Packet& packet);
|
|
|
|
|
|
void handleGuildInvite(network::Packet& packet);
|
|
|
|
|
|
void handleGuildCommandResult(network::Packet& packet);
|
2026-02-25 14:44:44 -08:00
|
|
|
|
void handlePetitionShowlist(network::Packet& packet);
|
2026-02-26 10:41:29 -08:00
|
|
|
|
void handlePetSpells(network::Packet& packet);
|
2026-02-25 14:44:44 -08:00
|
|
|
|
void handleTurnInPetitionResults(network::Packet& packet);
|
2026-02-13 21:39:48 -08:00
|
|
|
|
|
2026-02-05 14:13:48 -08:00
|
|
|
|
// ---- Character creation handler ----
|
|
|
|
|
|
void handleCharCreateResponse(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-05 12:07:58 -08:00
|
|
|
|
// ---- XP handler ----
|
|
|
|
|
|
void handleXpGain(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-06 13:47:03 -08:00
|
|
|
|
// ---- Creature movement handler ----
|
|
|
|
|
|
void handleMonsterMove(network::Packet& packet);
|
2026-02-18 03:15:25 -08:00
|
|
|
|
void handleCompressedMoves(network::Packet& packet);
|
2026-02-11 00:54:38 -08:00
|
|
|
|
void handleMonsterMoveTransport(network::Packet& packet);
|
2026-02-06 13:47:03 -08:00
|
|
|
|
|
2026-02-13 18:59:09 -08:00
|
|
|
|
// ---- Other player movement (MSG_MOVE_* from server) ----
|
|
|
|
|
|
void handleOtherPlayerMovement(network::Packet& packet);
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 5 handlers ----
|
|
|
|
|
|
void handleLootResponse(network::Packet& packet);
|
|
|
|
|
|
void handleLootReleaseResponse(network::Packet& packet);
|
|
|
|
|
|
void handleLootRemoved(network::Packet& packet);
|
|
|
|
|
|
void handleGossipMessage(network::Packet& packet);
|
2026-02-19 02:53:44 -08:00
|
|
|
|
void handleQuestgiverQuestList(network::Packet& packet);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void handleGossipComplete(network::Packet& packet);
|
2026-03-11 00:18:23 -07:00
|
|
|
|
void handleQuestPoiQueryResponse(network::Packet& packet);
|
2026-02-06 11:59:51 -08:00
|
|
|
|
void handleQuestDetails(network::Packet& packet);
|
2026-02-06 21:50:15 -08:00
|
|
|
|
void handleQuestRequestItems(network::Packet& packet);
|
|
|
|
|
|
void handleQuestOfferReward(network::Packet& packet);
|
2026-02-20 17:14:13 -08:00
|
|
|
|
void clearPendingQuestAccept(uint32_t questId);
|
|
|
|
|
|
void triggerQuestAcceptResync(uint32_t questId, uint64_t npcGuid, const char* reason);
|
|
|
|
|
|
bool hasQuestInLog(uint32_t questId) const;
|
2026-02-20 23:20:02 -08:00
|
|
|
|
int findQuestLogSlotIndexFromServer(uint32_t questId) const;
|
2026-02-20 17:14:13 -08:00
|
|
|
|
void addQuestToLocalLogIfMissing(uint32_t questId, const std::string& title, const std::string& objectives);
|
|
|
|
|
|
bool resyncQuestLogFromServerSlots(bool forceQueryMetadata);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void handleListInventory(network::Packet& packet);
|
2026-02-05 14:01:26 -08:00
|
|
|
|
void addMoneyCopper(uint32_t amount);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
|
// ---- Teleport handler ----
|
|
|
|
|
|
void handleTeleportAck(network::Packet& packet);
|
2026-02-08 03:05:38 -08:00
|
|
|
|
void handleNewWorld(network::Packet& packet);
|
2026-02-07 16:59:20 -08:00
|
|
|
|
|
Add missing movement ACK responses to avoid server stalls
Implement generic handlers for force speed changes (walk, run back,
swim, swim back, flight, flight back, turn rate, pitch rate),
movement flag toggles (CAN_FLY, HOVER, feather fall, water walk),
and knockback ACKs. Fix SMSG_TIME_SYNC_REQ to respond with
CMSG_TIME_SYNC_RESP instead of silently dropping.
2026-02-26 03:02:51 -08:00
|
|
|
|
// ---- Movement ACK handlers ----
|
2026-02-07 17:59:40 -08:00
|
|
|
|
void handleForceRunSpeedChange(network::Packet& packet);
|
Add missing movement ACK responses to avoid server stalls
Implement generic handlers for force speed changes (walk, run back,
swim, swim back, flight, flight back, turn rate, pitch rate),
movement flag toggles (CAN_FLY, HOVER, feather fall, water walk),
and knockback ACKs. Fix SMSG_TIME_SYNC_REQ to respond with
CMSG_TIME_SYNC_RESP instead of silently dropping.
2026-02-26 03:02:51 -08:00
|
|
|
|
void handleForceSpeedChange(network::Packet& packet, const char* name, Opcode ackOpcode, float* speedStorage);
|
2026-02-20 03:14:48 -08:00
|
|
|
|
void handleForceMoveRootState(network::Packet& packet, bool rooted);
|
Add missing movement ACK responses to avoid server stalls
Implement generic handlers for force speed changes (walk, run back,
swim, swim back, flight, flight back, turn rate, pitch rate),
movement flag toggles (CAN_FLY, HOVER, feather fall, water walk),
and knockback ACKs. Fix SMSG_TIME_SYNC_REQ to respond with
CMSG_TIME_SYNC_RESP instead of silently dropping.
2026-02-26 03:02:51 -08:00
|
|
|
|
void handleForceMoveFlagChange(network::Packet& packet, const char* name, Opcode ackOpcode, uint32_t flag, bool set);
|
2026-03-10 11:40:46 -07:00
|
|
|
|
void handleMoveSetCollisionHeight(network::Packet& packet);
|
Add missing movement ACK responses to avoid server stalls
Implement generic handlers for force speed changes (walk, run back,
swim, swim back, flight, flight back, turn rate, pitch rate),
movement flag toggles (CAN_FLY, HOVER, feather fall, water walk),
and knockback ACKs. Fix SMSG_TIME_SYNC_REQ to respond with
CMSG_TIME_SYNC_RESP instead of silently dropping.
2026-02-26 03:02:51 -08:00
|
|
|
|
void handleMoveKnockBack(network::Packet& packet);
|
2026-02-07 17:59:40 -08:00
|
|
|
|
|
2026-02-26 17:56:11 -08:00
|
|
|
|
// ---- Area trigger detection ----
|
|
|
|
|
|
void loadAreaTriggerDbc();
|
|
|
|
|
|
void checkAreaTriggers();
|
|
|
|
|
|
|
2026-03-09 13:36:23 -07:00
|
|
|
|
// ---- Instance lockout handler ----
|
|
|
|
|
|
void handleRaidInstanceInfo(network::Packet& packet);
|
2026-03-09 14:15:59 -07:00
|
|
|
|
void handleItemTextQueryResponse(network::Packet& packet);
|
2026-03-09 14:14:15 -07:00
|
|
|
|
void handleQuestConfirmAccept(network::Packet& packet);
|
2026-03-09 14:07:50 -07:00
|
|
|
|
void handleSummonRequest(network::Packet& packet);
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
void handleTradeStatus(network::Packet& packet);
|
2026-03-11 00:44:07 -07:00
|
|
|
|
void handleTradeStatusExtended(network::Packet& packet);
|
|
|
|
|
|
void resetTradeState();
|
2026-03-09 13:58:02 -07:00
|
|
|
|
void handleDuelRequested(network::Packet& packet);
|
|
|
|
|
|
void handleDuelComplete(network::Packet& packet);
|
|
|
|
|
|
void handleDuelWinner(network::Packet& packet);
|
2026-03-09 14:01:27 -07:00
|
|
|
|
void handleLootRoll(network::Packet& packet);
|
|
|
|
|
|
void handleLootRollWon(network::Packet& packet);
|
2026-03-09 13:36:23 -07:00
|
|
|
|
|
2026-03-09 13:30:23 -07:00
|
|
|
|
// ---- LFG / Dungeon Finder handlers ----
|
|
|
|
|
|
void handleLfgJoinResult(network::Packet& packet);
|
|
|
|
|
|
void handleLfgQueueStatus(network::Packet& packet);
|
|
|
|
|
|
void handleLfgProposalUpdate(network::Packet& packet);
|
|
|
|
|
|
void handleLfgRoleCheckUpdate(network::Packet& packet);
|
|
|
|
|
|
void handleLfgUpdatePlayer(network::Packet& packet);
|
|
|
|
|
|
void handleLfgPlayerReward(network::Packet& packet);
|
|
|
|
|
|
void handleLfgBootProposalUpdate(network::Packet& packet);
|
|
|
|
|
|
void handleLfgTeleportDenied(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-07 23:47:43 -08:00
|
|
|
|
// ---- Arena / Battleground handlers ----
|
|
|
|
|
|
void handleBattlefieldStatus(network::Packet& packet);
|
2026-02-26 17:56:11 -08:00
|
|
|
|
void handleInstanceDifficulty(network::Packet& packet);
|
2026-02-07 23:47:43 -08:00
|
|
|
|
void handleArenaTeamCommandResult(network::Packet& packet);
|
|
|
|
|
|
void handleArenaTeamQueryResponse(network::Packet& packet);
|
|
|
|
|
|
void handleArenaTeamInvite(network::Packet& packet);
|
|
|
|
|
|
void handleArenaTeamEvent(network::Packet& packet);
|
|
|
|
|
|
void handleArenaError(network::Packet& packet);
|
|
|
|
|
|
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
|
// ---- Bank handlers ----
|
|
|
|
|
|
void handleShowBank(network::Packet& packet);
|
|
|
|
|
|
void handleBuyBankSlotResult(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Guild Bank handlers ----
|
|
|
|
|
|
void handleGuildBankList(network::Packet& packet);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Auction House handlers ----
|
|
|
|
|
|
void handleAuctionHello(network::Packet& packet);
|
|
|
|
|
|
void handleAuctionListResult(network::Packet& packet);
|
|
|
|
|
|
void handleAuctionOwnerListResult(network::Packet& packet);
|
|
|
|
|
|
void handleAuctionBidderListResult(network::Packet& packet);
|
|
|
|
|
|
void handleAuctionCommandResult(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-15 14:00:41 -08:00
|
|
|
|
// ---- Mail handlers ----
|
|
|
|
|
|
void handleShowMailbox(network::Packet& packet);
|
|
|
|
|
|
void handleMailListResult(network::Packet& packet);
|
|
|
|
|
|
void handleSendMailResult(network::Packet& packet);
|
|
|
|
|
|
void handleReceivedMail(network::Packet& packet);
|
2026-02-16 18:46:44 -08:00
|
|
|
|
void handleQueryNextMailTime(network::Packet& packet);
|
2026-02-15 14:00:41 -08:00
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
|
// ---- Taxi handlers ----
|
|
|
|
|
|
void handleShowTaxiNodes(network::Packet& packet);
|
|
|
|
|
|
void handleActivateTaxiReply(network::Packet& packet);
|
|
|
|
|
|
void loadTaxiDbc();
|
|
|
|
|
|
|
2026-02-07 12:43:32 -08:00
|
|
|
|
// ---- Server info handlers ----
|
|
|
|
|
|
void handleQueryTimeResponse(network::Packet& packet);
|
|
|
|
|
|
void handlePlayedTime(network::Packet& packet);
|
|
|
|
|
|
void handleWho(network::Packet& packet);
|
|
|
|
|
|
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
|
// ---- Social handlers ----
|
2026-03-10 01:15:51 -07:00
|
|
|
|
void handleFriendList(network::Packet& packet); // Classic SMSG_FRIEND_LIST
|
|
|
|
|
|
void handleContactList(network::Packet& packet); // WotLK SMSG_CONTACT_LIST (full parse)
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
|
void handleFriendStatus(network::Packet& packet);
|
|
|
|
|
|
void handleRandomRoll(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-07 12:58:11 -08:00
|
|
|
|
// ---- Logout handlers ----
|
|
|
|
|
|
void handleLogoutResponse(network::Packet& packet);
|
|
|
|
|
|
void handleLogoutComplete(network::Packet& packet);
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
void addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource);
|
2026-02-05 13:22:15 -08:00
|
|
|
|
void addSystemChatMessage(const std::string& message);
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Send CMSG_PING to server (heartbeat)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendPing();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Send CMSG_AUTH_SESSION to server
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendAuthSession();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Generate random client seed
|
|
|
|
|
|
*/
|
|
|
|
|
|
uint32_t generateClientSeed();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Change state with logging
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setState(WorldState newState);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Fail connection with reason
|
|
|
|
|
|
*/
|
|
|
|
|
|
void fail(const std::string& reason);
|
2026-02-12 00:04:53 -08:00
|
|
|
|
void updateAttachedTransportChildren(float deltaTime);
|
|
|
|
|
|
void setTransportAttachment(uint64_t childGuid, ObjectType type, uint64_t transportGuid,
|
|
|
|
|
|
const glm::vec3& localOffset, bool hasLocalOrientation,
|
|
|
|
|
|
float localOrientation);
|
|
|
|
|
|
void clearTransportAttachment(uint64_t childGuid);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
2026-02-12 22:56:36 -08:00
|
|
|
|
// Opcode translation table (expansion-specific wire ↔ logical mapping)
|
|
|
|
|
|
OpcodeTable opcodeTable_;
|
|
|
|
|
|
|
|
|
|
|
|
// Update field table (expansion-specific field index mapping)
|
|
|
|
|
|
UpdateFieldTable updateFieldTable_;
|
|
|
|
|
|
|
|
|
|
|
|
// Packet parsers (expansion-specific binary format handling)
|
|
|
|
|
|
std::unique_ptr<PacketParsers> packetParsers_;
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
// Network
|
|
|
|
|
|
std::unique_ptr<network::WorldSocket> socket;
|
|
|
|
|
|
|
|
|
|
|
|
// State
|
|
|
|
|
|
WorldState state = WorldState::DISCONNECTED;
|
|
|
|
|
|
|
|
|
|
|
|
// Authentication data
|
|
|
|
|
|
std::vector<uint8_t> sessionKey; // 40-byte session key from auth server
|
|
|
|
|
|
std::string accountName; // Account name
|
|
|
|
|
|
uint32_t build = 12340; // Client build (3.3.5a)
|
2026-02-13 01:51:49 -08:00
|
|
|
|
uint32_t realmId_ = 0; // Realm ID from auth REALM_LIST (used in WotLK AUTH_SESSION)
|
2026-02-02 12:24:50 -08:00
|
|
|
|
uint32_t clientSeed = 0; // Random seed generated by client
|
|
|
|
|
|
uint32_t serverSeed = 0; // Seed from SMSG_AUTH_CHALLENGE
|
|
|
|
|
|
|
|
|
|
|
|
// Characters
|
|
|
|
|
|
std::vector<Character> characters; // Character list from SMSG_CHAR_ENUM
|
|
|
|
|
|
|
|
|
|
|
|
// Movement
|
|
|
|
|
|
MovementInfo movementInfo; // Current player movement state
|
|
|
|
|
|
uint32_t movementTime = 0; // Movement timestamp counter
|
2026-02-20 02:19:17 -08:00
|
|
|
|
std::chrono::steady_clock::time_point movementClockStart_ = std::chrono::steady_clock::now();
|
|
|
|
|
|
uint32_t lastMovementTimestampMs_ = 0;
|
2026-02-18 23:30:38 -08:00
|
|
|
|
bool serverMovementAllowed_ = true;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
2026-03-10 12:36:56 -07:00
|
|
|
|
// Fall/jump tracking for movement packet correctness.
|
|
|
|
|
|
// fallTime must be the elapsed ms since the FALLING flag was set; the server
|
|
|
|
|
|
// uses it for fall-damage calculations and anti-cheat validation.
|
|
|
|
|
|
bool isFalling_ = false;
|
|
|
|
|
|
uint32_t fallStartMs_ = 0; // movementInfo.time value when FALLING started
|
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
// Inventory
|
|
|
|
|
|
Inventory inventory;
|
|
|
|
|
|
|
|
|
|
|
|
// Entity tracking
|
|
|
|
|
|
EntityManager entityManager; // Manages all entities in view
|
|
|
|
|
|
|
|
|
|
|
|
// Chat
|
2026-02-04 11:31:08 -08:00
|
|
|
|
std::deque<MessageChatData> chatHistory; // Recent chat messages
|
2026-02-02 12:24:50 -08:00
|
|
|
|
size_t maxChatHistory = 100; // Maximum chat messages to keep
|
2026-02-14 14:30:09 -08:00
|
|
|
|
std::vector<std::string> joinedChannels_; // Active channel memberships
|
|
|
|
|
|
ChatBubbleCallback chatBubbleCallback_;
|
2026-02-14 15:11:43 -08:00
|
|
|
|
EmoteAnimCallback emoteAnimCallback_;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
|
|
// Targeting
|
|
|
|
|
|
uint64_t targetGuid = 0;
|
2026-02-07 13:44:36 -08:00
|
|
|
|
uint64_t focusGuid = 0; // Focus target
|
|
|
|
|
|
uint64_t lastTargetGuid = 0; // Previous target
|
2026-02-02 12:24:50 -08:00
|
|
|
|
std::vector<uint64_t> tabCycleList;
|
|
|
|
|
|
int tabCycleIndex = -1;
|
|
|
|
|
|
bool tabCycleStale = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Heartbeat
|
|
|
|
|
|
uint32_t pingSequence = 0; // Ping sequence number (increments)
|
|
|
|
|
|
float timeSinceLastPing = 0.0f; // Time since last ping sent (seconds)
|
|
|
|
|
|
float pingInterval = 30.0f; // Ping interval (30 seconds)
|
2026-02-11 21:14:35 -08:00
|
|
|
|
float timeSinceLastMoveHeartbeat_ = 0.0f; // Periodic movement heartbeat to keep server position synced
|
|
|
|
|
|
float moveHeartbeatInterval_ = 0.5f;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
uint32_t lastLatency = 0; // Last measured latency (milliseconds)
|
2026-03-11 19:45:03 -07:00
|
|
|
|
std::chrono::steady_clock::time_point pingTimestamp_; // Time CMSG_PING was sent
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
|
// Player GUID and map
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
uint64_t playerGuid = 0;
|
2026-02-07 16:59:20 -08:00
|
|
|
|
uint32_t currentMapId_ = 0;
|
2026-02-08 03:39:02 -08:00
|
|
|
|
bool hasHomeBind_ = false;
|
|
|
|
|
|
uint32_t homeBindMapId_ = 0;
|
|
|
|
|
|
glm::vec3 homeBindPos_{0.0f};
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
|
|
|
|
|
// ---- Phase 1: Name caches ----
|
|
|
|
|
|
std::unordered_map<uint64_t, std::string> playerNameCache;
|
|
|
|
|
|
std::unordered_set<uint64_t> pendingNameQueries;
|
|
|
|
|
|
std::unordered_map<uint32_t, CreatureQueryResponseData> creatureInfoCache;
|
|
|
|
|
|
std::unordered_set<uint32_t> pendingCreatureQueries;
|
2026-02-08 00:59:40 -08:00
|
|
|
|
std::unordered_map<uint32_t, GameObjectQueryResponseData> gameObjectInfoCache_;
|
|
|
|
|
|
std::unordered_set<uint32_t> pendingGameObjectQueries_;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-03-10 05:46:03 -07:00
|
|
|
|
// ---- Friend/contact list cache ----
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
|
std::unordered_map<std::string, uint64_t> friendsCache; // name -> guid
|
2026-03-10 01:15:51 -07:00
|
|
|
|
std::unordered_set<uint64_t> friendGuids_; // all known friend GUIDs (for name backfill)
|
2026-02-18 23:30:38 -08:00
|
|
|
|
uint32_t lastContactListMask_ = 0;
|
|
|
|
|
|
uint32_t lastContactListCount_ = 0;
|
2026-03-10 05:46:03 -07:00
|
|
|
|
std::vector<ContactEntry> contacts_; // structured contact list (friends + ignores)
|
2026-02-18 23:30:38 -08:00
|
|
|
|
|
|
|
|
|
|
// ---- World state and faction initialization snapshots ----
|
|
|
|
|
|
uint32_t worldStateMapId_ = 0;
|
|
|
|
|
|
uint32_t worldStateZoneId_ = 0;
|
|
|
|
|
|
std::unordered_map<uint32_t, uint32_t> worldStates_;
|
|
|
|
|
|
std::vector<FactionStandingInit> initialFactions_;
|
Add /roll and friend management commands
Roll Command:
- Add /roll, /random, /rnd commands for random number generation
- Support multiple formats: /roll, /roll 100, /roll 1-100, /roll 10 50
- Broadcasts rolls to party/raid with "[Name] rolls X (min-max)" format
- Cap max roll at 10,000 to prevent abuse
- Use MSG_RANDOM_ROLL (0x1FB) bidirectional opcode
Friend Commands:
- Add /friend add <name>, /addfriend <name> to add friends
- Add /friend remove <name>, /removefriend <name> to remove friends
- Support aliases: /delfriend, /remfriend
- Maintain local friends cache mapping names to GUIDs for lookups
- Display status messages for all friend actions:
- Friend added/removed confirmations
- Friend online/offline notifications
- Error messages (not found, already friends, list full, ignoring)
Social Opcodes:
- Add CMSG_ADD_FRIEND (0x69) and SMSG_FRIEND_STATUS (0x68)
- Add CMSG_DEL_FRIEND (0x6A) for friend removal
- Add CMSG_SET_CONTACT_NOTES (0x6B) for friend notes (future use)
- Add CMSG_ADD_IGNORE (0x6C) and CMSG_DEL_IGNORE (0x6D) (future use)
Implementation:
- Add RandomRollPacket builder and RandomRollParser for roll data
- Add AddFriendPacket and DelFriendPacket builders
- Add FriendStatusParser to handle server friend status updates
- Add friendsCache map to store friend name-to-GUID mappings
- Add handleRandomRoll() and handleFriendStatus() packet handlers
- Comprehensive slash command parsing with multiple formats and aliases
2026-02-07 12:51:30 -08:00
|
|
|
|
|
2026-02-07 12:58:11 -08:00
|
|
|
|
// ---- Ignore list cache ----
|
|
|
|
|
|
std::unordered_map<std::string, uint64_t> ignoreCache; // name -> guid
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Logout state ----
|
|
|
|
|
|
bool loggingOut_ = false;
|
|
|
|
|
|
|
2026-02-07 13:03:21 -08:00
|
|
|
|
// ---- Display state ----
|
|
|
|
|
|
bool helmVisible_ = true;
|
|
|
|
|
|
bool cloakVisible_ = true;
|
Implement SMSG_STANDSTATE_UPDATE and SMSG_ITEM_PUSH_RESULT handlers
SMSG_STANDSTATE_UPDATE:
- Parse uint8 stand state from server confirmation packet
- Store in standState_ member (0=stand, 7=dead, 8=kneel, etc.)
- Expose getStandState(), isSitting(), isDead(), isKneeling() accessors
SMSG_ITEM_PUSH_RESULT:
- Parse full WotLK 3.3.5a payload: guid, received, created, showInChat,
bagSlot, itemSlot, itemId, suffixFactor, randomPropertyId, count, totalCount
- Show "Received: <name> x<count>" chat notification when showInChat=1
- Queue item info lookup via queryItemInfo so name resolves asap
2026-03-09 12:58:52 -07:00
|
|
|
|
uint8_t standState_ = 0; // 0=stand, 1=sit, ..., 7=dead, 8=kneel (server-confirmed)
|
2026-02-07 13:03:21 -08:00
|
|
|
|
|
|
|
|
|
|
// ---- Follow state ----
|
|
|
|
|
|
uint64_t followTargetGuid_ = 0;
|
|
|
|
|
|
|
2026-02-07 13:17:01 -08:00
|
|
|
|
// ---- AFK/DND status ----
|
|
|
|
|
|
bool afkStatus_ = false;
|
|
|
|
|
|
bool dndStatus_ = false;
|
|
|
|
|
|
std::string afkMessage_;
|
|
|
|
|
|
std::string dndMessage_;
|
|
|
|
|
|
std::string lastWhisperSender_;
|
|
|
|
|
|
|
2026-02-06 03:11:43 -08:00
|
|
|
|
// ---- Online item tracking ----
|
|
|
|
|
|
struct OnlineItemInfo {
|
|
|
|
|
|
uint32_t entry = 0;
|
|
|
|
|
|
uint32_t stackCount = 1;
|
2026-03-10 16:21:09 -07:00
|
|
|
|
uint32_t curDurability = 0;
|
|
|
|
|
|
uint32_t maxDurability = 0;
|
2026-02-06 03:11:43 -08:00
|
|
|
|
};
|
|
|
|
|
|
std::unordered_map<uint64_t, OnlineItemInfo> onlineItems_;
|
|
|
|
|
|
std::unordered_map<uint32_t, ItemQueryResponseData> itemInfoCache_;
|
|
|
|
|
|
std::unordered_set<uint32_t> pendingItemQueries_;
|
|
|
|
|
|
std::array<uint64_t, 23> equipSlotGuids_{};
|
|
|
|
|
|
std::array<uint64_t, 16> backpackSlotGuids_{};
|
2026-02-13 22:14:34 -08:00
|
|
|
|
// Container (bag) contents: containerGuid -> array of item GUIDs per slot
|
|
|
|
|
|
struct ContainerInfo {
|
|
|
|
|
|
uint32_t numSlots = 0;
|
|
|
|
|
|
std::array<uint64_t, 36> slotGuids{}; // max 36 slots
|
|
|
|
|
|
};
|
|
|
|
|
|
std::unordered_map<uint64_t, ContainerInfo> containerContents_;
|
2026-02-06 18:34:45 -08:00
|
|
|
|
int invSlotBase_ = -1;
|
|
|
|
|
|
int packSlotBase_ = -1;
|
|
|
|
|
|
std::map<uint16_t, uint32_t> lastPlayerFields_;
|
2026-02-06 03:13:42 -08:00
|
|
|
|
bool onlineEquipDirty_ = false;
|
2026-02-14 16:33:24 -08:00
|
|
|
|
std::array<uint32_t, 19> lastEquipDisplayIds_{};
|
2026-02-06 03:11:43 -08:00
|
|
|
|
|
2026-02-13 20:10:19 -08:00
|
|
|
|
// Visible equipment for other players: detect the update-field layout (base + stride)
|
|
|
|
|
|
// using the local player's own equipped items, then decode other players by index.
|
2026-02-14 15:43:09 -08:00
|
|
|
|
// Default to known WotLK 3.3.5a layout: UNIT_END(148) + 0x0088 = 284, stride 2.
|
|
|
|
|
|
// The heuristic in maybeDetectVisibleItemLayout() can still override if needed.
|
|
|
|
|
|
int visibleItemEntryBase_ = 284;
|
2026-02-13 20:10:19 -08:00
|
|
|
|
int visibleItemStride_ = 2;
|
2026-02-14 15:43:09 -08:00
|
|
|
|
bool visibleItemLayoutVerified_ = false; // true once heuristic confirms/overrides default
|
2026-02-13 20:10:19 -08:00
|
|
|
|
std::unordered_map<uint64_t, std::array<uint32_t, 19>> otherPlayerVisibleItemEntries_;
|
|
|
|
|
|
std::unordered_set<uint64_t> otherPlayerVisibleDirty_;
|
2026-02-13 20:19:33 -08:00
|
|
|
|
std::unordered_map<uint64_t, uint32_t> otherPlayerMoveTimeMs_;
|
2026-02-19 16:45:39 -08:00
|
|
|
|
std::unordered_map<uint64_t, float> otherPlayerSmoothedIntervalMs_; // EMA of packet intervals
|
2026-02-13 20:10:19 -08:00
|
|
|
|
|
2026-02-13 20:26:55 -08:00
|
|
|
|
// Inspect fallback (when visible item fields are missing/unreliable)
|
|
|
|
|
|
std::unordered_map<uint64_t, std::array<uint32_t, 19>> inspectedPlayerItemEntries_;
|
|
|
|
|
|
std::unordered_set<uint64_t> pendingAutoInspect_;
|
|
|
|
|
|
float inspectRateLimit_ = 0.0f;
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 2: Combat ----
|
|
|
|
|
|
bool autoAttacking = false;
|
2026-02-20 03:38:12 -08:00
|
|
|
|
bool autoAttackRequested_ = false; // local intent (CMSG_ATTACKSWING sent)
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
uint64_t autoAttackTarget = 0;
|
2026-02-06 18:34:45 -08:00
|
|
|
|
bool autoAttackOutOfRange_ = false;
|
2026-02-20 17:53:33 -08:00
|
|
|
|
float autoAttackOutOfRangeTime_ = 0.0f;
|
|
|
|
|
|
float autoAttackRangeWarnCooldown_ = 0.0f;
|
2026-02-17 15:37:02 -08:00
|
|
|
|
float autoAttackResendTimer_ = 0.0f; // Re-send CMSG_ATTACKSWING every ~1s while attacking
|
2026-02-20 03:14:48 -08:00
|
|
|
|
float autoAttackFacingSyncTimer_ = 0.0f; // Periodic facing sync while meleeing
|
2026-02-06 18:34:45 -08:00
|
|
|
|
std::unordered_set<uint64_t> hostileAttackers_;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
std::vector<CombatTextEntry> combatText;
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Phase 3: Spells ----
|
2026-02-05 21:28:21 -08:00
|
|
|
|
WorldEntryCallback worldEntryCallback_;
|
2026-03-10 12:28:11 -07:00
|
|
|
|
KnockBackCallback knockBackCallback_;
|
2026-02-07 16:59:20 -08:00
|
|
|
|
UnstuckCallback unstuckCallback_;
|
2026-02-08 03:24:12 -08:00
|
|
|
|
UnstuckCallback unstuckGyCallback_;
|
2026-03-07 22:03:28 -08:00
|
|
|
|
UnstuckCallback unstuckHearthCallback_;
|
2026-02-08 03:32:00 -08:00
|
|
|
|
BindPointCallback bindPointCallback_;
|
2026-03-09 21:57:42 -07:00
|
|
|
|
HearthstonePreloadCallback hearthstonePreloadCallback_;
|
2026-02-05 21:55:52 -08:00
|
|
|
|
CreatureSpawnCallback creatureSpawnCallback_;
|
|
|
|
|
|
CreatureDespawnCallback creatureDespawnCallback_;
|
2026-02-13 19:40:54 -08:00
|
|
|
|
PlayerSpawnCallback playerSpawnCallback_;
|
|
|
|
|
|
PlayerDespawnCallback playerDespawnCallback_;
|
2026-02-13 20:10:19 -08:00
|
|
|
|
PlayerEquipmentCallback playerEquipmentCallback_;
|
2026-02-06 13:47:03 -08:00
|
|
|
|
CreatureMoveCallback creatureMoveCallback_;
|
2026-02-08 00:59:40 -08:00
|
|
|
|
TransportMoveCallback transportMoveCallback_;
|
2026-02-11 00:54:38 -08:00
|
|
|
|
TransportSpawnCallback transportSpawnCallback_;
|
2026-02-07 19:44:03 -08:00
|
|
|
|
GameObjectSpawnCallback gameObjectSpawnCallback_;
|
2026-02-12 00:04:53 -08:00
|
|
|
|
GameObjectMoveCallback gameObjectMoveCallback_;
|
2026-02-07 19:44:03 -08:00
|
|
|
|
GameObjectDespawnCallback gameObjectDespawnCallback_;
|
2026-02-23 05:39:02 -08:00
|
|
|
|
GameObjectCustomAnimCallback gameObjectCustomAnimCallback_;
|
2026-02-08 00:59:40 -08:00
|
|
|
|
|
|
|
|
|
|
// Transport tracking
|
2026-02-12 00:04:53 -08:00
|
|
|
|
struct TransportAttachment {
|
|
|
|
|
|
ObjectType type = ObjectType::OBJECT;
|
|
|
|
|
|
uint64_t transportGuid = 0;
|
|
|
|
|
|
glm::vec3 localOffset{0.0f};
|
|
|
|
|
|
float localOrientation = 0.0f;
|
|
|
|
|
|
bool hasLocalOrientation = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
std::unordered_map<uint64_t, TransportAttachment> transportAttachments_;
|
2026-02-08 00:59:40 -08:00
|
|
|
|
std::unordered_set<uint64_t> transportGuids_; // GUIDs of known transport GameObjects
|
2026-02-12 00:04:53 -08:00
|
|
|
|
std::unordered_set<uint64_t> serverUpdatedTransportGuids_;
|
2026-02-08 00:59:40 -08:00
|
|
|
|
uint64_t playerTransportGuid_ = 0; // Transport the player is riding (0 = none)
|
|
|
|
|
|
glm::vec3 playerTransportOffset_ = glm::vec3(0.0f); // Player offset on transport
|
2026-02-12 00:45:24 -08:00
|
|
|
|
uint64_t playerTransportStickyGuid_ = 0; // Last transport player was on (temporary retention)
|
|
|
|
|
|
float playerTransportStickyTimer_ = 0.0f; // Seconds to keep sticky transport alive after transient clears
|
2026-02-10 21:29:10 -08:00
|
|
|
|
std::unique_ptr<TransportManager> transportManager_; // Transport movement manager
|
2026-02-17 15:13:54 -08:00
|
|
|
|
std::unordered_set<uint32_t> knownSpells;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
std::unordered_map<uint32_t, float> spellCooldowns; // spellId -> remaining seconds
|
2026-03-09 20:23:38 -07:00
|
|
|
|
uint32_t weaponProficiency_ = 0; // bitmask from SMSG_SET_PROFICIENCY itemClass=2
|
|
|
|
|
|
uint32_t armorProficiency_ = 0; // bitmask from SMSG_SET_PROFICIENCY itemClass=4
|
2026-03-09 20:36:20 -07:00
|
|
|
|
std::vector<MinimapPing> minimapPings_;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
uint8_t castCount = 0;
|
|
|
|
|
|
bool casting = false;
|
|
|
|
|
|
uint32_t currentCastSpellId = 0;
|
|
|
|
|
|
float castTimeRemaining = 0.0f;
|
2026-03-09 23:13:30 -07:00
|
|
|
|
// Per-unit cast state (keyed by GUID, populated from SMSG_SPELL_START)
|
|
|
|
|
|
std::unordered_map<uint64_t, UnitCastState> unitCastStates_;
|
2026-02-19 03:31:49 -08:00
|
|
|
|
uint64_t pendingGameObjectInteractGuid_ = 0;
|
Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks
DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))
UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation
Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
|
|
|
|
|
|
|
|
|
|
// Talents (dual-spec support)
|
|
|
|
|
|
uint8_t activeTalentSpec_ = 0; // Currently active spec (0 or 1)
|
|
|
|
|
|
uint8_t unspentTalentPoints_[2] = {0, 0}; // Unspent points per spec
|
|
|
|
|
|
std::unordered_map<uint32_t, uint8_t> learnedTalents_[2]; // Learned talents per spec
|
|
|
|
|
|
std::unordered_map<uint32_t, TalentEntry> talentCache_; // talentId -> entry
|
|
|
|
|
|
std::unordered_map<uint32_t, TalentTabEntry> talentTabCache_; // tabId -> entry
|
|
|
|
|
|
bool talentDbcLoaded_ = false;
|
2026-03-10 07:41:27 -07:00
|
|
|
|
bool talentsInitialized_ = false; // Reset on world entry; guards first-spec selection
|
2026-02-26 17:56:11 -08:00
|
|
|
|
|
|
|
|
|
|
// ---- Area trigger detection ----
|
|
|
|
|
|
struct AreaTriggerEntry {
|
|
|
|
|
|
uint32_t id = 0;
|
|
|
|
|
|
uint32_t mapId = 0;
|
|
|
|
|
|
float x = 0, y = 0, z = 0; // canonical WoW coords (converted from DBC)
|
|
|
|
|
|
float radius = 0;
|
|
|
|
|
|
float boxLength = 0, boxWidth = 0, boxHeight = 0;
|
|
|
|
|
|
float boxYaw = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
bool areaTriggerDbcLoaded_ = false;
|
|
|
|
|
|
std::vector<AreaTriggerEntry> areaTriggers_;
|
|
|
|
|
|
std::unordered_set<uint32_t> activeAreaTriggers_; // triggers player is currently inside
|
|
|
|
|
|
float areaTriggerCheckTimer_ = 0.0f;
|
2026-02-27 04:59:12 -08:00
|
|
|
|
bool areaTriggerSuppressFirst_ = false; // suppress first check after map transfer
|
2026-02-26 17:56:11 -08:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
float castTimeTotal = 0.0f;
|
2026-03-10 06:04:43 -07:00
|
|
|
|
std::array<ActionBarSlot, ACTION_BAR_SLOTS> actionBar{};
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
std::vector<AuraSlot> playerAuras;
|
|
|
|
|
|
std::vector<AuraSlot> targetAuras;
|
2026-02-26 10:41:29 -08:00
|
|
|
|
uint64_t petGuid_ = 0;
|
2026-03-09 22:53:09 -07:00
|
|
|
|
uint32_t petActionSlots_[10] = {}; // SMSG_PET_SPELLS action bar (10 slots)
|
|
|
|
|
|
uint8_t petCommand_ = 1; // 0=stay,1=follow,2=attack,3=dismiss
|
|
|
|
|
|
uint8_t petReact_ = 1; // 0=passive,1=defensive,2=aggressive
|
|
|
|
|
|
std::vector<uint32_t> petSpellList_; // known pet spells
|
|
|
|
|
|
std::unordered_set<uint32_t> petAutocastSpells_; // spells with autocast on
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-26 17:56:11 -08:00
|
|
|
|
// ---- Battleground queue state ----
|
|
|
|
|
|
std::array<BgQueueSlot, 3> bgQueues_{};
|
|
|
|
|
|
|
|
|
|
|
|
// Instance difficulty
|
|
|
|
|
|
uint32_t instanceDifficulty_ = 0;
|
|
|
|
|
|
bool instanceIsHeroic_ = false;
|
|
|
|
|
|
|
2026-03-10 06:10:29 -07:00
|
|
|
|
// Raid target markers (icon 0-7 -> guid; 0 = empty slot)
|
|
|
|
|
|
std::array<uint64_t, kRaidMarkCount> raidTargetGuids_ = {};
|
|
|
|
|
|
|
2026-03-09 14:30:48 -07:00
|
|
|
|
// Mirror timers (0=fatigue, 1=breath, 2=feigndeath)
|
|
|
|
|
|
MirrorTimer mirrorTimers_[3];
|
|
|
|
|
|
|
|
|
|
|
|
// Combo points (rogues/druids)
|
|
|
|
|
|
uint8_t comboPoints_ = 0;
|
|
|
|
|
|
uint64_t comboTarget_ = 0;
|
|
|
|
|
|
|
2026-03-09 13:36:23 -07:00
|
|
|
|
// Instance / raid lockouts
|
|
|
|
|
|
std::vector<InstanceLockout> instanceLockouts_;
|
|
|
|
|
|
|
2026-03-09 19:54:32 -07:00
|
|
|
|
// Instance encounter boss units (slots 0-4 from SMSG_UPDATE_INSTANCE_ENCOUNTER_UNIT)
|
|
|
|
|
|
std::array<uint64_t, kMaxEncounterSlots> encounterUnitGuids_ = {}; // 0 = empty slot
|
|
|
|
|
|
|
2026-03-09 13:30:23 -07:00
|
|
|
|
// LFG / Dungeon Finder state
|
|
|
|
|
|
LfgState lfgState_ = LfgState::None;
|
|
|
|
|
|
uint32_t lfgDungeonId_ = 0; // current dungeon entry
|
2026-03-09 13:47:07 -07:00
|
|
|
|
uint32_t lfgProposalId_ = 0; // pending proposal id (0 = none)
|
2026-03-09 13:30:23 -07:00
|
|
|
|
int32_t lfgAvgWaitSec_ = -1; // estimated wait, -1=unknown
|
|
|
|
|
|
uint32_t lfgTimeInQueueMs_= 0; // ms already in queue
|
|
|
|
|
|
|
2026-03-09 14:48:30 -07:00
|
|
|
|
// Ready check state
|
2026-03-09 20:38:44 -07:00
|
|
|
|
bool pendingReadyCheck_ = false;
|
|
|
|
|
|
uint32_t readyCheckReadyCount_ = 0;
|
|
|
|
|
|
uint32_t readyCheckNotReadyCount_ = 0;
|
2026-03-09 14:48:30 -07:00
|
|
|
|
std::string readyCheckInitiator_;
|
|
|
|
|
|
|
|
|
|
|
|
// Faction standings (factionId → absolute standing value)
|
|
|
|
|
|
std::unordered_map<uint32_t, int32_t> factionStandings_;
|
|
|
|
|
|
// Faction name cache (factionId → name), populated lazily from Faction.dbc
|
|
|
|
|
|
std::unordered_map<uint32_t, std::string> factionNameCache_;
|
|
|
|
|
|
bool factionNameCacheLoaded_ = false;
|
|
|
|
|
|
void loadFactionNameCache();
|
|
|
|
|
|
std::string getFactionName(uint32_t factionId) const;
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 4: Group ----
|
|
|
|
|
|
GroupListData partyData;
|
|
|
|
|
|
bool pendingGroupInvite = false;
|
|
|
|
|
|
std::string pendingInviterName;
|
|
|
|
|
|
|
2026-03-09 14:15:59 -07:00
|
|
|
|
// Item text state
|
|
|
|
|
|
bool itemTextOpen_ = false;
|
|
|
|
|
|
std::string itemText_;
|
|
|
|
|
|
|
2026-03-09 14:14:15 -07:00
|
|
|
|
// Shared quest state
|
|
|
|
|
|
bool pendingSharedQuest_ = false;
|
|
|
|
|
|
uint32_t sharedQuestId_ = 0;
|
|
|
|
|
|
std::string sharedQuestTitle_;
|
|
|
|
|
|
std::string sharedQuestSharerName_;
|
|
|
|
|
|
uint64_t sharedQuestSharerGuid_ = 0;
|
|
|
|
|
|
|
2026-03-09 14:07:50 -07:00
|
|
|
|
// Summon state
|
|
|
|
|
|
bool pendingSummonRequest_ = false;
|
|
|
|
|
|
uint64_t summonerGuid_ = 0;
|
|
|
|
|
|
std::string summonerName_;
|
|
|
|
|
|
float summonTimeoutSec_ = 0.0f;
|
|
|
|
|
|
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
// Trade state
|
|
|
|
|
|
TradeStatus tradeStatus_ = TradeStatus::None;
|
|
|
|
|
|
uint64_t tradePeerGuid_= 0;
|
|
|
|
|
|
std::string tradePeerName_;
|
2026-03-11 00:44:07 -07:00
|
|
|
|
std::array<TradeSlot, TRADE_SLOT_COUNT> myTradeSlots_{};
|
|
|
|
|
|
std::array<TradeSlot, TRADE_SLOT_COUNT> peerTradeSlots_{};
|
|
|
|
|
|
uint64_t myTradeGold_ = 0;
|
|
|
|
|
|
uint64_t peerTradeGold_ = 0;
|
Implement basic trade request/accept/decline flow
- Parse SMSG_TRADE_STATUS for all 20+ status codes: incoming request,
open/cancel/complete/accept notifications, error conditions (too far,
wrong faction, stunned, dead, trial account, etc.)
- SMSG_TRADE_STATUS_EXTENDED consumed via shared handler (no full item
window yet; state tracking sufficient for accept/decline flow)
- Add acceptTradeRequest() (CMSG_BEGIN_TRADE), declineTradeRequest(),
acceptTrade() (CMSG_ACCEPT_TRADE), cancelTrade() (CMSG_CANCEL_TRADE)
- Add BeginTradePacket, CancelTradePacket, AcceptTradePacket builders
- Add renderTradeRequestPopup(): shows "X wants to trade" with
Accept/Decline buttons when tradeStatus_ == PendingIncoming
- TradeStatus enum tracks None/PendingIncoming/Open/Accepted/Complete
2026-03-09 14:05:42 -07:00
|
|
|
|
|
2026-03-09 13:58:02 -07:00
|
|
|
|
// Duel state
|
|
|
|
|
|
bool pendingDuelRequest_ = false;
|
|
|
|
|
|
uint64_t duelChallengerGuid_= 0;
|
|
|
|
|
|
uint64_t duelFlagGuid_ = 0;
|
|
|
|
|
|
std::string duelChallengerName_;
|
|
|
|
|
|
|
2026-02-13 21:39:48 -08:00
|
|
|
|
// ---- Guild state ----
|
|
|
|
|
|
std::string guildName_;
|
|
|
|
|
|
std::vector<std::string> guildRankNames_;
|
|
|
|
|
|
GuildRosterData guildRoster_;
|
2026-02-25 14:44:44 -08:00
|
|
|
|
GuildInfoData guildInfoData_;
|
|
|
|
|
|
GuildQueryResponseData guildQueryData_;
|
2026-02-13 21:39:48 -08:00
|
|
|
|
bool hasGuildRoster_ = false;
|
|
|
|
|
|
bool pendingGuildInvite_ = false;
|
|
|
|
|
|
std::string pendingGuildInviterName_;
|
|
|
|
|
|
std::string pendingGuildInviteGuildName_;
|
2026-02-25 14:44:44 -08:00
|
|
|
|
bool showPetitionDialog_ = false;
|
|
|
|
|
|
uint32_t petitionCost_ = 0;
|
|
|
|
|
|
uint64_t petitionNpcGuid_ = 0;
|
2026-02-13 21:39:48 -08:00
|
|
|
|
|
2026-02-05 14:35:12 -08:00
|
|
|
|
uint64_t activeCharacterGuid_ = 0;
|
2026-02-08 03:05:38 -08:00
|
|
|
|
Race playerRace_ = Race::HUMAN;
|
2026-02-05 14:35:12 -08:00
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// ---- Phase 5: Loot ----
|
|
|
|
|
|
bool lootWindowOpen = false;
|
2026-02-17 16:31:00 -08:00
|
|
|
|
bool autoLoot_ = false;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
LootResponseData currentLoot;
|
2026-03-09 14:01:27 -07:00
|
|
|
|
|
|
|
|
|
|
// Group loot roll state
|
|
|
|
|
|
bool pendingLootRollActive_ = false;
|
|
|
|
|
|
LootRollEntry pendingLootRoll_;
|
2026-02-05 14:01:26 -08:00
|
|
|
|
struct LocalLootState {
|
|
|
|
|
|
LootResponseData data;
|
|
|
|
|
|
bool moneyTaken = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
std::unordered_map<uint64_t, LocalLootState> localLootState_;
|
2026-02-18 04:13:26 -08:00
|
|
|
|
struct PendingLootRetry {
|
|
|
|
|
|
uint64_t guid = 0;
|
|
|
|
|
|
float timer = 0.0f;
|
|
|
|
|
|
uint8_t remainingRetries = 0;
|
2026-02-20 19:51:04 -08:00
|
|
|
|
bool sendLoot = false;
|
2026-02-18 04:13:26 -08:00
|
|
|
|
};
|
|
|
|
|
|
std::vector<PendingLootRetry> pendingGameObjectLootRetries_;
|
2026-02-20 19:51:04 -08:00
|
|
|
|
struct PendingLootOpen {
|
|
|
|
|
|
uint64_t guid = 0;
|
|
|
|
|
|
float timer = 0.0f;
|
|
|
|
|
|
};
|
|
|
|
|
|
std::vector<PendingLootOpen> pendingGameObjectLootOpens_;
|
2026-02-18 04:11:00 -08:00
|
|
|
|
uint64_t pendingLootMoneyGuid_ = 0;
|
|
|
|
|
|
uint32_t pendingLootMoneyAmount_ = 0;
|
|
|
|
|
|
float pendingLootMoneyNotifyTimer_ = 0.0f;
|
|
|
|
|
|
std::unordered_map<uint64_t, float> recentLootMoneyAnnounceCooldowns_;
|
2026-02-05 14:01:26 -08:00
|
|
|
|
uint64_t playerMoneyCopper_ = 0;
|
2026-02-19 17:45:09 -08:00
|
|
|
|
int32_t playerArmorRating_ = 0;
|
2026-03-10 23:08:15 -07:00
|
|
|
|
// Server-authoritative primary stats: [0]=STR [1]=AGI [2]=STA [3]=INT [4]=SPI; -1 = not received yet
|
|
|
|
|
|
int32_t playerStats_[5] = {-1, -1, -1, -1, -1};
|
2026-02-13 19:47:49 -08:00
|
|
|
|
// Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating
|
|
|
|
|
|
// money-notify deltas with update-field diffs and then overriding UF::PLAYER_FIELD_COINAGE at runtime.
|
|
|
|
|
|
uint32_t pendingMoneyDelta_ = 0;
|
|
|
|
|
|
float pendingMoneyDeltaTimer_ = 0.0f;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
|
|
|
|
|
// Gossip
|
|
|
|
|
|
bool gossipWindowOpen = false;
|
|
|
|
|
|
GossipMessageData currentGossip;
|
2026-03-09 14:38:45 -07:00
|
|
|
|
std::vector<GossipPoi> gossipPois_;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-19 03:31:49 -08:00
|
|
|
|
void performGameObjectInteractionNow(uint64_t guid);
|
|
|
|
|
|
|
2026-02-06 11:59:51 -08:00
|
|
|
|
// Quest details
|
|
|
|
|
|
bool questDetailsOpen = false;
|
2026-03-11 17:27:23 -07:00
|
|
|
|
std::chrono::steady_clock::time_point questDetailsOpenTime{}; // Delayed opening to allow item data to load
|
2026-02-06 11:59:51 -08:00
|
|
|
|
QuestDetailsData currentQuestDetails;
|
|
|
|
|
|
|
2026-02-06 21:50:15 -08:00
|
|
|
|
// Quest turn-in
|
|
|
|
|
|
bool questRequestItemsOpen_ = false;
|
|
|
|
|
|
QuestRequestItemsData currentQuestRequestItems_;
|
2026-02-19 01:12:14 -08:00
|
|
|
|
uint32_t pendingTurnInQuestId_ = 0;
|
|
|
|
|
|
uint64_t pendingTurnInNpcGuid_ = 0;
|
|
|
|
|
|
bool pendingTurnInRewardRequest_ = false;
|
2026-02-20 17:14:13 -08:00
|
|
|
|
std::unordered_map<uint32_t, float> pendingQuestAcceptTimeouts_;
|
|
|
|
|
|
std::unordered_map<uint32_t, uint64_t> pendingQuestAcceptNpcGuids_;
|
2026-02-06 21:50:15 -08:00
|
|
|
|
bool questOfferRewardOpen_ = false;
|
|
|
|
|
|
QuestOfferRewardData currentQuestOfferReward_;
|
|
|
|
|
|
|
2026-02-06 13:47:03 -08:00
|
|
|
|
// Quest log
|
|
|
|
|
|
std::vector<QuestLogEntry> questLog_;
|
2026-02-19 00:30:21 -08:00
|
|
|
|
std::unordered_set<uint32_t> pendingQuestQueryIds_;
|
2026-03-10 05:18:45 -07:00
|
|
|
|
std::unordered_set<uint32_t> trackedQuestIds_;
|
2026-02-20 17:14:13 -08:00
|
|
|
|
bool pendingLoginQuestResync_ = false;
|
|
|
|
|
|
float pendingLoginQuestResyncTimeout_ = 0.0f;
|
2026-02-06 13:47:03 -08:00
|
|
|
|
|
2026-02-06 20:10:10 -08:00
|
|
|
|
// Quest giver status per NPC
|
|
|
|
|
|
std::unordered_map<uint64_t, QuestGiverStatus> npcQuestStatus_;
|
|
|
|
|
|
|
2026-02-06 14:24:38 -08:00
|
|
|
|
// Faction hostility lookup (populated from FactionTemplate.dbc)
|
|
|
|
|
|
std::unordered_map<uint32_t, bool> factionHostileMap_;
|
|
|
|
|
|
bool isHostileFaction(uint32_t factionTemplateId) const {
|
|
|
|
|
|
auto it = factionHostileMap_.find(factionTemplateId);
|
|
|
|
|
|
return it != factionHostileMap_.end() ? it->second : true; // default hostile if unknown
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-07 16:59:20 -08:00
|
|
|
|
// Taxi / Flight Paths
|
2026-03-09 19:30:18 -07:00
|
|
|
|
std::unordered_map<uint64_t, bool> taxiNpcHasRoutes_; // guid -> has new/available routes
|
2026-02-07 16:59:20 -08:00
|
|
|
|
std::unordered_map<uint32_t, TaxiNode> taxiNodes_;
|
|
|
|
|
|
std::vector<TaxiPathEdge> taxiPathEdges_;
|
2026-02-08 21:32:38 -08:00
|
|
|
|
std::unordered_map<uint32_t, std::vector<TaxiPathNode>> taxiPathNodes_; // pathId -> ordered waypoints
|
2026-02-07 16:59:20 -08:00
|
|
|
|
bool taxiDbcLoaded_ = false;
|
|
|
|
|
|
bool taxiWindowOpen_ = false;
|
|
|
|
|
|
ShowTaxiNodesData currentTaxiData_;
|
|
|
|
|
|
uint64_t taxiNpcGuid_ = 0;
|
|
|
|
|
|
bool onTaxiFlight_ = false;
|
2026-02-08 03:05:38 -08:00
|
|
|
|
bool taxiMountActive_ = false;
|
|
|
|
|
|
uint32_t taxiMountDisplayId_ = 0;
|
|
|
|
|
|
bool taxiActivatePending_ = false;
|
|
|
|
|
|
float taxiActivateTimer_ = 0.0f;
|
|
|
|
|
|
bool taxiClientActive_ = false;
|
2026-02-08 23:15:26 -08:00
|
|
|
|
float taxiLandingCooldown_ = 0.0f; // Prevent re-entering taxi right after landing
|
2026-02-11 21:14:35 -08:00
|
|
|
|
float taxiStartGrace_ = 0.0f; // Ignore transient landing/dismount checks right after takeoff
|
2026-02-08 03:05:38 -08:00
|
|
|
|
size_t taxiClientIndex_ = 0;
|
|
|
|
|
|
std::vector<glm::vec3> taxiClientPath_;
|
2026-02-11 18:25:04 -08:00
|
|
|
|
float taxiClientSpeed_ = 32.0f;
|
2026-02-08 03:05:38 -08:00
|
|
|
|
float taxiClientSegmentProgress_ = 0.0f;
|
|
|
|
|
|
bool taxiRecoverPending_ = false;
|
|
|
|
|
|
uint32_t taxiRecoverMapId_ = 0;
|
|
|
|
|
|
glm::vec3 taxiRecoverPos_{0.0f};
|
2026-02-07 17:59:40 -08:00
|
|
|
|
uint32_t knownTaxiMask_[12] = {}; // Track previously known nodes for discovery alerts
|
|
|
|
|
|
bool taxiMaskInitialized_ = false; // First SMSG_SHOWTAXINODES seeds mask without alerts
|
2026-02-07 19:04:15 -08:00
|
|
|
|
std::unordered_map<uint32_t, uint32_t> taxiCostMap_; // destNodeId -> total cost in copper
|
|
|
|
|
|
void buildTaxiCostMap();
|
2026-02-08 03:05:38 -08:00
|
|
|
|
void applyTaxiMountForCurrentNode();
|
2026-02-20 02:19:17 -08:00
|
|
|
|
uint32_t nextMovementTimestampMs();
|
2026-02-11 22:27:02 -08:00
|
|
|
|
void sanitizeMovementForTaxi();
|
2026-02-08 03:05:38 -08:00
|
|
|
|
void startClientTaxiPath(const std::vector<uint32_t>& pathNodes);
|
|
|
|
|
|
void updateClientTaxi(float deltaTime);
|
2026-02-07 16:59:20 -08:00
|
|
|
|
|
2026-02-15 14:00:41 -08:00
|
|
|
|
// Mail
|
|
|
|
|
|
bool mailboxOpen_ = false;
|
|
|
|
|
|
uint64_t mailboxGuid_ = 0;
|
|
|
|
|
|
std::vector<MailMessage> mailInbox_;
|
|
|
|
|
|
int selectedMailIndex_ = -1;
|
|
|
|
|
|
bool showMailCompose_ = false;
|
2026-02-16 18:46:44 -08:00
|
|
|
|
bool hasNewMail_ = false;
|
2026-02-25 14:11:09 -08:00
|
|
|
|
std::array<MailAttachSlot, MAIL_MAX_ATTACHMENTS> mailAttachments_{};
|
2026-02-15 14:00:41 -08:00
|
|
|
|
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
|
// Bank
|
|
|
|
|
|
bool bankOpen_ = false;
|
|
|
|
|
|
uint64_t bankerGuid_ = 0;
|
|
|
|
|
|
std::array<uint64_t, 28> bankSlotGuids_{};
|
|
|
|
|
|
std::array<uint64_t, 7> bankBagSlotGuids_{};
|
2026-02-26 11:12:34 -08:00
|
|
|
|
int effectiveBankSlots_ = 28; // 24 for Classic, 28 for TBC/WotLK
|
|
|
|
|
|
int effectiveBankBagSlots_ = 7; // 6 for Classic, 7 for TBC/WotLK
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
|
|
|
|
|
|
|
// Guild Bank
|
|
|
|
|
|
bool guildBankOpen_ = false;
|
|
|
|
|
|
uint64_t guildBankerGuid_ = 0;
|
|
|
|
|
|
GuildBankData guildBankData_;
|
|
|
|
|
|
uint8_t guildBankActiveTab_ = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Auction House
|
|
|
|
|
|
bool auctionOpen_ = false;
|
|
|
|
|
|
uint64_t auctioneerGuid_ = 0;
|
|
|
|
|
|
uint32_t auctionHouseId_ = 0;
|
|
|
|
|
|
AuctionListResult auctionBrowseResults_;
|
|
|
|
|
|
AuctionListResult auctionOwnerResults_;
|
|
|
|
|
|
AuctionListResult auctionBidderResults_;
|
|
|
|
|
|
int auctionActiveTab_ = 0; // 0=Browse, 1=Bids, 2=Auctions
|
|
|
|
|
|
float auctionSearchDelayTimer_ = 0.0f;
|
2026-02-25 14:44:44 -08:00
|
|
|
|
// Last search params for re-query (pagination, auto-refresh after bid/buyout)
|
|
|
|
|
|
struct AuctionSearchParams {
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
uint8_t levelMin = 0, levelMax = 0;
|
|
|
|
|
|
uint32_t quality = 0xFFFFFFFF;
|
|
|
|
|
|
uint32_t itemClass = 0xFFFFFFFF;
|
|
|
|
|
|
uint32_t itemSubClass = 0xFFFFFFFF;
|
|
|
|
|
|
uint32_t invTypeMask = 0;
|
|
|
|
|
|
uint8_t usableOnly = 0;
|
|
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
AuctionSearchParams lastAuctionSearch_;
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
|
// Routing: which result vector to populate from next SMSG_AUCTION_LIST_RESULT
|
|
|
|
|
|
enum class AuctionResultTarget { BROWSE, OWNER, BIDDER };
|
|
|
|
|
|
AuctionResultTarget pendingAuctionTarget_ = AuctionResultTarget::BROWSE;
|
|
|
|
|
|
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
// Vendor
|
|
|
|
|
|
bool vendorWindowOpen = false;
|
|
|
|
|
|
ListInventoryData currentVendorItems;
|
2026-02-19 05:28:13 -08:00
|
|
|
|
std::deque<BuybackItem> buybackItems_;
|
|
|
|
|
|
std::unordered_map<uint64_t, BuybackItem> pendingSellToBuyback_;
|
|
|
|
|
|
int pendingBuybackSlot_ = -1;
|
2026-02-19 05:48:40 -08:00
|
|
|
|
uint32_t pendingBuybackWireSlot_ = 0;
|
2026-02-19 05:28:13 -08:00
|
|
|
|
uint32_t pendingBuyItemId_ = 0;
|
|
|
|
|
|
uint32_t pendingBuyItemSlot_ = 0;
|
Add gameplay systems: combat, spells, groups, loot, vendors, and UI
Implement ~70 new protocol opcodes across 5 phases while maintaining
full 3.3.5a private server compatibility:
- Phase 1: Server-aware targeting (CMSG_SET_SELECTION), player/creature
name queries, CMSG_SET_ACTIVE_MOVER after login
- Phase 2: Auto-attack, melee/spell damage parsing, health/mana/power
tracking from UPDATE_OBJECT fields, floating combat text
- Phase 3: Spell casting, action bar (12 slots, keys 1-=), cast bar,
cooldown tracking, aura/buff system with cancellation
- Phase 4: Group invite/accept/decline/leave, party frames UI,
/invite chat command
- Phase 5: Loot window, NPC gossip dialog, vendor buy/sell interface
Also: disable debug HUD/panels by default, gate 3D rendering to
IN_GAME state only, fix window resize not updating UI positions.
2026-02-04 10:30:52 -08:00
|
|
|
|
|
2026-02-08 14:33:39 -08:00
|
|
|
|
// Trainer
|
|
|
|
|
|
bool trainerWindowOpen_ = false;
|
|
|
|
|
|
TrainerListData currentTrainerList_;
|
2026-03-09 19:24:09 -07:00
|
|
|
|
struct SpellNameEntry { std::string name; std::string rank; uint32_t schoolMask = 0; };
|
2026-02-08 14:33:39 -08:00
|
|
|
|
std::unordered_map<uint32_t, SpellNameEntry> spellNameCache_;
|
|
|
|
|
|
bool spellNameCacheLoaded_ = false;
|
2026-03-09 19:34:33 -07:00
|
|
|
|
|
|
|
|
|
|
// Achievement name cache (lazy-loaded from Achievement.dbc on first earned event)
|
|
|
|
|
|
std::unordered_map<uint32_t, std::string> achievementNameCache_;
|
|
|
|
|
|
bool achievementNameCacheLoaded_ = false;
|
|
|
|
|
|
void loadAchievementNameCache();
|
2026-03-10 20:53:21 -07:00
|
|
|
|
// Set of achievement IDs earned by the player (populated from SMSG_ALL_ACHIEVEMENT_DATA)
|
|
|
|
|
|
std::unordered_set<uint32_t> earnedAchievements_;
|
|
|
|
|
|
void handleAllAchievementData(network::Packet& packet);
|
2026-03-10 08:06:21 -07:00
|
|
|
|
|
|
|
|
|
|
// Area name cache (lazy-loaded from WorldMapArea.dbc; maps AreaTable ID → display name)
|
|
|
|
|
|
std::unordered_map<uint32_t, std::string> areaNameCache_;
|
|
|
|
|
|
bool areaNameCacheLoaded_ = false;
|
|
|
|
|
|
void loadAreaNameCache();
|
|
|
|
|
|
std::string getAreaName(uint32_t areaId) const;
|
2026-02-08 14:46:01 -08:00
|
|
|
|
std::vector<TrainerTab> trainerTabs_;
|
2026-02-08 14:33:39 -08:00
|
|
|
|
void handleTrainerList(network::Packet& packet);
|
|
|
|
|
|
void loadSpellNameCache();
|
2026-02-08 14:46:01 -08:00
|
|
|
|
void categorizeTrainerSpells();
|
2026-02-08 14:33:39 -08:00
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
|
// Callbacks
|
|
|
|
|
|
WorldConnectSuccessCallback onSuccess;
|
|
|
|
|
|
WorldConnectFailureCallback onFailure;
|
2026-02-05 14:13:48 -08:00
|
|
|
|
CharCreateCallback charCreateCallback_;
|
2026-02-06 03:24:46 -08:00
|
|
|
|
CharDeleteCallback charDeleteCallback_;
|
2026-02-17 13:59:29 -08:00
|
|
|
|
CharLoginFailCallback charLoginFailCallback_;
|
2026-02-06 18:34:45 -08:00
|
|
|
|
uint8_t lastCharDeleteResult_ = 0xFF;
|
2026-02-05 14:18:41 -08:00
|
|
|
|
bool pendingCharCreateResult_ = false;
|
|
|
|
|
|
bool pendingCharCreateSuccess_ = false;
|
|
|
|
|
|
std::string pendingCharCreateMsg_;
|
2026-02-12 02:09:15 -08:00
|
|
|
|
bool requiresWarden_ = false;
|
|
|
|
|
|
bool wardenGateSeen_ = false;
|
|
|
|
|
|
float wardenGateElapsed_ = 0.0f;
|
|
|
|
|
|
float wardenGateNextStatusLog_ = 2.0f;
|
|
|
|
|
|
uint32_t wardenPacketsAfterGate_ = 0;
|
|
|
|
|
|
bool wardenCharEnumBlockedLogged_ = false;
|
|
|
|
|
|
std::unique_ptr<WardenCrypto> wardenCrypto_;
|
2026-02-14 02:00:15 -08:00
|
|
|
|
std::unique_ptr<WardenMemory> wardenMemory_;
|
2026-02-12 02:43:20 -08:00
|
|
|
|
std::unique_ptr<WardenModuleManager> wardenModuleManager_;
|
2026-02-05 12:01:03 -08:00
|
|
|
|
|
2026-02-13 16:53:28 -08:00
|
|
|
|
// Warden module download state
|
|
|
|
|
|
enum class WardenState {
|
|
|
|
|
|
WAIT_MODULE_USE, // Waiting for first SMSG (MODULE_USE)
|
|
|
|
|
|
WAIT_MODULE_CACHE, // Sent MODULE_MISSING, receiving module chunks
|
|
|
|
|
|
WAIT_HASH_REQUEST, // Module received, waiting for HASH_REQUEST
|
|
|
|
|
|
WAIT_CHECKS, // Hash sent, waiting for check requests
|
|
|
|
|
|
};
|
|
|
|
|
|
WardenState wardenState_ = WardenState::WAIT_MODULE_USE;
|
|
|
|
|
|
std::vector<uint8_t> wardenModuleHash_; // 16 bytes MD5
|
|
|
|
|
|
std::vector<uint8_t> wardenModuleKey_; // 16 bytes RC4
|
|
|
|
|
|
uint32_t wardenModuleSize_ = 0;
|
|
|
|
|
|
std::vector<uint8_t> wardenModuleData_; // Downloaded module chunks
|
2026-02-13 18:59:09 -08:00
|
|
|
|
std::vector<uint8_t> wardenLoadedModuleImage_; // Parsed module image for key derivation
|
2026-02-14 19:20:32 -08:00
|
|
|
|
std::shared_ptr<WardenModule> wardenLoadedModule_; // Loaded Warden module
|
2026-02-13 18:59:09 -08:00
|
|
|
|
|
|
|
|
|
|
// Pre-computed challenge/response entries from .cr file
|
|
|
|
|
|
struct WardenCREntry {
|
|
|
|
|
|
uint8_t seed[16];
|
|
|
|
|
|
uint8_t reply[20];
|
|
|
|
|
|
uint8_t clientKey[16]; // Encrypt key (client→server)
|
|
|
|
|
|
uint8_t serverKey[16]; // Decrypt key (server→client)
|
|
|
|
|
|
};
|
|
|
|
|
|
std::vector<WardenCREntry> wardenCREntries_;
|
|
|
|
|
|
// Module-specific check type opcodes [9]: MEM, PAGE_A, PAGE_B, MPQ, LUA, DRIVER, TIMING, PROC, MODULE
|
|
|
|
|
|
uint8_t wardenCheckOpcodes_[9] = {};
|
|
|
|
|
|
bool loadWardenCRFile(const std::string& moduleHashHex);
|
2026-02-13 16:53:28 -08:00
|
|
|
|
|
2026-02-05 12:07:58 -08:00
|
|
|
|
// ---- XP tracking ----
|
|
|
|
|
|
uint32_t playerXp_ = 0;
|
|
|
|
|
|
uint32_t playerNextLevelXp_ = 0;
|
2026-03-10 07:35:30 -07:00
|
|
|
|
uint32_t playerRestedXp_ = 0;
|
|
|
|
|
|
bool isResting_ = false;
|
2026-02-05 12:07:58 -08:00
|
|
|
|
uint32_t serverPlayerLevel_ = 1;
|
|
|
|
|
|
static uint32_t xpForLevel(uint32_t level);
|
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
|
// ---- Server time tracking (for deterministic celestial/sky systems) ----
|
|
|
|
|
|
float gameTime_ = 0.0f; // Server game time in seconds
|
|
|
|
|
|
float timeSpeed_ = 0.0166f; // Time scale (default: 1 game day = 1 real hour)
|
|
|
|
|
|
void handleLoginSetTimeSpeed(network::Packet& packet);
|
|
|
|
|
|
|
2026-02-17 17:59:41 -08:00
|
|
|
|
// ---- Weather state (SMSG_WEATHER) ----
|
|
|
|
|
|
uint32_t weatherType_ = 0; // 0=clear, 1=rain, 2=snow, 3=storm
|
|
|
|
|
|
float weatherIntensity_ = 0.0f; // 0.0 to 1.0
|
|
|
|
|
|
|
2026-03-09 14:57:46 -07:00
|
|
|
|
// ---- Light override (SMSG_OVERRIDE_LIGHT) ----
|
|
|
|
|
|
uint32_t overrideLightId_ = 0; // 0 = no override
|
|
|
|
|
|
uint32_t overrideLightTransMs_ = 0;
|
|
|
|
|
|
|
2026-02-07 14:21:50 -08:00
|
|
|
|
// ---- Player skills ----
|
|
|
|
|
|
std::map<uint32_t, PlayerSkill> playerSkills_;
|
|
|
|
|
|
std::unordered_map<uint32_t, std::string> skillLineNames_;
|
|
|
|
|
|
std::unordered_map<uint32_t, uint32_t> skillLineCategories_;
|
2026-02-08 14:46:01 -08:00
|
|
|
|
std::unordered_map<uint32_t, uint32_t> spellToSkillLine_; // spellID -> skillLineID
|
2026-02-07 14:21:50 -08:00
|
|
|
|
bool skillLineDbcLoaded_ = false;
|
2026-02-08 14:46:01 -08:00
|
|
|
|
bool skillLineAbilityLoaded_ = false;
|
2026-02-11 18:25:04 -08:00
|
|
|
|
static constexpr size_t PLAYER_EXPLORED_ZONES_COUNT = 128;
|
|
|
|
|
|
std::vector<uint32_t> playerExploredZones_ =
|
|
|
|
|
|
std::vector<uint32_t>(PLAYER_EXPLORED_ZONES_COUNT, 0u);
|
|
|
|
|
|
bool hasPlayerExploredZones_ = false;
|
2026-02-07 14:21:50 -08:00
|
|
|
|
void loadSkillLineDbc();
|
2026-02-08 14:46:01 -08:00
|
|
|
|
void loadSkillLineAbilityDbc();
|
2026-02-07 14:21:50 -08:00
|
|
|
|
void extractSkillFields(const std::map<uint16_t, uint32_t>& fields);
|
2026-02-11 18:25:04 -08:00
|
|
|
|
void extractExploredZoneFields(const std::map<uint16_t, uint32_t>& fields);
|
2026-03-10 23:33:38 -07:00
|
|
|
|
void applyQuestStateFromFields(const std::map<uint16_t, uint32_t>& fields);
|
2026-03-10 23:52:18 -07:00
|
|
|
|
// Apply packed kill counts from player update fields to a quest entry that has
|
|
|
|
|
|
// already had its killObjectives populated from SMSG_QUEST_QUERY_RESPONSE.
|
|
|
|
|
|
void applyPackedKillCountsFromFields(QuestLogEntry& quest);
|
2026-02-07 14:21:50 -08:00
|
|
|
|
|
2026-02-07 00:00:06 -08:00
|
|
|
|
NpcDeathCallback npcDeathCallback_;
|
Add comprehensive NPC voice system with interaction and combat sounds
Implements full NPC voice interaction system supporting 6 different sound categories
for all playable races/genders. System loads ~450+ voice clips from MPQ archives.
Voice Categories:
- Greeting: Play on NPC right-click interaction
- Farewell: Play when closing gossip/dialog windows
- Vendor: Play when opening merchant/vendor windows
- Pissed: Play after clicking NPC 5+ times (spam protection)
- Aggro: Play when NPC enters combat with player
- Flee: Play when NPC is fleeing (ready for low-health triggers)
Features:
- Race/gender detection from NPC display IDs via CreatureDisplayInfoExtra.dbc
- Intelligent click tracking for pissed sounds
- Combat sounds use player character vocal files for humanoid NPCs
- Cooldown system prevents voice spam (2s default, combat sounds bypass)
- Generic fallback voices for unsupported NPC types
- 3D positional audio support
Voice Support:
- All playable races: Human, Dwarf, Gnome, Night Elf, Orc, Tauren, Troll, Undead
- Male and female variants for each race
- StandardNPC sounds for social interactions
- Character vocal sounds for combat
Technical Changes:
- Refactored NpcVoiceManager to support multiple sound categories
- Added callbacks: NpcFarewell, NpcVendor, NpcAggro
- Extended voice loading to parse both StandardNPC and Character vocal paths
- Integrated with GameHandler for gossip, vendor, and combat events
- Added detailed voice detection logging for debugging
Also includes:
- Sound manifest files added to docs/ for reference
- Blacksmith hammer pitch increased to 1.6x (was 1.4x)
- Blacksmith volume reduced 30% to 0.25 (was 0.35)
2026-02-09 16:03:51 -08:00
|
|
|
|
NpcAggroCallback npcAggroCallback_;
|
2026-02-07 00:00:06 -08:00
|
|
|
|
NpcRespawnCallback npcRespawnCallback_;
|
2026-03-10 09:46:46 -07:00
|
|
|
|
StandStateCallback standStateCallback_;
|
2026-03-10 09:57:24 -07:00
|
|
|
|
GhostStateCallback ghostStateCallback_;
|
2026-02-05 14:01:26 -08:00
|
|
|
|
MeleeSwingCallback meleeSwingCallback_;
|
2026-03-10 09:42:17 -07:00
|
|
|
|
SpellCastAnimCallback spellCastAnimCallback_;
|
2026-03-10 10:30:50 -07:00
|
|
|
|
UnitAnimHintCallback unitAnimHintCallback_;
|
2026-03-10 10:55:23 -07:00
|
|
|
|
UnitMoveFlagsCallback unitMoveFlagsCallback_;
|
2026-02-07 00:00:06 -08:00
|
|
|
|
NpcSwingCallback npcSwingCallback_;
|
2026-02-09 01:29:44 -08:00
|
|
|
|
NpcGreetingCallback npcGreetingCallback_;
|
Add comprehensive NPC voice system with interaction and combat sounds
Implements full NPC voice interaction system supporting 6 different sound categories
for all playable races/genders. System loads ~450+ voice clips from MPQ archives.
Voice Categories:
- Greeting: Play on NPC right-click interaction
- Farewell: Play when closing gossip/dialog windows
- Vendor: Play when opening merchant/vendor windows
- Pissed: Play after clicking NPC 5+ times (spam protection)
- Aggro: Play when NPC enters combat with player
- Flee: Play when NPC is fleeing (ready for low-health triggers)
Features:
- Race/gender detection from NPC display IDs via CreatureDisplayInfoExtra.dbc
- Intelligent click tracking for pissed sounds
- Combat sounds use player character vocal files for humanoid NPCs
- Cooldown system prevents voice spam (2s default, combat sounds bypass)
- Generic fallback voices for unsupported NPC types
- 3D positional audio support
Voice Support:
- All playable races: Human, Dwarf, Gnome, Night Elf, Orc, Tauren, Troll, Undead
- Male and female variants for each race
- StandardNPC sounds for social interactions
- Character vocal sounds for combat
Technical Changes:
- Refactored NpcVoiceManager to support multiple sound categories
- Added callbacks: NpcFarewell, NpcVendor, NpcAggro
- Extended voice loading to parse both StandardNPC and Character vocal paths
- Integrated with GameHandler for gossip, vendor, and combat events
- Added detailed voice detection logging for debugging
Also includes:
- Sound manifest files added to docs/ for reference
- Blacksmith hammer pitch increased to 1.6x (was 1.4x)
- Blacksmith volume reduced 30% to 0.25 (was 0.35)
2026-02-09 16:03:51 -08:00
|
|
|
|
NpcFarewellCallback npcFarewellCallback_;
|
|
|
|
|
|
NpcVendorCallback npcVendorCallback_;
|
2026-02-19 21:13:13 -08:00
|
|
|
|
ChargeCallback chargeCallback_;
|
2026-02-17 17:23:42 -08:00
|
|
|
|
LevelUpCallback levelUpCallback_;
|
2026-02-19 20:36:25 -08:00
|
|
|
|
OtherPlayerLevelUpCallback otherPlayerLevelUpCallback_;
|
2026-03-09 13:53:42 -07:00
|
|
|
|
AchievementEarnedCallback achievementEarnedCallback_;
|
2026-02-07 17:59:40 -08:00
|
|
|
|
MountCallback mountCallback_;
|
2026-02-08 21:32:38 -08:00
|
|
|
|
TaxiPrecacheCallback taxiPrecacheCallback_;
|
2026-02-08 22:00:33 -08:00
|
|
|
|
TaxiOrientationCallback taxiOrientationCallback_;
|
|
|
|
|
|
TaxiFlightStartCallback taxiFlightStartCallback_;
|
2026-02-07 17:59:40 -08:00
|
|
|
|
uint32_t currentMountDisplayId_ = 0;
|
2026-02-14 16:42:47 -08:00
|
|
|
|
uint32_t mountAuraSpellId_ = 0; // Spell ID of the aura that caused mounting (for CMSG_CANCEL_AURA fallback)
|
2026-02-07 17:59:40 -08:00
|
|
|
|
float serverRunSpeed_ = 7.0f;
|
Add missing movement ACK responses to avoid server stalls
Implement generic handlers for force speed changes (walk, run back,
swim, swim back, flight, flight back, turn rate, pitch rate),
movement flag toggles (CAN_FLY, HOVER, feather fall, water walk),
and knockback ACKs. Fix SMSG_TIME_SYNC_REQ to respond with
CMSG_TIME_SYNC_RESP instead of silently dropping.
2026-02-26 03:02:51 -08:00
|
|
|
|
float serverWalkSpeed_ = 2.5f;
|
|
|
|
|
|
float serverRunBackSpeed_ = 4.5f;
|
|
|
|
|
|
float serverSwimSpeed_ = 4.722f;
|
|
|
|
|
|
float serverSwimBackSpeed_ = 2.5f;
|
|
|
|
|
|
float serverFlightSpeed_ = 7.0f;
|
|
|
|
|
|
float serverFlightBackSpeed_ = 4.5f;
|
|
|
|
|
|
float serverTurnRate_ = 3.14159f;
|
|
|
|
|
|
float serverPitchRate_ = 3.14159f;
|
2026-02-06 17:27:20 -08:00
|
|
|
|
bool playerDead_ = false;
|
2026-02-07 23:12:24 -08:00
|
|
|
|
bool releasedSpirit_ = false;
|
2026-03-09 22:31:56 -07:00
|
|
|
|
uint32_t corpseMapId_ = 0;
|
|
|
|
|
|
float corpseX_ = 0.0f, corpseY_ = 0.0f, corpseZ_ = 0.0f;
|
2026-03-09 18:28:03 -07:00
|
|
|
|
// Death Knight runes (class 6): slots 0-1=Blood, 2-3=Unholy, 4-5=Frost initially
|
|
|
|
|
|
std::array<RuneSlot, 6> playerRunes_ = [] {
|
|
|
|
|
|
std::array<RuneSlot, 6> r{};
|
|
|
|
|
|
r[0].type = r[1].type = RuneType::Blood;
|
|
|
|
|
|
r[2].type = r[3].type = RuneType::Unholy;
|
|
|
|
|
|
r[4].type = r[5].type = RuneType::Frost;
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}();
|
2026-02-07 21:47:14 -08:00
|
|
|
|
uint64_t pendingSpiritHealerGuid_ = 0;
|
|
|
|
|
|
bool resurrectPending_ = false;
|
2026-02-07 23:12:24 -08:00
|
|
|
|
bool resurrectRequestPending_ = false;
|
2026-03-10 12:53:05 -07:00
|
|
|
|
// ---- Talent wipe confirm dialog ----
|
|
|
|
|
|
bool talentWipePending_ = false;
|
|
|
|
|
|
uint64_t talentWipeNpcGuid_ = 0;
|
|
|
|
|
|
uint32_t talentWipeCost_ = 0;
|
2026-03-09 22:27:24 -07:00
|
|
|
|
bool resurrectIsSpiritHealer_ = false; // true = SMSG_SPIRIT_HEALER_CONFIRM, false = SMSG_RESURRECT_REQUEST
|
2026-02-07 23:12:24 -08:00
|
|
|
|
uint64_t resurrectCasterGuid_ = 0;
|
2026-03-09 22:27:24 -07:00
|
|
|
|
std::string resurrectCasterName_;
|
2026-02-07 21:47:14 -08:00
|
|
|
|
bool repopPending_ = false;
|
|
|
|
|
|
uint64_t lastRepopRequestMs_ = 0;
|
2026-03-09 15:23:02 -07:00
|
|
|
|
|
2026-03-09 15:27:20 -07:00
|
|
|
|
// ---- Completed quest IDs (SMSG_QUERY_QUESTS_COMPLETED_RESPONSE) ----
|
|
|
|
|
|
std::unordered_set<uint32_t> completedQuests_;
|
|
|
|
|
|
|
2026-03-09 15:23:02 -07:00
|
|
|
|
// ---- Equipment sets (SMSG_EQUIPMENT_SET_LIST) ----
|
|
|
|
|
|
struct EquipmentSet {
|
|
|
|
|
|
uint64_t setGuid = 0;
|
|
|
|
|
|
uint32_t setId = 0;
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
std::string iconName;
|
|
|
|
|
|
uint32_t ignoreSlotMask = 0;
|
|
|
|
|
|
std::array<uint64_t, 19> itemGuids{};
|
|
|
|
|
|
};
|
|
|
|
|
|
std::vector<EquipmentSet> equipmentSets_;
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Forced faction reactions (SMSG_SET_FORCED_REACTIONS) ----
|
|
|
|
|
|
std::unordered_map<uint32_t, uint8_t> forcedReactions_; // factionId -> reaction tier
|
2026-03-09 15:46:19 -07:00
|
|
|
|
|
2026-03-09 16:11:19 -07:00
|
|
|
|
// ---- Server-triggered audio ----
|
2026-03-09 15:46:19 -07:00
|
|
|
|
PlayMusicCallback playMusicCallback_;
|
2026-03-09 16:11:19 -07:00
|
|
|
|
PlaySoundCallback playSoundCallback_;
|
2026-03-09 16:16:39 -07:00
|
|
|
|
PlayPositionalSoundCallback playPositionalSoundCallback_;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace game
|
|
|
|
|
|
} // namespace wowee
|