mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00: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.
This commit is contained in:
parent
6bf3fa4ed4
commit
c49bb58e47
14 changed files with 3039 additions and 84 deletions
|
|
@ -35,6 +35,7 @@ public:
|
|||
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
void setSize(int w, int h) { width = w; height = h; }
|
||||
float getAspectRatio() const { return static_cast<float>(width) / height; }
|
||||
|
||||
SDL_Window* getSDLWindow() const { return window; }
|
||||
|
|
|
|||
|
|
@ -129,15 +129,33 @@ public:
|
|||
uint32_t getMaxHealth() const { return maxHealth; }
|
||||
void setMaxHealth(uint32_t h) { maxHealth = h; }
|
||||
|
||||
// Power (mana/rage/energy)
|
||||
uint32_t getPower() const { return power; }
|
||||
void setPower(uint32_t p) { power = p; }
|
||||
|
||||
uint32_t getMaxPower() const { return maxPower; }
|
||||
void setMaxPower(uint32_t p) { maxPower = p; }
|
||||
|
||||
uint8_t getPowerType() const { return powerType; }
|
||||
void setPowerType(uint8_t t) { powerType = t; }
|
||||
|
||||
// Level
|
||||
uint32_t getLevel() const { return level; }
|
||||
void setLevel(uint32_t l) { level = l; }
|
||||
|
||||
// Entry ID (creature template entry)
|
||||
uint32_t getEntry() const { return entry; }
|
||||
void setEntry(uint32_t e) { entry = e; }
|
||||
|
||||
protected:
|
||||
std::string name;
|
||||
uint32_t health = 0;
|
||||
uint32_t maxHealth = 0;
|
||||
uint32_t power = 0;
|
||||
uint32_t maxPower = 0;
|
||||
uint8_t powerType = 0; // 0=mana, 1=rage, 2=focus, 3=energy
|
||||
uint32_t level = 1;
|
||||
uint32_t entry = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@
|
|||
#include "game/world_packets.hpp"
|
||||
#include "game/character.hpp"
|
||||
#include "game/inventory.hpp"
|
||||
#include "game/spell_defines.hpp"
|
||||
#include "game/group_defines.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace wowee {
|
||||
namespace network { class WorldSocket; class Packet; }
|
||||
|
|
@ -165,6 +170,77 @@ public:
|
|||
bool hasTarget() const { return targetGuid != 0; }
|
||||
void tabTarget(float playerX, float playerY, float playerZ);
|
||||
|
||||
// ---- Phase 1: Name queries ----
|
||||
void queryPlayerName(uint64_t guid);
|
||||
void queryCreatureInfo(uint32_t entry, uint64_t guid);
|
||||
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; }
|
||||
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);
|
||||
const std::vector<uint32_t>& getKnownSpells() const { return knownSpells; }
|
||||
bool isCasting() const { return casting; }
|
||||
uint32_t getCurrentCastSpellId() const { return currentCastSpellId; }
|
||||
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
||||
float getCastTimeRemaining() const { return castTimeRemaining; }
|
||||
|
||||
// Action bar
|
||||
static constexpr int ACTION_BAR_SLOTS = 12;
|
||||
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);
|
||||
|
||||
// Auras
|
||||
const std::vector<AuraSlot>& getPlayerAuras() const { return playerAuras; }
|
||||
const std::vector<AuraSlot>& getTargetAuras() const { return targetAuras; }
|
||||
|
||||
// Cooldowns
|
||||
float getSpellCooldown(uint32_t spellId) const;
|
||||
|
||||
// Player GUID
|
||||
uint64_t getPlayerGuid() const { return playerGuid; }
|
||||
void setPlayerGuid(uint64_t guid) { playerGuid = guid; }
|
||||
|
||||
// ---- 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; }
|
||||
bool hasPendingGroupInvite() const { return pendingGroupInvite; }
|
||||
const std::string& getPendingInviterName() const { return pendingInviterName; }
|
||||
|
||||
// ---- Phase 5: Loot ----
|
||||
void lootTarget(uint64_t guid);
|
||||
void lootItem(uint8_t slotIndex);
|
||||
void closeLoot();
|
||||
bool isLootWindowOpen() const { return lootWindowOpen; }
|
||||
const LootResponseData& getCurrentLoot() const { return currentLoot; }
|
||||
|
||||
// NPC Gossip
|
||||
void interactWithNpc(uint64_t guid);
|
||||
void selectGossipOption(uint32_t optionId);
|
||||
void closeGossip();
|
||||
bool isGossipWindowOpen() const { return gossipWindowOpen; }
|
||||
const GossipMessageData& getCurrentGossip() const { return currentGossip; }
|
||||
|
||||
// Vendor
|
||||
void openVendor(uint64_t npcGuid);
|
||||
void buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint8_t count);
|
||||
void sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count);
|
||||
bool isVendorWindowOpen() const { return vendorWindowOpen; }
|
||||
const ListInventoryData& getVendorItems() const { return currentVendorItems; }
|
||||
|
||||
/**
|
||||
* Set callbacks
|
||||
*/
|
||||
|
|
@ -234,6 +310,45 @@ private:
|
|||
*/
|
||||
void handleMessageChat(network::Packet& packet);
|
||||
|
||||
// ---- Phase 1 handlers ----
|
||||
void handleNameQueryResponse(network::Packet& packet);
|
||||
void handleCreatureQueryResponse(network::Packet& packet);
|
||||
|
||||
// ---- 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);
|
||||
|
||||
// ---- 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);
|
||||
void handleAuraUpdate(network::Packet& packet, bool isAll);
|
||||
void handleLearnedSpell(network::Packet& packet);
|
||||
void handleRemovedSpell(network::Packet& packet);
|
||||
|
||||
// ---- 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);
|
||||
|
||||
// ---- Phase 5 handlers ----
|
||||
void handleLootResponse(network::Packet& packet);
|
||||
void handleLootReleaseResponse(network::Packet& packet);
|
||||
void handleLootRemoved(network::Packet& packet);
|
||||
void handleGossipMessage(network::Packet& packet);
|
||||
void handleGossipComplete(network::Packet& packet);
|
||||
void handleListInventory(network::Packet& packet);
|
||||
|
||||
void addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource);
|
||||
|
||||
/**
|
||||
* Send CMSG_PING to server (heartbeat)
|
||||
*/
|
||||
|
|
@ -301,6 +416,49 @@ private:
|
|||
float pingInterval = 30.0f; // Ping interval (30 seconds)
|
||||
uint32_t lastLatency = 0; // Last measured latency (milliseconds)
|
||||
|
||||
// Player GUID
|
||||
uint64_t playerGuid = 0;
|
||||
|
||||
// ---- 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;
|
||||
|
||||
// ---- Phase 2: Combat ----
|
||||
bool autoAttacking = false;
|
||||
uint64_t autoAttackTarget = 0;
|
||||
std::vector<CombatTextEntry> combatText;
|
||||
|
||||
// ---- Phase 3: Spells ----
|
||||
std::vector<uint32_t> knownSpells;
|
||||
std::unordered_map<uint32_t, float> spellCooldowns; // spellId -> remaining seconds
|
||||
uint8_t castCount = 0;
|
||||
bool casting = false;
|
||||
uint32_t currentCastSpellId = 0;
|
||||
float castTimeRemaining = 0.0f;
|
||||
float castTimeTotal = 0.0f;
|
||||
std::array<ActionBarSlot, 12> actionBar{};
|
||||
std::vector<AuraSlot> playerAuras;
|
||||
std::vector<AuraSlot> targetAuras;
|
||||
|
||||
// ---- Phase 4: Group ----
|
||||
GroupListData partyData;
|
||||
bool pendingGroupInvite = false;
|
||||
std::string pendingInviterName;
|
||||
|
||||
// ---- Phase 5: Loot ----
|
||||
bool lootWindowOpen = false;
|
||||
LootResponseData currentLoot;
|
||||
|
||||
// Gossip
|
||||
bool gossipWindowOpen = false;
|
||||
GossipMessageData currentGossip;
|
||||
|
||||
// Vendor
|
||||
bool vendorWindowOpen = false;
|
||||
ListInventoryData currentVendorItems;
|
||||
|
||||
// Callbacks
|
||||
WorldConnectSuccessCallback onSuccess;
|
||||
WorldConnectFailureCallback onFailure;
|
||||
|
|
|
|||
72
include/game/group_defines.hpp
Normal file
72
include/game/group_defines.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Party/Group member data
|
||||
*/
|
||||
struct GroupMember {
|
||||
std::string name;
|
||||
uint64_t guid = 0;
|
||||
uint8_t isOnline = 0; // 0 = offline, 1 = online
|
||||
uint8_t subGroup = 0; // Raid subgroup (0 for party)
|
||||
uint8_t flags = 0; // Assistant, main tank, etc.
|
||||
uint8_t roles = 0; // LFG roles (3.3.5a)
|
||||
};
|
||||
|
||||
/**
|
||||
* Full group/party data from SMSG_GROUP_LIST
|
||||
*/
|
||||
struct GroupListData {
|
||||
uint8_t groupType = 0; // 0 = party, 1 = raid
|
||||
uint8_t subGroup = 0;
|
||||
uint8_t flags = 0;
|
||||
uint8_t roles = 0;
|
||||
uint8_t lootMethod = 0; // 0=free for all, 1=round robin, 2=master loot
|
||||
uint64_t looterGuid = 0;
|
||||
uint8_t lootThreshold = 0;
|
||||
uint8_t difficultyId = 0;
|
||||
uint8_t raidDifficultyId = 0;
|
||||
uint32_t memberCount = 0;
|
||||
std::vector<GroupMember> members;
|
||||
uint64_t leaderGuid = 0;
|
||||
|
||||
bool isValid() const { return true; }
|
||||
bool isEmpty() const { return memberCount == 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Party command types
|
||||
*/
|
||||
enum class PartyCommand : uint32_t {
|
||||
INVITE = 0,
|
||||
UNINVITE = 1,
|
||||
LEAVE = 2,
|
||||
SWAP = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Party command result codes
|
||||
*/
|
||||
enum class PartyResult : uint32_t {
|
||||
OK = 0,
|
||||
BAD_PLAYER_NAME = 1,
|
||||
TARGET_NOT_IN_GROUP = 2,
|
||||
TARGET_NOT_IN_INSTANCE = 3,
|
||||
GROUP_FULL = 4,
|
||||
ALREADY_IN_GROUP = 5,
|
||||
NOT_IN_GROUP = 6,
|
||||
NOT_LEADER = 7,
|
||||
PLAYER_WRONG_FACTION = 8,
|
||||
IGNORING_YOU = 9,
|
||||
LFG_PENDING = 12,
|
||||
INVITE_RESTRICTED = 13
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
@ -9,48 +9,132 @@ namespace game {
|
|||
// Values derived from community reverse-engineering efforts
|
||||
// Reference: https://wowdev.wiki/World_Packet
|
||||
enum class Opcode : uint16_t {
|
||||
// Client to Server
|
||||
CMSG_PING = 0x1DC,
|
||||
CMSG_AUTH_SESSION = 0x1ED,
|
||||
CMSG_CHAR_ENUM = 0x037,
|
||||
CMSG_PLAYER_LOGIN = 0x03D,
|
||||
// ---- Client to Server (Core) ----
|
||||
CMSG_PING = 0x1DC,
|
||||
CMSG_AUTH_SESSION = 0x1ED,
|
||||
CMSG_CHAR_ENUM = 0x037,
|
||||
CMSG_PLAYER_LOGIN = 0x03D,
|
||||
|
||||
// Movement
|
||||
CMSG_MOVE_START_FORWARD = 0x0B5,
|
||||
CMSG_MOVE_START_BACKWARD = 0x0B6,
|
||||
CMSG_MOVE_STOP = 0x0B7,
|
||||
CMSG_MOVE_START_STRAFE_LEFT = 0x0B8,
|
||||
CMSG_MOVE_START_STRAFE_RIGHT = 0x0B9,
|
||||
CMSG_MOVE_STOP_STRAFE = 0x0BA,
|
||||
CMSG_MOVE_JUMP = 0x0BB,
|
||||
CMSG_MOVE_START_TURN_LEFT = 0x0BC,
|
||||
CMSG_MOVE_START_TURN_RIGHT = 0x0BD,
|
||||
CMSG_MOVE_STOP_TURN = 0x0BE,
|
||||
CMSG_MOVE_SET_FACING = 0x0DA,
|
||||
CMSG_MOVE_FALL_LAND = 0x0C9,
|
||||
CMSG_MOVE_START_SWIM = 0x0CA,
|
||||
CMSG_MOVE_STOP_SWIM = 0x0CB,
|
||||
CMSG_MOVE_HEARTBEAT = 0x0EE,
|
||||
// ---- Movement ----
|
||||
CMSG_MOVE_START_FORWARD = 0x0B5,
|
||||
CMSG_MOVE_START_BACKWARD = 0x0B6,
|
||||
CMSG_MOVE_STOP = 0x0B7,
|
||||
CMSG_MOVE_START_STRAFE_LEFT = 0x0B8,
|
||||
CMSG_MOVE_START_STRAFE_RIGHT = 0x0B9,
|
||||
CMSG_MOVE_STOP_STRAFE = 0x0BA,
|
||||
CMSG_MOVE_JUMP = 0x0BB,
|
||||
CMSG_MOVE_START_TURN_LEFT = 0x0BC,
|
||||
CMSG_MOVE_START_TURN_RIGHT = 0x0BD,
|
||||
CMSG_MOVE_STOP_TURN = 0x0BE,
|
||||
CMSG_MOVE_SET_FACING = 0x0DA,
|
||||
CMSG_MOVE_FALL_LAND = 0x0C9,
|
||||
CMSG_MOVE_START_SWIM = 0x0CA,
|
||||
CMSG_MOVE_STOP_SWIM = 0x0CB,
|
||||
CMSG_MOVE_HEARTBEAT = 0x0EE,
|
||||
|
||||
// Server to Client
|
||||
SMSG_AUTH_CHALLENGE = 0x1EC,
|
||||
SMSG_AUTH_RESPONSE = 0x1EE,
|
||||
SMSG_CHAR_ENUM = 0x03B,
|
||||
SMSG_PONG = 0x1DD,
|
||||
SMSG_LOGIN_VERIFY_WORLD = 0x236,
|
||||
SMSG_ACCOUNT_DATA_TIMES = 0x209,
|
||||
SMSG_FEATURE_SYSTEM_STATUS = 0x3ED,
|
||||
SMSG_MOTD = 0x33D,
|
||||
// ---- Server to Client (Core) ----
|
||||
SMSG_AUTH_CHALLENGE = 0x1EC,
|
||||
SMSG_AUTH_RESPONSE = 0x1EE,
|
||||
SMSG_CHAR_ENUM = 0x03B,
|
||||
SMSG_PONG = 0x1DD,
|
||||
SMSG_LOGIN_VERIFY_WORLD = 0x236,
|
||||
SMSG_ACCOUNT_DATA_TIMES = 0x209,
|
||||
SMSG_FEATURE_SYSTEM_STATUS = 0x3ED,
|
||||
SMSG_MOTD = 0x33D,
|
||||
|
||||
// Entity/Object updates
|
||||
SMSG_UPDATE_OBJECT = 0x0A9,
|
||||
SMSG_DESTROY_OBJECT = 0x0AA,
|
||||
// ---- Entity/Object updates ----
|
||||
SMSG_UPDATE_OBJECT = 0x0A9,
|
||||
SMSG_DESTROY_OBJECT = 0x0AA,
|
||||
|
||||
// Chat
|
||||
CMSG_MESSAGECHAT = 0x095,
|
||||
SMSG_MESSAGECHAT = 0x096,
|
||||
// ---- Chat ----
|
||||
CMSG_MESSAGECHAT = 0x095,
|
||||
SMSG_MESSAGECHAT = 0x096,
|
||||
|
||||
// TODO: Add more opcodes as needed
|
||||
// ---- Phase 1: Foundation (Targeting, Queries) ----
|
||||
CMSG_SET_SELECTION = 0x13D,
|
||||
CMSG_NAME_QUERY = 0x050,
|
||||
SMSG_NAME_QUERY_RESPONSE = 0x051,
|
||||
CMSG_CREATURE_QUERY = 0x060,
|
||||
SMSG_CREATURE_QUERY_RESPONSE = 0x061,
|
||||
CMSG_GAMEOBJECT_QUERY = 0x05E,
|
||||
SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x05F,
|
||||
CMSG_SET_ACTIVE_MOVER = 0x26A,
|
||||
|
||||
// ---- Phase 2: Combat Core ----
|
||||
CMSG_ATTACKSWING = 0x141,
|
||||
CMSG_ATTACKSTOP = 0x142,
|
||||
SMSG_ATTACKSTART = 0x143,
|
||||
SMSG_ATTACKSTOP = 0x144,
|
||||
SMSG_ATTACKERSTATEUPDATE = 0x14A,
|
||||
SMSG_SPELLNONMELEEDAMAGELOG = 0x250,
|
||||
SMSG_SPELLHEALLOG = 0x150,
|
||||
SMSG_SPELLENERGIZELOG = 0x25B,
|
||||
SMSG_PERIODICAURALOG = 0x24E,
|
||||
SMSG_ENVIRONMENTALDAMAGELOG = 0x1FC,
|
||||
|
||||
// ---- Phase 3: Spells, Action Bar, Auras ----
|
||||
CMSG_CAST_SPELL = 0x12E,
|
||||
CMSG_CANCEL_CAST = 0x12F,
|
||||
CMSG_CANCEL_AURA = 0x033,
|
||||
SMSG_CAST_FAILED = 0x130,
|
||||
SMSG_SPELL_START = 0x131,
|
||||
SMSG_SPELL_GO = 0x132,
|
||||
SMSG_SPELL_FAILURE = 0x133,
|
||||
SMSG_SPELL_COOLDOWN = 0x134,
|
||||
SMSG_COOLDOWN_EVENT = 0x135,
|
||||
SMSG_UPDATE_AURA_DURATION = 0x137,
|
||||
SMSG_INITIAL_SPELLS = 0x12A,
|
||||
SMSG_LEARNED_SPELL = 0x12B,
|
||||
SMSG_REMOVED_SPELL = 0x203,
|
||||
SMSG_SPELL_DELAYED = 0x1E2,
|
||||
SMSG_AURA_UPDATE = 0x3FA,
|
||||
SMSG_AURA_UPDATE_ALL = 0x495,
|
||||
SMSG_SET_FLAT_SPELL_MODIFIER = 0x266,
|
||||
SMSG_SET_PCT_SPELL_MODIFIER = 0x267,
|
||||
|
||||
// ---- Phase 4: Group/Party ----
|
||||
CMSG_GROUP_INVITE = 0x06E,
|
||||
SMSG_GROUP_INVITE = 0x06F,
|
||||
CMSG_GROUP_ACCEPT = 0x072,
|
||||
CMSG_GROUP_DECLINE = 0x073,
|
||||
SMSG_GROUP_DECLINE = 0x074,
|
||||
CMSG_GROUP_UNINVITE_GUID = 0x076,
|
||||
SMSG_GROUP_UNINVITE = 0x077,
|
||||
CMSG_GROUP_SET_LEADER = 0x078,
|
||||
SMSG_GROUP_SET_LEADER = 0x079,
|
||||
CMSG_GROUP_DISBAND = 0x07B,
|
||||
SMSG_GROUP_LIST = 0x07D,
|
||||
SMSG_PARTY_COMMAND_RESULT = 0x07E,
|
||||
MSG_RAID_TARGET_UPDATE = 0x321,
|
||||
|
||||
// ---- Phase 5: Loot ----
|
||||
CMSG_LOOT = 0x15D,
|
||||
CMSG_LOOT_RELEASE = 0x15E,
|
||||
SMSG_LOOT_RESPONSE = 0x160,
|
||||
SMSG_LOOT_RELEASE_RESPONSE = 0x161,
|
||||
CMSG_AUTOSTORE_LOOT_ITEM = 0x162,
|
||||
SMSG_LOOT_REMOVED = 0x163,
|
||||
SMSG_LOOT_MONEY_NOTIFY = 0x164,
|
||||
SMSG_LOOT_CLEAR_MONEY = 0x165,
|
||||
|
||||
// ---- Phase 5: NPC Gossip ----
|
||||
CMSG_GOSSIP_HELLO = 0x17C,
|
||||
SMSG_GOSSIP_MESSAGE = 0x17D,
|
||||
CMSG_GOSSIP_SELECT_OPTION = 0x17E,
|
||||
SMSG_GOSSIP_COMPLETE = 0x17F,
|
||||
SMSG_NPC_TEXT_UPDATE = 0x180,
|
||||
|
||||
// ---- Phase 5: Vendor ----
|
||||
CMSG_LIST_INVENTORY = 0x19E,
|
||||
SMSG_LIST_INVENTORY = 0x19F,
|
||||
CMSG_SELL_ITEM = 0x1A0,
|
||||
SMSG_SELL_ITEM = 0x1A1,
|
||||
CMSG_BUY_ITEM = 0x1A2,
|
||||
SMSG_BUY_FAILED = 0x1A5,
|
||||
|
||||
// ---- Phase 5: Item/Equip ----
|
||||
CMSG_AUTOEQUIP_ITEM = 0x10A,
|
||||
SMSG_INVENTORY_CHANGE_FAILURE = 0x112,
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
|||
69
include/game/spell_defines.hpp
Normal file
69
include/game/spell_defines.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
/**
|
||||
* Aura slot data for buff/debuff tracking
|
||||
*/
|
||||
struct AuraSlot {
|
||||
uint32_t spellId = 0;
|
||||
uint8_t flags = 0; // Active, positive/negative, etc.
|
||||
uint8_t level = 0;
|
||||
uint8_t charges = 0;
|
||||
int32_t durationMs = -1;
|
||||
int32_t maxDurationMs = -1;
|
||||
uint64_t casterGuid = 0;
|
||||
|
||||
bool isEmpty() const { return spellId == 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Action bar slot
|
||||
*/
|
||||
struct ActionBarSlot {
|
||||
enum Type : uint8_t { EMPTY = 0, SPELL = 1, ITEM = 2, MACRO = 3 };
|
||||
Type type = EMPTY;
|
||||
uint32_t id = 0; // spellId, itemId, or macroId
|
||||
float cooldownRemaining = 0.0f;
|
||||
float cooldownTotal = 0.0f;
|
||||
|
||||
bool isReady() const { return cooldownRemaining <= 0.0f; }
|
||||
bool isEmpty() const { return type == EMPTY; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Floating combat text entry
|
||||
*/
|
||||
struct CombatTextEntry {
|
||||
enum Type : uint8_t {
|
||||
MELEE_DAMAGE, SPELL_DAMAGE, HEAL, MISS, DODGE, PARRY, BLOCK,
|
||||
CRIT_DAMAGE, CRIT_HEAL, PERIODIC_DAMAGE, PERIODIC_HEAL, ENVIRONMENTAL
|
||||
};
|
||||
Type type;
|
||||
int32_t amount = 0;
|
||||
uint32_t spellId = 0;
|
||||
float age = 0.0f; // Seconds since creation (for fadeout)
|
||||
bool isPlayerSource = false; // True if player dealt this
|
||||
|
||||
static constexpr float LIFETIME = 2.5f;
|
||||
bool isExpired() const { return age >= LIFETIME; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Spell cooldown entry received from server
|
||||
*/
|
||||
struct SpellCooldownEntry {
|
||||
uint32_t spellId;
|
||||
uint16_t itemId;
|
||||
uint16_t categoryId;
|
||||
uint32_t cooldownMs;
|
||||
uint32_t categoryCooldownMs;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
@ -3,10 +3,13 @@
|
|||
#include "network/packet.hpp"
|
||||
#include "game/opcodes.hpp"
|
||||
#include "game/entity.hpp"
|
||||
#include "game/spell_defines.hpp"
|
||||
#include "game/group_defines.hpp"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
|
@ -397,6 +400,14 @@ public:
|
|||
*/
|
||||
static bool parse(network::Packet& packet, UpdateObjectData& data);
|
||||
|
||||
/**
|
||||
* Read packed GUID from packet
|
||||
*
|
||||
* @param packet Packet to read from
|
||||
* @return GUID value
|
||||
*/
|
||||
static uint64_t readPackedGuid(network::Packet& packet);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parse a single update block
|
||||
|
|
@ -424,14 +435,6 @@ private:
|
|||
* @return true if successful
|
||||
*/
|
||||
static bool parseUpdateFields(network::Packet& packet, UpdateBlock& block);
|
||||
|
||||
/**
|
||||
* Read packed GUID from packet
|
||||
*
|
||||
* @param packet Packet to read from
|
||||
* @return GUID value
|
||||
*/
|
||||
static uint64_t readPackedGuid(network::Packet& packet);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -562,5 +565,521 @@ public:
|
|||
*/
|
||||
const char* getChatTypeString(ChatType type);
|
||||
|
||||
// ============================================================
|
||||
// Phase 1: Foundation — Targeting, Name Queries
|
||||
// ============================================================
|
||||
|
||||
/** CMSG_SET_SELECTION packet builder */
|
||||
class SetSelectionPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t targetGuid);
|
||||
};
|
||||
|
||||
/** CMSG_SET_ACTIVE_MOVER packet builder */
|
||||
class SetActiveMoverPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t guid);
|
||||
};
|
||||
|
||||
/** CMSG_NAME_QUERY packet builder */
|
||||
class NameQueryPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t playerGuid);
|
||||
};
|
||||
|
||||
/** SMSG_NAME_QUERY_RESPONSE data */
|
||||
struct NameQueryResponseData {
|
||||
uint64_t guid = 0;
|
||||
uint8_t found = 1; // 0 = found, 1 = not found
|
||||
std::string name;
|
||||
std::string realmName;
|
||||
uint8_t race = 0;
|
||||
uint8_t gender = 0;
|
||||
uint8_t classId = 0;
|
||||
|
||||
bool isValid() const { return found == 0 && !name.empty(); }
|
||||
};
|
||||
|
||||
/** SMSG_NAME_QUERY_RESPONSE parser */
|
||||
class NameQueryResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, NameQueryResponseData& data);
|
||||
};
|
||||
|
||||
/** CMSG_CREATURE_QUERY packet builder */
|
||||
class CreatureQueryPacket {
|
||||
public:
|
||||
static network::Packet build(uint32_t entry, uint64_t guid);
|
||||
};
|
||||
|
||||
/** SMSG_CREATURE_QUERY_RESPONSE data */
|
||||
struct CreatureQueryResponseData {
|
||||
uint32_t entry = 0;
|
||||
std::string name;
|
||||
std::string subName;
|
||||
std::string iconName;
|
||||
uint32_t typeFlags = 0;
|
||||
uint32_t creatureType = 0;
|
||||
uint32_t family = 0;
|
||||
uint32_t rank = 0; // 0=Normal, 1=Elite, 2=Rare Elite, 3=Boss, 4=Rare
|
||||
|
||||
bool isValid() const { return entry != 0 && !name.empty(); }
|
||||
};
|
||||
|
||||
/** SMSG_CREATURE_QUERY_RESPONSE parser */
|
||||
class CreatureQueryResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, CreatureQueryResponseData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 2: Combat Core
|
||||
// ============================================================
|
||||
|
||||
/** CMSG_ATTACKSWING packet builder */
|
||||
class AttackSwingPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t targetGuid);
|
||||
};
|
||||
|
||||
/** CMSG_ATTACKSTOP packet builder */
|
||||
class AttackStopPacket {
|
||||
public:
|
||||
static network::Packet build();
|
||||
};
|
||||
|
||||
/** SMSG_ATTACKSTART data */
|
||||
struct AttackStartData {
|
||||
uint64_t attackerGuid = 0;
|
||||
uint64_t victimGuid = 0;
|
||||
bool isValid() const { return attackerGuid != 0 && victimGuid != 0; }
|
||||
};
|
||||
|
||||
class AttackStartParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AttackStartData& data);
|
||||
};
|
||||
|
||||
/** SMSG_ATTACKSTOP data */
|
||||
struct AttackStopData {
|
||||
uint64_t attackerGuid = 0;
|
||||
uint64_t victimGuid = 0;
|
||||
uint32_t unknown = 0;
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
class AttackStopParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AttackStopData& data);
|
||||
};
|
||||
|
||||
/** Sub-damage entry for melee hits */
|
||||
struct SubDamage {
|
||||
uint32_t schoolMask = 0;
|
||||
float damage = 0.0f;
|
||||
uint32_t intDamage = 0;
|
||||
uint32_t absorbed = 0;
|
||||
uint32_t resisted = 0;
|
||||
};
|
||||
|
||||
/** SMSG_ATTACKERSTATEUPDATE data */
|
||||
struct AttackerStateUpdateData {
|
||||
uint32_t hitInfo = 0;
|
||||
uint64_t attackerGuid = 0;
|
||||
uint64_t targetGuid = 0;
|
||||
int32_t totalDamage = 0;
|
||||
uint8_t subDamageCount = 0;
|
||||
std::vector<SubDamage> subDamages;
|
||||
uint32_t victimState = 0; // 0=hit, 1=dodge, 2=parry, 3=interrupt, 4=block, etc.
|
||||
int32_t overkill = -1;
|
||||
uint32_t blocked = 0;
|
||||
|
||||
bool isValid() const { return attackerGuid != 0; }
|
||||
bool isCrit() const { return (hitInfo & 0x200) != 0; }
|
||||
bool isMiss() const { return (hitInfo & 0x10) != 0; }
|
||||
};
|
||||
|
||||
class AttackerStateUpdateParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AttackerStateUpdateData& data);
|
||||
};
|
||||
|
||||
/** SMSG_SPELLNONMELEEDAMAGELOG data (simplified) */
|
||||
struct SpellDamageLogData {
|
||||
uint64_t targetGuid = 0;
|
||||
uint64_t attackerGuid = 0;
|
||||
uint32_t spellId = 0;
|
||||
uint32_t damage = 0;
|
||||
uint32_t overkill = 0;
|
||||
uint8_t schoolMask = 0;
|
||||
uint32_t absorbed = 0;
|
||||
uint32_t resisted = 0;
|
||||
bool isCrit = false;
|
||||
|
||||
bool isValid() const { return spellId != 0; }
|
||||
};
|
||||
|
||||
class SpellDamageLogParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, SpellDamageLogData& data);
|
||||
};
|
||||
|
||||
/** SMSG_SPELLHEALLOG data (simplified) */
|
||||
struct SpellHealLogData {
|
||||
uint64_t targetGuid = 0;
|
||||
uint64_t casterGuid = 0;
|
||||
uint32_t spellId = 0;
|
||||
uint32_t heal = 0;
|
||||
uint32_t overheal = 0;
|
||||
uint32_t absorbed = 0;
|
||||
bool isCrit = false;
|
||||
|
||||
bool isValid() const { return spellId != 0; }
|
||||
};
|
||||
|
||||
class SpellHealLogParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, SpellHealLogData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 3: Spells, Action Bar, Auras
|
||||
// ============================================================
|
||||
|
||||
/** SMSG_INITIAL_SPELLS data */
|
||||
struct InitialSpellsData {
|
||||
uint8_t talentSpec = 0;
|
||||
std::vector<uint32_t> spellIds;
|
||||
std::vector<SpellCooldownEntry> cooldowns;
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
class InitialSpellsParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, InitialSpellsData& data);
|
||||
};
|
||||
|
||||
/** CMSG_CAST_SPELL packet builder */
|
||||
class CastSpellPacket {
|
||||
public:
|
||||
static network::Packet build(uint32_t spellId, uint64_t targetGuid, uint8_t castCount);
|
||||
};
|
||||
|
||||
/** CMSG_CANCEL_CAST packet builder */
|
||||
class CancelCastPacket {
|
||||
public:
|
||||
static network::Packet build(uint32_t spellId);
|
||||
};
|
||||
|
||||
/** CMSG_CANCEL_AURA packet builder */
|
||||
class CancelAuraPacket {
|
||||
public:
|
||||
static network::Packet build(uint32_t spellId);
|
||||
};
|
||||
|
||||
/** SMSG_CAST_FAILED data */
|
||||
struct CastFailedData {
|
||||
uint8_t castCount = 0;
|
||||
uint32_t spellId = 0;
|
||||
uint8_t result = 0;
|
||||
|
||||
bool isValid() const { return spellId != 0; }
|
||||
};
|
||||
|
||||
class CastFailedParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, CastFailedData& data);
|
||||
};
|
||||
|
||||
/** SMSG_SPELL_START data (simplified) */
|
||||
struct SpellStartData {
|
||||
uint64_t casterGuid = 0;
|
||||
uint64_t casterUnit = 0;
|
||||
uint8_t castCount = 0;
|
||||
uint32_t spellId = 0;
|
||||
uint32_t castFlags = 0;
|
||||
uint32_t castTime = 0;
|
||||
uint64_t targetGuid = 0;
|
||||
|
||||
bool isValid() const { return spellId != 0; }
|
||||
};
|
||||
|
||||
class SpellStartParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, SpellStartData& data);
|
||||
};
|
||||
|
||||
/** SMSG_SPELL_GO data (simplified) */
|
||||
struct SpellGoData {
|
||||
uint64_t casterGuid = 0;
|
||||
uint64_t casterUnit = 0;
|
||||
uint8_t castCount = 0;
|
||||
uint32_t spellId = 0;
|
||||
uint32_t castFlags = 0;
|
||||
uint8_t hitCount = 0;
|
||||
std::vector<uint64_t> hitTargets;
|
||||
uint8_t missCount = 0;
|
||||
|
||||
bool isValid() const { return spellId != 0; }
|
||||
};
|
||||
|
||||
class SpellGoParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, SpellGoData& data);
|
||||
};
|
||||
|
||||
/** SMSG_AURA_UPDATE / SMSG_AURA_UPDATE_ALL data */
|
||||
struct AuraUpdateData {
|
||||
uint64_t guid = 0;
|
||||
std::vector<std::pair<uint8_t, AuraSlot>> updates; // slot index + aura data
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
class AuraUpdateParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, AuraUpdateData& data, bool isAll);
|
||||
};
|
||||
|
||||
/** SMSG_SPELL_COOLDOWN data */
|
||||
struct SpellCooldownData {
|
||||
uint64_t guid = 0;
|
||||
uint8_t flags = 0;
|
||||
std::vector<std::pair<uint32_t, uint32_t>> cooldowns; // spellId, cooldownMs
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
class SpellCooldownParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, SpellCooldownData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 4: Group/Party System
|
||||
// ============================================================
|
||||
|
||||
/** CMSG_GROUP_INVITE packet builder */
|
||||
class GroupInvitePacket {
|
||||
public:
|
||||
static network::Packet build(const std::string& playerName);
|
||||
};
|
||||
|
||||
/** SMSG_GROUP_INVITE data */
|
||||
struct GroupInviteResponseData {
|
||||
uint8_t canAccept = 0;
|
||||
std::string inviterName;
|
||||
|
||||
bool isValid() const { return !inviterName.empty(); }
|
||||
};
|
||||
|
||||
class GroupInviteResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, GroupInviteResponseData& data);
|
||||
};
|
||||
|
||||
/** CMSG_GROUP_ACCEPT packet builder */
|
||||
class GroupAcceptPacket {
|
||||
public:
|
||||
static network::Packet build();
|
||||
};
|
||||
|
||||
/** CMSG_GROUP_DECLINE packet builder */
|
||||
class GroupDeclinePacket {
|
||||
public:
|
||||
static network::Packet build();
|
||||
};
|
||||
|
||||
/** CMSG_GROUP_DISBAND (leave party) packet builder */
|
||||
class GroupDisbandPacket {
|
||||
public:
|
||||
static network::Packet build();
|
||||
};
|
||||
|
||||
/** SMSG_GROUP_LIST parser */
|
||||
class GroupListParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, GroupListData& data);
|
||||
};
|
||||
|
||||
/** SMSG_PARTY_COMMAND_RESULT data */
|
||||
struct PartyCommandResultData {
|
||||
PartyCommand command;
|
||||
std::string name;
|
||||
PartyResult result;
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
class PartyCommandResultParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, PartyCommandResultData& data);
|
||||
};
|
||||
|
||||
/** SMSG_GROUP_DECLINE data */
|
||||
struct GroupDeclineData {
|
||||
std::string playerName;
|
||||
bool isValid() const { return !playerName.empty(); }
|
||||
};
|
||||
|
||||
class GroupDeclineResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, GroupDeclineData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 5: Loot System
|
||||
// ============================================================
|
||||
|
||||
/** Loot item entry */
|
||||
struct LootItem {
|
||||
uint8_t slotIndex = 0;
|
||||
uint32_t itemId = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t displayInfoId = 0;
|
||||
uint32_t randomSuffix = 0;
|
||||
uint32_t randomPropertyId = 0;
|
||||
uint8_t lootSlotType = 0;
|
||||
};
|
||||
|
||||
/** SMSG_LOOT_RESPONSE data */
|
||||
struct LootResponseData {
|
||||
uint64_t lootGuid = 0;
|
||||
uint8_t lootType = 0;
|
||||
uint32_t gold = 0; // In copper
|
||||
std::vector<LootItem> items;
|
||||
|
||||
bool isValid() const { return true; }
|
||||
uint32_t getGold() const { return gold / 10000; }
|
||||
uint32_t getSilver() const { return (gold / 100) % 100; }
|
||||
uint32_t getCopper() const { return gold % 100; }
|
||||
};
|
||||
|
||||
/** CMSG_LOOT packet builder */
|
||||
class LootPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t targetGuid);
|
||||
};
|
||||
|
||||
/** CMSG_AUTOSTORE_LOOT_ITEM packet builder */
|
||||
class AutostoreLootItemPacket {
|
||||
public:
|
||||
static network::Packet build(uint8_t slotIndex);
|
||||
};
|
||||
|
||||
/** CMSG_LOOT_RELEASE packet builder */
|
||||
class LootReleasePacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t lootGuid);
|
||||
};
|
||||
|
||||
/** SMSG_LOOT_RESPONSE parser */
|
||||
class LootResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, LootResponseData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 5: NPC Gossip
|
||||
// ============================================================
|
||||
|
||||
/** Gossip menu option */
|
||||
struct GossipOption {
|
||||
uint32_t id = 0;
|
||||
uint8_t icon = 0; // 0=chat, 1=vendor, 2=taxi, 3=trainer, etc.
|
||||
bool isCoded = false;
|
||||
uint32_t boxMoney = 0;
|
||||
std::string text;
|
||||
std::string boxText;
|
||||
};
|
||||
|
||||
/** Gossip quest item */
|
||||
struct GossipQuestItem {
|
||||
uint32_t questId = 0;
|
||||
uint32_t questIcon = 0;
|
||||
int32_t questLevel = 0;
|
||||
uint32_t questFlags = 0;
|
||||
uint8_t isRepeatable = 0;
|
||||
std::string title;
|
||||
};
|
||||
|
||||
/** SMSG_GOSSIP_MESSAGE data */
|
||||
struct GossipMessageData {
|
||||
uint64_t npcGuid = 0;
|
||||
uint32_t menuId = 0;
|
||||
uint32_t titleTextId = 0;
|
||||
std::vector<GossipOption> options;
|
||||
std::vector<GossipQuestItem> quests;
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
/** CMSG_GOSSIP_HELLO packet builder */
|
||||
class GossipHelloPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t npcGuid);
|
||||
};
|
||||
|
||||
/** CMSG_GOSSIP_SELECT_OPTION packet builder */
|
||||
class GossipSelectOptionPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t npcGuid, uint32_t optionId, const std::string& code = "");
|
||||
};
|
||||
|
||||
/** SMSG_GOSSIP_MESSAGE parser */
|
||||
class GossipMessageParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, GossipMessageData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 5: Vendor
|
||||
// ============================================================
|
||||
|
||||
/** Vendor item entry */
|
||||
struct VendorItem {
|
||||
uint32_t slot = 0;
|
||||
uint32_t itemId = 0;
|
||||
uint32_t displayInfoId = 0;
|
||||
int32_t maxCount = -1; // -1 = unlimited
|
||||
uint32_t buyPrice = 0; // In copper
|
||||
uint32_t durability = 0;
|
||||
uint32_t stackCount = 0;
|
||||
uint32_t extendedCost = 0;
|
||||
};
|
||||
|
||||
/** SMSG_LIST_INVENTORY data */
|
||||
struct ListInventoryData {
|
||||
uint64_t vendorGuid = 0;
|
||||
std::vector<VendorItem> items;
|
||||
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
/** CMSG_LIST_INVENTORY packet builder */
|
||||
class ListInventoryPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t npcGuid);
|
||||
};
|
||||
|
||||
/** CMSG_BUY_ITEM packet builder */
|
||||
class BuyItemPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint8_t count);
|
||||
};
|
||||
|
||||
/** CMSG_SELL_ITEM packet builder */
|
||||
class SellItemPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count);
|
||||
};
|
||||
|
||||
/** SMSG_LIST_INVENTORY parser */
|
||||
class ListInventoryParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, ListInventoryData& data);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ private:
|
|||
*/
|
||||
void calculateFPS();
|
||||
|
||||
bool enabled = true; // Enabled by default, press F1 to toggle
|
||||
bool enabled = false; // Disabled by default, press F1 to toggle
|
||||
Position position = Position::TOP_LEFT;
|
||||
|
||||
// Section visibility
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ private:
|
|||
int selectedChatType = 0; // 0=SAY, 1=YELL, 2=PARTY, etc.
|
||||
|
||||
// UI state
|
||||
bool showEntityWindow = true;
|
||||
bool showEntityWindow = false;
|
||||
bool showChatWindow = true;
|
||||
bool showPlayerInfo = true;
|
||||
bool showPlayerInfo = false;
|
||||
bool refocusChatInput = false;
|
||||
|
||||
/**
|
||||
|
|
@ -95,6 +95,17 @@ private:
|
|||
*/
|
||||
void updateCharacterTextures(game::Inventory& inventory);
|
||||
|
||||
// ---- New UI renders ----
|
||||
void renderActionBar(game::GameHandler& gameHandler);
|
||||
void renderCastBar(game::GameHandler& gameHandler);
|
||||
void renderCombatText(game::GameHandler& gameHandler);
|
||||
void renderPartyFrames(game::GameHandler& gameHandler);
|
||||
void renderGroupInvitePopup(game::GameHandler& gameHandler);
|
||||
void renderBuffBar(game::GameHandler& gameHandler);
|
||||
void renderLootWindow(game::GameHandler& gameHandler);
|
||||
void renderGossipWindow(game::GameHandler& gameHandler);
|
||||
void renderVendorWindow(game::GameHandler& gameHandler);
|
||||
|
||||
/**
|
||||
* Inventory screen
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue