Merge pull request #33 from ldmonster/chore/game-screen-extract

[chore] refactor(ui): GameScreen — extract 7 UI sub-panels
This commit is contained in:
Kelsi Rae Davis 2026-03-31 21:52:48 -07:00 committed by GitHub
commit 9eb8ca2988
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 20568 additions and 19417 deletions

View file

@ -616,6 +616,14 @@ set(WOWEE_SOURCES
src/ui/character_create_screen.cpp
src/ui/character_screen.cpp
src/ui/game_screen.cpp
src/ui/chat_panel.cpp
src/ui/toast_manager.cpp
src/ui/dialog_manager.cpp
src/ui/settings_panel.cpp
src/ui/combat_ui.cpp
src/ui/social_panel.cpp
src/ui/action_bar_panel.cpp
src/ui/window_manager.cpp
src/ui/inventory_screen.cpp
src/ui/quest_log_screen.cpp
src/ui/spellbook_screen.cpp

View file

@ -143,28 +143,25 @@ private:
void applyUpdateObjectBlock(const UpdateBlock& block, bool& newItemCreated);
void finalizeUpdateObjectBatch(bool newItemCreated);
// --- Phase 1: Extracted helper methods ---
bool extractPlayerAppearance(const std::map<uint16_t, uint32_t>& fields,
uint8_t& outRace, uint8_t& outGender,
uint32_t& outAppearanceBytes, uint8_t& outFacial) const;
void maybeDetectCoinageIndex(const std::map<uint16_t, uint32_t>& oldFields,
const std::map<uint16_t, uint32_t>& newFields);
// --- Phase 2: Update type handlers ---
void handleCreateObject(const UpdateBlock& block, bool& newItemCreated);
void handleValuesUpdate(const UpdateBlock& block);
void handleMovementUpdate(const UpdateBlock& block);
// --- Phase 3: Concern-specific helpers ---
// 3i: Update transport-relative child attachment (non-player entities).
// Update transport-relative child attachment (non-player entities).
// Consolidates identical logic from CREATE/VALUES/MOVEMENT handlers.
void updateNonPlayerTransportAttachment(const UpdateBlock& block,
const std::shared_ptr<Entity>& entity,
ObjectType entityType);
// 3f: Rebuild playerAuras_ from UNIT_FIELD_AURAS (Classic/vanilla only).
// Rebuild playerAuras_ from UNIT_FIELD_AURAS (Classic/vanilla only).
// Consolidates identical logic from CREATE and VALUES handlers.
void syncClassicAurasFromFields(const std::shared_ptr<Entity>& entity);
// 3h: Detect mount/dismount from UNIT_FIELD_MOUNTDISPLAYID changes (self-player only).
// Detect mount/dismount from UNIT_FIELD_MOUNTDISPLAYID changes (self-player only).
// Consolidates identical logic from CREATE and VALUES handlers.
void detectPlayerMountChange(uint32_t newMountDisplayId,
const std::map<uint16_t, uint32_t>& blockFields);
@ -172,7 +169,6 @@ private:
// Shared player-death handler: caches corpse position, sets death state.
void markPlayerDead(const char* source);
// --- Phase 4: Field index cache structs ---
// Cached field indices resolved once per handler call to avoid repeated lookups.
struct UnitFieldIndices {
uint16_t health, maxHealth, powerBase, maxPowerBase;
@ -202,43 +198,42 @@ private:
uint32_t oldDisplayId = 0;
};
// --- Phase 3: Extracted concern-specific helpers (continued) ---
// 3a: Entity factory — creates the correct Entity subclass for the given block.
// Entity factory — creates the correct Entity subclass for the given block.
std::shared_ptr<Entity> createEntityFromBlock(const UpdateBlock& block);
// 3b: Track player-on-transport state from movement blocks.
// Track player-on-transport state from movement blocks.
void applyPlayerTransportState(const UpdateBlock& block,
const std::shared_ptr<Entity>& entity,
const glm::vec3& canonicalPos, float oCanonical,
bool updateMovementInfoPos);
// 3c: Apply unit fields during CREATE — returns true if entity is initially dead.
// Apply unit fields during CREATE — returns true if entity is initially dead.
bool applyUnitFieldsOnCreate(const UpdateBlock& block,
std::shared_ptr<Unit>& unit,
const UnitFieldIndices& ufi);
// 3c: Apply unit fields during VALUES — returns change tracking result.
// Apply unit fields during VALUES — returns change tracking result.
UnitFieldUpdateResult applyUnitFieldsOnUpdate(const UpdateBlock& block,
const std::shared_ptr<Entity>& entity,
std::shared_ptr<Unit>& unit,
const UnitFieldIndices& ufi);
// 3d: Apply player stat fields (XP, inventory, skills, etc.). isCreate=true for CREATE path.
// Apply player stat fields (XP, inventory, skills, etc.). isCreate=true for CREATE path.
bool applyPlayerStatFields(const std::map<uint16_t, uint32_t>& fields,
const PlayerFieldIndices& pfi, bool isCreate);
// 3e: Dispatch spawn callbacks (creature/player) — deduplicates CREATE and VALUES paths.
// Dispatch spawn callbacks (creature/player) — deduplicates CREATE and VALUES paths.
void dispatchEntitySpawn(uint64_t guid, ObjectType objectType,
const std::shared_ptr<Entity>& entity,
const std::shared_ptr<Unit>& unit, bool isDead);
// 3g: Track item/container on CREATE.
// Track item/container on CREATE.
void trackItemOnCreate(const UpdateBlock& block, bool& newItemCreated);
// 3g: Update item fields on VALUES update.
// Update item fields on VALUES update.
void updateItemOnValuesUpdate(const UpdateBlock& block,
const std::shared_ptr<Entity>& entity);
// --- Phase 5: Strategy pattern — object-type handler interface ---
// Allows extending object-type handling without modifying handler dispatch.
struct IObjectTypeHandler {
virtual ~IObjectTypeHandler() = default;
virtual void onCreate(const UpdateBlock&, std::shared_ptr<Entity>&, bool&) {}
virtual void onValuesUpdate(const UpdateBlock&, std::shared_ptr<Entity>&) {}
virtual void onMovementUpdate(const UpdateBlock&, std::shared_ptr<Entity>&) {}
virtual void onCreate(const UpdateBlock& /*block*/, std::shared_ptr<Entity>& /*entity*/,
bool& /*newItemCreated*/) {}
virtual void onValuesUpdate(const UpdateBlock& /*block*/, std::shared_ptr<Entity>& /*entity*/) {}
virtual void onMovementUpdate(const UpdateBlock& /*block*/, std::shared_ptr<Entity>& /*entity*/) {}
};
struct UnitTypeHandler;
struct PlayerTypeHandler;
@ -249,7 +244,6 @@ private:
void initTypeHandlers();
IObjectTypeHandler* getTypeHandler(ObjectType type) const;
// --- Phase 5: Type-specific handler implementations (trampolined from handlers) ---
void onCreateUnit(const UpdateBlock& block, std::shared_ptr<Entity>& entity);
void onCreatePlayer(const UpdateBlock& block, std::shared_ptr<Entity>& entity);
void onCreateGameObject(const UpdateBlock& block, std::shared_ptr<Entity>& entity);
@ -264,7 +258,6 @@ private:
void onValuesUpdateItem(const UpdateBlock& block, std::shared_ptr<Entity>& entity);
void onValuesUpdateGameObject(const UpdateBlock& block, std::shared_ptr<Entity>& entity);
// --- Phase 6: Deferred event bus ---
// Collects addon events during block processing, flushes at the end.
struct PendingEvents {
std::vector<std::pair<std::string, std::vector<std::string>>> events;

View file

@ -627,7 +627,6 @@ public:
void resetWardenState(); // clear all warden module/crypto state for connect/disconnect
void clearUnitCaches(); // clear per-unit cast states and aura caches
// ---- Phase 1: Name queries (delegated to EntityController) ----
void queryPlayerName(uint64_t guid);
void queryCreatureInfo(uint32_t entry, uint64_t guid);
void queryGameObjectInfo(uint32_t entry, uint64_t guid);
@ -661,7 +660,6 @@ public:
return entityController_->getCreatureFamily(entry);
}
// ---- Phase 2: Combat (delegated to CombatHandler) ----
void startAutoAttack(uint64_t targetGuid);
void stopAutoAttack();
bool isAutoAttacking() const;
@ -696,7 +694,6 @@ public:
const std::vector<ThreatEntry>* getThreatList(uint64_t unitGuid) const;
const std::vector<ThreatEntry>* getTargetThreatList() const;
// ---- Phase 3: Spells ----
void castSpell(uint32_t spellId, uint64_t targetGuid = 0);
void cancelCast();
void cancelAura(uint32_t spellId);
@ -1239,7 +1236,7 @@ public:
void acceptResurrect();
void declineResurrect();
// ---- Phase 4: Group ----
// ---- Group ----
void inviteToGroup(const std::string& playerName);
void acceptGroupInvite();
void declineGroupInvite();
@ -1396,7 +1393,7 @@ public:
return nullptr;
}
// ---- Phase 5: Loot ----
// ---- Loot ----
void lootTarget(uint64_t guid);
void lootItem(uint8_t slotIndex);
void closeLoot();
@ -2177,7 +2174,6 @@ private:
*/
void handlePong(network::Packet& packet);
// ---- Phase 1 handlers (entity queries delegated to EntityController) ----
void handleItemQueryResponse(network::Packet& packet);
void queryItemInfo(uint32_t entry, uint64_t guid);
void rebuildOnlineInventory();
@ -2190,7 +2186,6 @@ private:
void extractContainerFields(uint64_t containerGuid, const std::map<uint16_t, uint32_t>& fields);
uint64_t resolveOnlineItemGuid(uint32_t itemId) const;
// ---- Phase 2 handlers (dead — dispatched via CombatHandler) ----
// handleAttackStart, handleAttackStop, handleAttackerStateUpdate,
// handleSpellDamageLog, handleSpellHealLog removed
@ -2198,12 +2193,6 @@ private:
void handleUpdateAuraDuration(uint8_t slot, uint32_t durationMs);
// handleSetForcedReactions — dispatched via CombatHandler
// ---- Phase 3 handlers ----
// ---- Talent handlers ----
// ---- Phase 4 handlers ----
// ---- Guild handlers ----
void handlePetSpells(network::Packet& packet);
@ -2217,7 +2206,6 @@ private:
// ---- Other player movement (MSG_MOVE_* from server) ----
// ---- Phase 5 handlers ----
void clearPendingQuestAccept(uint32_t questId);
void triggerQuestAcceptResync(uint32_t questId, uint64_t npcGuid, const char* reason);
bool hasQuestInLog(uint32_t questId) const;
@ -2411,7 +2399,6 @@ private:
uint32_t homeBindZoneId_ = 0;
glm::vec3 homeBindPos_{0.0f};
// ---- Phase 1: Name caches (moved to EntityController) ----
// ---- Friend/contact list cache ----
std::unordered_map<std::string, uint64_t> friendsCache; // name -> guid
@ -2510,11 +2497,10 @@ private:
std::unordered_set<uint64_t> pendingAutoInspect_;
float inspectRateLimit_ = 0.0f;
// ---- Phase 2: Combat (state moved to CombatHandler) ----
// ---- Combat ----
bool wasCombat_ = false; // Previous frame combat state for PLAYER_REGEN edge detection
std::deque<std::string> areaTriggerMsgs_;
// ---- Phase 3: Spells ----
WorldEntryCallback worldEntryCallback_;
KnockBackCallback knockBackCallback_;
CameraShakeCallback cameraShakeCallback_;
@ -2674,7 +2660,7 @@ private:
void loadFactionNameCache() const;
std::string getFactionName(uint32_t factionId) const;
// ---- Phase 4: Group ----
// ---- Group ----
GroupListData partyData;
bool pendingGroupInvite = false;
std::string pendingInviterName;
@ -2745,7 +2731,7 @@ private:
// Barber shop
bool barberShopOpen_ = false;
// ---- Phase 5: Loot ----
// ---- Loot ----
bool lootWindowOpen = false;
bool autoLoot_ = false;
bool autoSellGrey_ = false;

View file

@ -1460,7 +1460,7 @@ public:
};
// ============================================================
// Phase 1: Foundation — Targeting, Name Queries
// Foundation — Targeting, Name Queries
// ============================================================
/** CMSG_SET_SELECTION packet builder */
@ -1663,7 +1663,7 @@ public:
};
// ============================================================
// Phase 2: Combat Core
// Combat Core
// ============================================================
/** SMSG_MONSTER_MOVE data */
@ -1805,7 +1805,7 @@ public:
};
// ============================================================
// Phase 3: Spells, Action Bar, Auras
// Spells, Action Bar, Auras
// ============================================================
/** SMSG_INITIAL_SPELLS data */
@ -1930,7 +1930,7 @@ public:
};
// ============================================================
// Phase 4: Group/Party System
// Group/Party System
// ============================================================
/** CMSG_GROUP_INVITE packet builder */
@ -1998,7 +1998,7 @@ public:
};
// ============================================================
// Phase 5: Loot System
// Loot System
// ============================================================
/** Loot item entry */
@ -2100,7 +2100,7 @@ public:
};
// ============================================================
// Phase 5: NPC Gossip
// NPC Gossip
// ============================================================
/** Gossip menu option */
@ -2278,7 +2278,7 @@ public:
};
// ============================================================
// Phase 5: Vendor
// Vendor
// ============================================================
/** Vendor item entry */

View file

@ -0,0 +1,78 @@
// ============================================================
// ActionBarPanel — extracted from GameScreen
// Owns all action bar rendering: main bar, stance bar, bag bar,
// XP bar, reputation bar, macro resolution.
// ============================================================
#pragma once
#include <cstdint>
#include <unordered_map>
#include <functional>
#include <vulkan/vulkan.h>
namespace wowee {
namespace game { class GameHandler; }
namespace pipeline { class AssetManager; }
namespace ui {
class ChatPanel;
class SettingsPanel;
class InventoryScreen;
class SpellbookScreen;
class QuestLogScreen;
class ActionBarPanel {
public:
// Callback type for resolving spell icons (spellId, assetMgr) → VkDescriptorSet
using SpellIconFn = std::function<VkDescriptorSet(uint32_t, pipeline::AssetManager*)>;
// ---- Action bar render methods ----
void renderActionBar(game::GameHandler& gameHandler,
SettingsPanel& settingsPanel,
ChatPanel& chatPanel,
InventoryScreen& inventoryScreen,
SpellbookScreen& spellbookScreen,
QuestLogScreen& questLogScreen,
SpellIconFn getSpellIcon);
void renderStanceBar(game::GameHandler& gameHandler,
SettingsPanel& settingsPanel,
SpellbookScreen& spellbookScreen,
SpellIconFn getSpellIcon);
void renderBagBar(game::GameHandler& gameHandler,
SettingsPanel& settingsPanel,
InventoryScreen& inventoryScreen);
void renderXpBar(game::GameHandler& gameHandler,
SettingsPanel& settingsPanel);
void renderRepBar(game::GameHandler& gameHandler,
SettingsPanel& settingsPanel);
// ---- State owned by this panel ----
// Action bar error-flash: spellId → wall-clock time (seconds) when the flash ends
std::unordered_map<uint32_t, float> actionFlashEndTimes_;
static constexpr float kActionFlashDuration = 0.5f;
// Action bar drag state (-1 = not dragging)
int actionBarDragSlot_ = -1;
VkDescriptorSet actionBarDragIcon_ = VK_NULL_HANDLE;
// Bag bar state
VkDescriptorSet backpackIconTexture_ = VK_NULL_HANDLE;
VkDescriptorSet emptyBagSlotTexture_ = VK_NULL_HANDLE;
int bagBarPickedSlot_ = -1;
int bagBarDragSource_ = -1;
// Macro editor popup state
uint32_t macroEditorId_ = 0;
bool macroEditorOpen_ = false;
char macroEditorBuf_[256] = {};
// Macro cooldown cache: maps macro ID → resolved primary spell ID
std::unordered_map<uint32_t, uint32_t> macroPrimarySpellCache_;
size_t macroCacheSpellCount_ = 0;
private:
uint32_t resolveMacroPrimarySpellId(uint32_t macroId, game::GameHandler& gameHandler);
};
} // namespace ui
} // namespace wowee

194
include/ui/chat_panel.hpp Normal file
View file

@ -0,0 +1,194 @@
#pragma once
#include "game/game_handler.hpp"
#include <vulkan/vulkan.h>
#include <imgui.h>
#include <string>
#include <vector>
#include <functional>
namespace wowee {
namespace pipeline { class AssetManager; }
namespace rendering { class Renderer; }
namespace ui {
class InventoryScreen;
class SpellbookScreen;
class QuestLogScreen;
/**
* Self-contained chat UI panel extracted from GameScreen.
*
* Owns all chat state: input buffer, sent-history, tab filtering,
* slash-command parsing, chat bubbles, and chat-related settings.
*/
class ChatPanel {
public:
ChatPanel();
// ---- Main entry points (called by GameScreen) ----
/**
* Render the chat window (tabs, history, input, etc.)
*/
void render(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
SpellbookScreen& spellbookScreen,
QuestLogScreen& questLogScreen);
/**
* Render 3D-projected chat bubbles above entities.
*/
void renderBubbles(game::GameHandler& gameHandler);
/**
* Register one-shot callbacks on GameHandler (call once per session).
* Sets up the chat-bubble callback.
*/
void setupCallbacks(game::GameHandler& gameHandler);
// ---- Input helpers (called by GameScreen keybind handling) ----
bool isChatInputActive() const { return chatInputActive_; }
/** Insert a spell / item link into the chat input buffer (shift-click). */
void insertChatLink(const std::string& link);
/** Activate the input field with a leading '/' (slash key). */
void activateSlashInput();
/** Activate (focus) the input field (Enter key). */
void activateInput();
/** Request that the chat input be focused next frame. */
void requestRefocus() { refocusChatInput_ = true; }
/** Set up a whisper to the given player name and focus input. */
void setWhisperTarget(const std::string& name);
/** Execute a macro body (one line per 'click'). */
void executeMacroText(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
SpellbookScreen& spellbookScreen,
QuestLogScreen& questLogScreen,
const std::string& macroText);
// ---- Slash-command side-effects ----
// GameScreen reads these each frame, then clears them.
struct SlashCommands {
bool showInspect = false;
bool toggleThreat = false;
bool showBgScore = false;
bool showGmTicket = false;
bool showWho = false;
bool toggleCombatLog = false;
bool takeScreenshot = false;
};
/** Return accumulated slash-command flags and reset them. */
SlashCommands consumeSlashCommands();
// ---- Chat settings (read/written by GameScreen save/load & settings tab) ----
bool chatShowTimestamps = false;
int chatFontSize = 1; // 0=small, 1=medium, 2=large
bool chatAutoJoinGeneral = true;
bool chatAutoJoinTrade = true;
bool chatAutoJoinLocalDefense = true;
bool chatAutoJoinLFG = true;
bool chatAutoJoinLocal = true;
int activeChatTab = 0;
/** Spell icon lookup callback — set by GameScreen each frame before render(). */
std::function<VkDescriptorSet(uint32_t, pipeline::AssetManager*)> getSpellIcon;
/** Render the "Chat" tab inside the Settings window. */
void renderSettingsTab(std::function<void()> saveSettingsFn);
/** Reset all chat settings to defaults. */
void restoreDefaults();
/** Replace $g/$G and $n/$N gender/name placeholders in quest/chat text. */
std::string replaceGenderPlaceholders(const std::string& text, game::GameHandler& gameHandler);
private:
// ---- Chat input state ----
char chatInputBuffer_[512] = "";
char whisperTargetBuffer_[256] = "";
bool chatInputActive_ = false;
int selectedChatType_ = 0; // 0=SAY .. 10=CHANNEL
int lastChatType_ = 0;
int selectedChannelIdx_ = 0;
bool chatInputMoveCursorToEnd_ = false;
bool refocusChatInput_ = false;
// Sent-message history (Up/Down arrow recall)
std::vector<std::string> chatSentHistory_;
int chatHistoryIdx_ = -1;
// Macro stop flag
bool macroStopped_ = false;
// Tab-completion state
std::string chatTabPrefix_;
std::vector<std::string> chatTabMatches_;
int chatTabMatchIdx_ = -1;
// Mention notification
size_t chatMentionSeenCount_ = 0;
// ---- Chat tabs ----
struct ChatTab {
std::string name;
uint64_t typeMask;
};
std::vector<ChatTab> chatTabs_;
std::vector<int> chatTabUnread_;
size_t chatTabSeenCount_ = 0;
void initChatTabs();
bool shouldShowMessage(const game::MessageChatData& msg, int tabIndex) const;
// ---- Chat window visual state ----
bool chatScrolledUp_ = false;
bool chatForceScrollToBottom_ = false;
bool chatWindowLocked_ = true;
ImVec2 chatWindowPos_ = ImVec2(0.0f, 0.0f);
bool chatWindowPosInit_ = false;
// ---- Chat bubbles ----
struct ChatBubble {
uint64_t senderGuid = 0;
std::string message;
float timeRemaining = 0.0f;
float totalDuration = 0.0f;
bool isYell = false;
};
std::vector<ChatBubble> chatBubbles_;
bool chatBubbleCallbackSet_ = false;
// ---- Whisper toast state (populated in render, rendered by GameScreen/ToastManager) ----
// Whisper scanning lives here because it's tightly coupled to chat history iteration.
size_t whisperSeenCount_ = 0;
// ---- Helpers ----
void sendChatMessage(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
SpellbookScreen& spellbookScreen,
QuestLogScreen& questLogScreen);
const char* getChatTypeName(game::ChatType type) const;
ImVec4 getChatTypeColor(game::ChatType type) const;
// Cached game handler for input callback (set each frame in render)
game::GameHandler* cachedGameHandler_ = nullptr;
// Join channel input buffer
char joinChannelBuffer_[128] = "";
// Slash command flags (accumulated, consumed by GameScreen)
SlashCommands slashCmds_;
};
} // namespace ui
} // namespace wowee

76
include/ui/combat_ui.hpp Normal file
View file

@ -0,0 +1,76 @@
#pragma once
#include <imgui.h>
#include <vulkan/vulkan.h>
#include <string>
#include <vector>
#include <cstdint>
#include <functional>
namespace wowee {
namespace game { class GameHandler; }
namespace pipeline { class AssetManager; }
namespace ui {
class SettingsPanel;
class SpellbookScreen;
/**
* Combat UI overlay manager (extracted from GameScreen)
*
* Owns all combat-related rendering:
* cast bar, cooldown tracker, raid warning overlay, floating combat text,
* DPS/HPS meter, buff bar, battleground score HUD, combat log,
* threat window, BG scoreboard.
*/
class CombatUI {
public:
CombatUI() = default;
// ---- Callback type for spell icon lookup (stays in GameScreen) ----
using SpellIconFn = std::function<VkDescriptorSet(uint32_t spellId, pipeline::AssetManager*)>;
// ---- Toggle booleans (written by slash commands / escape handler / settings) ----
bool showCombatLog_ = false;
bool showThreatWindow_ = false;
bool showBgScoreboard_ = false;
// ---- Raid Warning / Boss Emote big-text overlay ----
struct RaidWarnEntry {
std::string text;
float age = 0.0f;
bool isBossEmote = false;
static constexpr float LIFETIME = 5.0f;
};
std::vector<RaidWarnEntry> raidWarnEntries_;
bool raidWarnCallbackSet_ = false;
size_t raidWarnChatSeenCount_ = 0;
// ---- DPS meter state ----
float dpsCombatAge_ = 0.0f;
bool dpsWasInCombat_ = false;
float dpsEncounterDamage_ = 0.0f;
float dpsEncounterHeal_ = 0.0f;
size_t dpsLogSeenCount_ = 0;
// ---- Public render methods ----
void renderCastBar(game::GameHandler& gameHandler, SpellIconFn getSpellIcon);
void renderCooldownTracker(game::GameHandler& gameHandler,
const SettingsPanel& settings,
SpellIconFn getSpellIcon);
void renderRaidWarningOverlay(game::GameHandler& gameHandler);
void renderCombatText(game::GameHandler& gameHandler);
void renderDPSMeter(game::GameHandler& gameHandler,
const SettingsPanel& settings);
void renderBuffBar(game::GameHandler& gameHandler,
SpellbookScreen& spellbookScreen,
SpellIconFn getSpellIcon);
void renderBattlegroundScore(game::GameHandler& gameHandler);
void renderCombatLog(game::GameHandler& gameHandler,
SpellbookScreen& spellbookScreen);
void renderThreatWindow(game::GameHandler& gameHandler);
void renderBgScoreboard(game::GameHandler& gameHandler);
};
} // namespace ui
} // namespace wowee

View file

@ -0,0 +1,71 @@
#pragma once
#include <imgui.h>
#include <string>
#include <cstdint>
namespace wowee {
namespace game { class GameHandler; }
namespace ui {
class ChatPanel;
class InventoryScreen;
/**
* Dialog / popup overlay manager
*
* Owns all yes/no popup rendering:
* group invite, duel request, duel countdown, loot roll, trade request,
* trade window, summon request, shared quest, item text, guild invite,
* ready check, BG invite, BF manager invite, LFG proposal, LFG role check,
* resurrect, talent wipe confirm, pet unlearn confirm.
*/
class DialogManager {
public:
DialogManager() = default;
/// Render "early" dialogs (group invite through LFG role check)
/// called in render() before guild roster / social frame
void renderDialogs(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
/// Render "late" dialogs (resurrect, talent wipe, pet unlearn)
/// called in render() after reclaim corpse button
void renderLateDialogs(game::GameHandler& gameHandler);
private:
// Common ImGui window flags for popup dialogs
static constexpr ImGuiWindowFlags kDialogFlags =
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize;
// ---- LFG role state ----
uint8_t lfgRoles_ = 0x08; // default: DPS (0x02=tank, 0x04=healer, 0x08=dps)
// ---- Individual dialog renderers ----
void renderGroupInvitePopup(game::GameHandler& gameHandler);
void renderDuelRequestPopup(game::GameHandler& gameHandler);
void renderDuelCountdown(game::GameHandler& gameHandler);
void renderItemTextWindow(game::GameHandler& gameHandler);
void renderSharedQuestPopup(game::GameHandler& gameHandler);
void renderSummonRequestPopup(game::GameHandler& gameHandler);
void renderTradeRequestPopup(game::GameHandler& gameHandler);
void renderTradeWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderLootRollPopup(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderGuildInvitePopup(game::GameHandler& gameHandler);
void renderReadyCheckPopup(game::GameHandler& gameHandler);
void renderBgInvitePopup(game::GameHandler& gameHandler);
void renderBfMgrInvitePopup(game::GameHandler& gameHandler);
void renderLfgProposalPopup(game::GameHandler& gameHandler);
void renderLfgRoleCheckPopup(game::GameHandler& gameHandler);
void renderResurrectDialog(game::GameHandler& gameHandler);
void renderTalentWipeConfirmDialog(game::GameHandler& gameHandler);
void renderPetUnlearnConfirmDialog(game::GameHandler& gameHandler);
};
} // namespace ui
} // namespace wowee

View file

@ -9,6 +9,14 @@
#include "ui/spellbook_screen.hpp"
#include "ui/talent_screen.hpp"
#include "ui/keybinding_manager.hpp"
#include "ui/chat_panel.hpp"
#include "ui/toast_manager.hpp"
#include "ui/dialog_manager.hpp"
#include "ui/settings_panel.hpp"
#include "ui/combat_ui.hpp"
#include "ui/social_panel.hpp"
#include "ui/action_bar_panel.hpp"
#include "ui/window_manager.hpp"
#include <vulkan/vulkan.h>
#include <imgui.h>
#include <string>
@ -37,82 +45,46 @@ public:
/**
* Check if chat input is active
*/
bool isChatInputActive() const { return chatInputActive; }
bool isChatInputActive() const { return chatPanel_.isChatInputActive(); }
void saveSettings();
void loadSettings();
void applyAudioVolumes(rendering::Renderer* renderer);
private:
// Chat state
char chatInputBuffer[512] = "";
char whisperTargetBuffer[256] = "";
bool chatInputActive = false;
int selectedChatType = 0; // 0=SAY, 1=YELL, 2=PARTY, 3=GUILD, 4=WHISPER, ..., 10=CHANNEL
int lastChatType = 0; // Track chat type changes
int selectedChannelIdx = 0; // Index into joinedChannels_ when selectedChatType==10
bool chatInputMoveCursorToEnd = false;
// Chat panel (extracted from GameScreen — owns all chat state and rendering)
ChatPanel chatPanel_;
// Chat sent-message history (Up/Down arrow recall)
std::vector<std::string> chatSentHistory_;
int chatHistoryIdx_ = -1; // -1 = not browsing history
// Toast manager (extracted from GameScreen — owns all toast/notification state and rendering)
ToastManager toastManager_;
// Set to true by /stopmacro; checked in executeMacroText to halt remaining commands.
bool macroStopped_ = false;
// Dialog manager (extracted from GameScreen — owns all popup/dialog rendering)
DialogManager dialogManager_;
// Action bar error-flash: spellId → wall-clock time (seconds) when the flash ends.
// Populated by the SpellCastFailedCallback; queried during action bar button rendering.
std::unordered_map<uint32_t, float> actionFlashEndTimes_;
// Settings panel (extracted from GameScreen — owns all settings UI and config state)
SettingsPanel settingsPanel_;
// Cached game handler for input callbacks (set each frame in render)
game::GameHandler* cachedGameHandler_ = nullptr;
// Combat UI (extracted from GameScreen — owns all combat overlay rendering)
CombatUI combatUI_;
// Tab-completion state for slash commands and player names
std::string chatTabPrefix_; // prefix captured on first Tab press
std::vector<std::string> chatTabMatches_; // matching command list
int chatTabMatchIdx_ = -1; // active match index (-1 = inactive)
// Social panel (extracted from GameScreen — owns all social/group UI rendering)
SocialPanel socialPanel_;
// Mention notification: plays a sound when the player's name appears in chat
size_t chatMentionSeenCount_ = 0; // how many messages have been scanned for mentions
// Action bar panel (extracted from GameScreen — owns action/stance/bag/xp/rep bars)
ActionBarPanel actionBarPanel_;
// Chat tabs
int activeChatTab_ = 0;
struct ChatTab {
std::string name;
uint64_t typeMask; // bitmask of ChatType values to show (64-bit: types go up to 84)
};
std::vector<ChatTab> chatTabs_;
std::vector<int> chatTabUnread_; // unread message count per tab (0 = none)
size_t chatTabSeenCount_ = 0; // how many history messages have been processed
void initChatTabs();
bool shouldShowMessage(const game::MessageChatData& msg, int tabIndex) const;
// Window manager (extracted from GameScreen — owns NPC windows, popups, overlays)
WindowManager windowManager_;
// UI state
bool showEntityWindow = false;
bool showChatWindow = true;
bool showMinimap_ = true; // M key toggles minimap
bool showNameplates_ = true; // V key toggles enemy/NPC nameplates
bool showFriendlyNameplates_ = true; // Shift+V toggles friendly player nameplates
float nameplateScale_ = 1.0f; // Scale multiplier for nameplate bar dimensions
uint64_t nameplateCtxGuid_ = 0; // GUID of nameplate right-clicked (0 = none)
ImVec2 nameplateCtxPos_{}; // Screen position of nameplate right-click
uint32_t lastPlayerHp_ = 0; // Previous frame HP for damage flash detection
float damageFlashAlpha_ = 0.0f; // Screen edge flash intensity (fades to 0)
bool damageFlashEnabled_ = true;
bool lowHealthVignetteEnabled_ = true; // Persistent pulsing red vignette below 20% HP
float levelUpFlashAlpha_ = 0.0f; // Golden level-up burst effect (fades to 0)
uint32_t levelUpDisplayLevel_ = 0; // Level shown in level-up text
// Raid Warning / Boss Emote big-text overlay (center-screen, fades after 5s)
struct RaidWarnEntry {
std::string text;
float age = 0.0f;
bool isBossEmote = false; // true = amber, false (raid warning) = red+yellow
static constexpr float LIFETIME = 5.0f;
};
std::vector<RaidWarnEntry> raidWarnEntries_;
bool raidWarnCallbackSet_ = false;
size_t raidWarnChatSeenCount_ = 0; // index into chat history for unread scan
// UIErrorsFrame: WoW-style center-bottom error messages (spell fails, out of range, etc.)
struct UIErrorEntry { std::string text; float age = 0.0f; };
@ -120,161 +92,15 @@ private:
bool uiErrorCallbackSet_ = false;
static constexpr float kUIErrorLifetime = 2.5f;
bool castFailedCallbackSet_ = false;
static constexpr float kActionFlashDuration = 0.5f; // seconds for error-red overlay to fade
// Reputation change toast: brief colored slide-in below minimap
struct RepToastEntry { std::string factionName; int32_t delta = 0; int32_t standing = 0; float age = 0.0f; };
std::vector<RepToastEntry> repToasts_;
bool repChangeCallbackSet_ = false;
static constexpr float kRepToastLifetime = 3.5f;
// Quest completion toast: slide-in when a quest is turned in
struct QuestCompleteToastEntry { uint32_t questId = 0; std::string title; float age = 0.0f; };
std::vector<QuestCompleteToastEntry> questCompleteToasts_;
bool questCompleteCallbackSet_ = false;
static constexpr float kQuestCompleteToastLifetime = 4.0f;
// Zone entry toast: brief banner when entering a new zone
struct ZoneToastEntry { std::string zoneName; float age = 0.0f; };
std::vector<ZoneToastEntry> zoneToasts_;
struct AreaTriggerToast { std::string text; float age = 0.0f; };
std::vector<AreaTriggerToast> areaTriggerToasts_;
void renderAreaTriggerToasts(float deltaTime, game::GameHandler& gameHandler);
std::string lastKnownZone_;
static constexpr float kZoneToastLifetime = 3.0f;
// Death screen: elapsed time since the death dialog first appeared
float deathElapsed_ = 0.0f;
bool deathTimerRunning_ = false;
// WoW forces release after ~6 minutes; show countdown until then
static constexpr float kForcedReleaseSec = 360.0f;
void renderZoneToasts(float deltaTime);
bool showPlayerInfo = false;
bool showSocialFrame_ = false; // O key toggles social/friends list
bool showGuildRoster_ = false;
bool showRaidFrames_ = true; // F key toggles raid/party frames
bool showWorldMap_ = false; // W key toggles world map
std::string selectedGuildMember_;
bool showGuildNoteEdit_ = false;
bool editingOfficerNote_ = false;
char guildNoteEditBuffer_[256] = {0};
int guildRosterTab_ = 0; // 0=Roster, 1=Guild Info
char guildMotdEditBuffer_[256] = {0};
bool showMotdEdit_ = false;
char petitionNameBuffer_[64] = {0};
char addRankNameBuffer_[64] = {0};
bool showAddRankModal_ = false;
bool refocusChatInput = false;
bool vendorBagsOpened_ = false; // Track if bags were auto-opened for current vendor session
bool chatScrolledUp_ = false; // true when user has scrolled above the latest messages
bool chatForceScrollToBottom_ = false; // set to true to jump to bottom next frame
bool chatWindowLocked = true;
ImVec2 chatWindowPos_ = ImVec2(0.0f, 0.0f);
bool chatWindowPosInit_ = false;
ImVec2 questTrackerPos_ = ImVec2(-1.0f, -1.0f); // <0 = use default
ImVec2 questTrackerSize_ = ImVec2(220.0f, 200.0f); // saved size
float questTrackerRightOffset_ = -1.0f; // pixels from right edge; <0 = use default
bool questTrackerPosInit_ = false;
bool showEscapeMenu = false;
bool showEscapeSettingsNotice = false;
bool showSettingsWindow = false;
bool settingsInit = false;
bool pendingFullscreen = false;
bool pendingVsync = false;
int pendingResIndex = 0;
bool pendingShadows = true;
float pendingShadowDistance = 300.0f;
bool pendingWaterRefraction = true;
int pendingBrightness = 50; // 0-100, maps to 0.0-2.0 (50 = 1.0 default)
int pendingMasterVolume = 100;
int pendingMusicVolume = 30;
int pendingAmbientVolume = 100;
int pendingUiVolume = 100;
int pendingCombatVolume = 100;
int pendingSpellVolume = 100;
int pendingMovementVolume = 100;
int pendingFootstepVolume = 100;
int pendingNpcVoiceVolume = 100;
int pendingMountVolume = 100;
int pendingActivityVolume = 100;
float pendingMouseSensitivity = 0.2f;
bool pendingInvertMouse = false;
bool pendingExtendedZoom = false;
float pendingCameraStiffness = 30.0f; // Camera smooth speed (higher = tighter, less sway)
float pendingPivotHeight = 1.6f; // Camera pivot height above feet (lower = less detached feel)
float pendingFov = 70.0f; // degrees, default matches WoW's ~70° horizontal FOV
int pendingUiOpacity = 65;
bool pendingMinimapRotate = false;
bool pendingMinimapSquare = false;
bool pendingMinimapNpcDots = false;
bool pendingShowLatencyMeter = true;
bool pendingSeparateBags = true;
bool pendingShowKeyring = true;
bool pendingAutoLoot = false;
bool pendingAutoSellGrey = false;
bool pendingAutoRepair = false;
// Keybinding customization
int pendingRebindAction = -1; // -1 = not rebinding, otherwise action index
bool awaitingKeyPress = false;
// Macro editor popup state
uint32_t macroEditorId_ = 0; // macro index being edited
bool macroEditorOpen_ = false; // deferred OpenPopup flag
char macroEditorBuf_[256] = {}; // edit buffer
bool pendingUseOriginalSoundtrack = true;
bool pendingShowActionBar2 = true; // Show second action bar above main bar
float pendingActionBarScale = 1.0f; // Multiplier for action bar slot size (0.51.5)
float pendingActionBar2OffsetX = 0.0f; // Horizontal offset from default center position
float pendingActionBar2OffsetY = 0.0f; // Vertical offset from default (above bar 1)
bool pendingShowRightBar = false; // Right-edge vertical action bar (bar 3, slots 24-35)
bool pendingShowLeftBar = false; // Left-edge vertical action bar (bar 4, slots 36-47)
float pendingRightBarOffsetY = 0.0f; // Vertical offset from screen center
float pendingLeftBarOffsetY = 0.0f; // Vertical offset from screen center
int pendingGroundClutterDensity = 100;
int pendingAntiAliasing = 0; // 0=Off, 1=2x, 2=4x, 3=8x
bool pendingFXAA = false; // FXAA post-process (combinable with MSAA)
bool pendingNormalMapping = true; // on by default
float pendingNormalMapStrength = 0.8f; // 0.0-2.0
bool pendingPOM = true; // on by default
int pendingPOMQuality = 1; // 0=Low(16), 1=Medium(32), 2=High(64)
bool pendingFSR = false;
int pendingUpscalingMode = 0; // 0=Off, 1=FSR1, 2=FSR3
int pendingFSRQuality = 3; // 0=UltraQuality, 1=Quality, 2=Balanced, 3=Native(100%)
float pendingFSRSharpness = 1.6f;
float pendingFSR2JitterSign = 0.38f;
float pendingFSR2MotionVecScaleX = 1.0f;
float pendingFSR2MotionVecScaleY = 1.0f;
bool pendingAMDFramegen = false;
bool fsrSettingsApplied_ = false;
// Graphics quality presets
enum class GraphicsPreset : int {
CUSTOM = 0,
LOW = 1,
MEDIUM = 2,
HIGH = 3,
ULTRA = 4
};
GraphicsPreset currentGraphicsPreset = GraphicsPreset::CUSTOM;
GraphicsPreset pendingGraphicsPreset = GraphicsPreset::CUSTOM;
// UI element transparency (0.0 = fully transparent, 1.0 = fully opaque)
float uiOpacity_ = 0.65f;
bool minimapRotate_ = false;
bool minimapSquare_ = false;
bool minimapNpcDots_ = false;
bool showLatencyMeter_ = true; // Show server latency indicator
bool minimapSettingsApplied_ = false;
bool volumeSettingsApplied_ = false; // True once saved volume settings applied to audio managers
bool msaaSettingsApplied_ = false; // True once saved MSAA setting applied to renderer
bool fxaaSettingsApplied_ = false; // True once saved FXAA setting applied to renderer
bool waterRefractionApplied_ = false;
bool normalMapSettingsApplied_ = false; // True once saved normal map/POM settings applied
// Mute state: mute bypasses master volume without touching slider values
bool soundMuted_ = false;
float preMuteVolume_ = 1.0f; // AudioEngine master volume before muting
/**
* Render player info window
@ -286,27 +112,6 @@ private:
*/
void renderEntityList(game::GameHandler& gameHandler);
/**
* Render chat window
*/
void renderChatWindow(game::GameHandler& gameHandler);
/**
* Send chat message
*/
void sendChatMessage(game::GameHandler& gameHandler);
void executeMacroText(game::GameHandler& gameHandler, const std::string& macroText);
/**
* Get chat type name
*/
const char* getChatTypeName(game::ChatType type) const;
/**
* Get chat type color
*/
ImVec4 getChatTypeColor(game::ChatType type) const;
/**
* Render player unit frame (top-left)
*/
@ -339,80 +144,13 @@ private:
*/
void updateCharacterTextures(game::Inventory& inventory);
// ---- New UI renders ----
void renderActionBar(game::GameHandler& gameHandler);
void renderStanceBar(game::GameHandler& gameHandler);
void renderBagBar(game::GameHandler& gameHandler);
void renderXpBar(game::GameHandler& gameHandler);
void renderRepBar(game::GameHandler& gameHandler);
void renderCastBar(game::GameHandler& gameHandler);
void renderMirrorTimers(game::GameHandler& gameHandler);
void renderCooldownTracker(game::GameHandler& gameHandler);
void renderCombatText(game::GameHandler& gameHandler);
void renderRaidWarningOverlay(game::GameHandler& gameHandler);
void renderPartyFrames(game::GameHandler& gameHandler);
void renderBossFrames(game::GameHandler& gameHandler);
void renderUIErrors(game::GameHandler& gameHandler, float deltaTime);
void renderRepToasts(float deltaTime);
void renderQuestCompleteToasts(float deltaTime);
void renderGroupInvitePopup(game::GameHandler& gameHandler);
void renderDuelRequestPopup(game::GameHandler& gameHandler);
void renderDuelCountdown(game::GameHandler& gameHandler);
void renderLootRollPopup(game::GameHandler& gameHandler);
void renderTradeRequestPopup(game::GameHandler& gameHandler);
void renderTradeWindow(game::GameHandler& gameHandler);
void renderSummonRequestPopup(game::GameHandler& gameHandler);
void renderSharedQuestPopup(game::GameHandler& gameHandler);
void renderItemTextWindow(game::GameHandler& gameHandler);
void renderBuffBar(game::GameHandler& gameHandler);
void renderSocialFrame(game::GameHandler& gameHandler);
void renderLootWindow(game::GameHandler& gameHandler);
void renderGossipWindow(game::GameHandler& gameHandler);
void renderQuestDetailsWindow(game::GameHandler& gameHandler);
void renderQuestRequestItemsWindow(game::GameHandler& gameHandler);
void renderQuestOfferRewardWindow(game::GameHandler& gameHandler);
void renderVendorWindow(game::GameHandler& gameHandler);
void renderTrainerWindow(game::GameHandler& gameHandler);
void renderBarberShopWindow(game::GameHandler& gameHandler);
void renderStableWindow(game::GameHandler& gameHandler);
void renderTaxiWindow(game::GameHandler& gameHandler);
void renderLogoutCountdown(game::GameHandler& gameHandler);
void renderDeathScreen(game::GameHandler& gameHandler);
void renderReclaimCorpseButton(game::GameHandler& gameHandler);
void renderResurrectDialog(game::GameHandler& gameHandler);
void renderTalentWipeConfirmDialog(game::GameHandler& gameHandler);
void renderPetUnlearnConfirmDialog(game::GameHandler& gameHandler);
void renderEscapeMenu();
void renderSettingsWindow();
void renderSettingsAudioTab();
void renderSettingsChatTab();
void renderSettingsAboutTab();
void renderSettingsInterfaceTab();
void renderSettingsGameplayTab();
void renderSettingsControlsTab();
void applyGraphicsPreset(GraphicsPreset preset);
void updateGraphicsPresetFromCurrentSettings();
void renderQuestMarkers(game::GameHandler& gameHandler);
void renderMinimapMarkers(game::GameHandler& gameHandler);
void renderQuestObjectiveTracker(game::GameHandler& gameHandler);
void renderGuildRoster(game::GameHandler& gameHandler);
void renderGuildInvitePopup(game::GameHandler& gameHandler);
void renderReadyCheckPopup(game::GameHandler& gameHandler);
void renderBgInvitePopup(game::GameHandler& gameHandler);
void renderBfMgrInvitePopup(game::GameHandler& gameHandler);
void renderLfgProposalPopup(game::GameHandler& gameHandler);
void renderLfgRoleCheckPopup(game::GameHandler& gameHandler);
void renderChatBubbles(game::GameHandler& gameHandler);
void renderMailWindow(game::GameHandler& gameHandler);
void renderMailComposeWindow(game::GameHandler& gameHandler);
void renderBankWindow(game::GameHandler& gameHandler);
void renderGuildBankWindow(game::GameHandler& gameHandler);
void renderAuctionHouseWindow(game::GameHandler& gameHandler);
void renderDungeonFinderWindow(game::GameHandler& gameHandler);
void renderInstanceLockouts(game::GameHandler& gameHandler);
void renderNameplates(game::GameHandler& gameHandler);
void renderBattlegroundScore(game::GameHandler& gameHandler);
void renderDPSMeter(game::GameHandler& gameHandler);
void renderDurabilityWarning(game::GameHandler& gameHandler);
void takeScreenshot(game::GameHandler& gameHandler);
@ -437,298 +175,29 @@ private:
bool spellIconDbLoaded_ = false;
VkDescriptorSet getSpellIcon(uint32_t spellId, pipeline::AssetManager* am);
// ItemExtendedCost.dbc cache: extendedCostId -> cost details
struct ExtendedCostEntry {
uint32_t honorPoints = 0;
uint32_t arenaPoints = 0;
uint32_t itemId[5] = {};
uint32_t itemCount[5] = {};
};
std::unordered_map<uint32_t, ExtendedCostEntry> extendedCostCache_;
bool extendedCostDbLoaded_ = false;
void loadExtendedCostDBC();
std::string formatExtendedCost(uint32_t extendedCostId, game::GameHandler& gameHandler);
// Macro cooldown cache: maps macro ID → resolved primary spell ID (0 = no spell found)
std::unordered_map<uint32_t, uint32_t> macroPrimarySpellCache_;
size_t macroCacheSpellCount_ = 0; // invalidates cache when spell list changes
uint32_t resolveMacroPrimarySpellId(uint32_t macroId, game::GameHandler& gameHandler);
// Death Knight rune bar: client-predicted fill (0.0=depleted, 1.0=ready) for smooth animation
float runeClientFill_[6] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
// Action bar drag state (-1 = not dragging)
int actionBarDragSlot_ = -1;
VkDescriptorSet actionBarDragIcon_ = VK_NULL_HANDLE;
// Bag bar state
VkDescriptorSet backpackIconTexture_ = VK_NULL_HANDLE;
VkDescriptorSet emptyBagSlotTexture_ = VK_NULL_HANDLE;
int bagBarPickedSlot_ = -1; // Visual drag in progress (-1 = none)
int bagBarDragSource_ = -1; // Mouse pressed on this slot, waiting for drag or click (-1 = none)
// Who Results window
bool showWhoWindow_ = false;
void renderWhoWindow(game::GameHandler& gameHandler);
// Combat Log window
bool showCombatLog_ = false;
void renderCombatLog(game::GameHandler& gameHandler);
// Instance Lockouts window
bool showInstanceLockouts_ = false;
// Dungeon Finder state
bool showDungeonFinder_ = false;
// Achievements window
bool showAchievementWindow_ = false;
char achievementSearchBuf_[128] = {};
void renderAchievementWindow(game::GameHandler& gameHandler);
// Skills / Professions window (K key)
bool showSkillsWindow_ = false;
void renderSkillsWindow(game::GameHandler& gameHandler);
// Titles window
bool showTitlesWindow_ = false;
void renderTitlesWindow(game::GameHandler& gameHandler);
// Equipment Set Manager window
bool showEquipSetWindow_ = false;
void renderEquipSetWindow(game::GameHandler& gameHandler);
// GM Ticket window
bool showGmTicketWindow_ = false;
bool gmTicketWindowWasOpen_ = false; ///< Previous frame state; used to fire one-shot query
char gmTicketBuf_[2048] = {};
void renderGmTicketWindow(game::GameHandler& gameHandler);
// Pet rename modal (triggered from pet frame context menu)
bool petRenameOpen_ = false;
char petRenameBuf_[16] = {};
// Inspect window
bool showInspectWindow_ = false;
void renderInspectWindow(game::GameHandler& gameHandler);
// Readable text window (books / scrolls / notes)
bool showBookWindow_ = false;
int bookCurrentPage_ = 0;
void renderBookWindow(game::GameHandler& gameHandler);
// Threat window
bool showThreatWindow_ = false;
void renderThreatWindow(game::GameHandler& gameHandler);
// BG scoreboard window
bool showBgScoreboard_ = false;
void renderBgScoreboard(game::GameHandler& gameHandler);
uint8_t lfgRoles_ = 0x08; // default: DPS (0x02=tank, 0x04=healer, 0x08=dps)
uint32_t lfgSelectedDungeon_ = 861; // default: random dungeon (entry 861 = Random Dungeon WotLK)
// Chat settings
bool chatShowTimestamps_ = false;
int chatFontSize_ = 1; // 0=small, 1=medium, 2=large
bool chatAutoJoinGeneral_ = true;
bool chatAutoJoinTrade_ = true;
bool chatAutoJoinLocalDefense_ = true;
bool chatAutoJoinLFG_ = true;
bool chatAutoJoinLocal_ = true;
// Join channel input buffer
char joinChannelBuffer_[128] = "";
static std::string getSettingsPath();
// Gender placeholder replacement
std::string replaceGenderPlaceholders(const std::string& text, game::GameHandler& gameHandler);
// Chat bubbles
struct ChatBubble {
uint64_t senderGuid = 0;
std::string message;
float timeRemaining = 0.0f;
float totalDuration = 0.0f;
bool isYell = false;
};
std::vector<ChatBubble> chatBubbles_;
bool chatBubbleCallbackSet_ = false;
bool levelUpCallbackSet_ = false;
bool achievementCallbackSet_ = false;
// Mail compose state
char mailRecipientBuffer_[256] = "";
char mailSubjectBuffer_[256] = "";
char mailBodyBuffer_[2048] = "";
int mailComposeMoney_[3] = {0, 0, 0}; // gold, silver, copper
// Vendor search filter
char vendorSearchFilter_[128] = "";
// Vendor purchase confirmation for expensive items
bool vendorConfirmOpen_ = false;
uint64_t vendorConfirmGuid_ = 0;
uint32_t vendorConfirmItemId_ = 0;
uint32_t vendorConfirmSlot_ = 0;
uint32_t vendorConfirmQty_ = 1;
uint32_t vendorConfirmPrice_ = 0;
std::string vendorConfirmItemName_;
// Barber shop UI state
int barberHairStyle_ = 0;
int barberHairColor_ = 0;
int barberFacialHair_ = 0;
int barberOrigHairStyle_ = 0;
int barberOrigHairColor_ = 0;
int barberOrigFacialHair_ = 0;
bool barberInitialized_ = false;
// Trainer search filter
char trainerSearchFilter_[128] = "";
// Auction house UI state
char auctionSearchName_[256] = "";
int auctionLevelMin_ = 0;
int auctionLevelMax_ = 0;
int auctionQuality_ = 0;
int auctionSellDuration_ = 2; // 0=12h, 1=24h, 2=48h
int auctionSellBid_[3] = {0, 0, 0}; // gold, silver, copper
int auctionSellBuyout_[3] = {0, 0, 0}; // gold, silver, copper
int auctionSelectedItem_ = -1;
int auctionSellSlotIndex_ = -1; // Selected backpack slot for selling
uint32_t auctionBrowseOffset_ = 0; // Pagination offset for browse results
int auctionItemClass_ = -1; // Item class filter (-1 = All)
int auctionItemSubClass_ = -1; // Item subclass filter (-1 = All)
bool auctionUsableOnly_ = false; // Filter to items usable by current class/level
// Guild bank money input
int guildBankMoneyInput_[3] = {0, 0, 0}; // gold, silver, copper
// Left-click targeting: distinguish click from camera drag
glm::vec2 leftClickPressPos_ = glm::vec2(0.0f);
bool leftClickWasPress_ = false;
// Level-up ding animation
static constexpr float DING_DURATION = 4.0f;
float dingTimer_ = 0.0f;
uint32_t dingLevel_ = 0;
uint32_t dingHpDelta_ = 0;
uint32_t dingManaDelta_ = 0;
uint32_t dingStats_[5] = {}; // str/agi/sta/int/spi deltas
void renderDingEffect();
// Achievement toast banner
static constexpr float ACHIEVEMENT_TOAST_DURATION = 5.0f;
float achievementToastTimer_ = 0.0f;
uint32_t achievementToastId_ = 0;
std::string achievementToastName_;
void renderAchievementToast();
// Area discovery toast ("Discovered! <AreaName> +XP XP")
static constexpr float DISCOVERY_TOAST_DURATION = 4.0f;
float discoveryToastTimer_ = 0.0f;
std::string discoveryToastName_;
uint32_t discoveryToastXP_ = 0;
bool areaDiscoveryCallbackSet_ = false;
void renderDiscoveryToast();
// Whisper toast — brief overlay at screen top when a whisper arrives while chat is not focused
struct WhisperToastEntry {
std::string sender;
std::string preview; // first ~60 chars of message
float age = 0.0f;
};
static constexpr float WHISPER_TOAST_DURATION = 5.0f;
std::vector<WhisperToastEntry> whisperToasts_;
size_t whisperSeenCount_ = 0; // how many chat entries have been scanned for whispers
void renderWhisperToasts();
// Quest objective progress toast ("Quest: <ObjectiveName> X/Y")
struct QuestProgressToastEntry {
std::string questTitle;
std::string objectiveName;
uint32_t current = 0;
uint32_t required = 0;
float age = 0.0f;
};
static constexpr float QUEST_TOAST_DURATION = 4.0f;
std::vector<QuestProgressToastEntry> questToasts_;
bool questProgressCallbackSet_ = false;
void renderQuestProgressToasts();
// Nearby player level-up toast ("<Name> is now level X!")
struct PlayerLevelUpToastEntry {
uint64_t guid = 0;
std::string playerName; // resolved lazily at render time
uint32_t newLevel = 0;
float age = 0.0f;
};
static constexpr float PLAYER_LEVELUP_TOAST_DURATION = 4.0f;
std::vector<PlayerLevelUpToastEntry> playerLevelUpToasts_;
bool otherPlayerLevelUpCallbackSet_ = false;
void renderPlayerLevelUpToasts(game::GameHandler& gameHandler);
// PvP honor credit toast ("+N Honor" shown when an honorable kill is credited)
struct PvpHonorToastEntry {
uint32_t honor = 0;
uint32_t victimRank = 0; // 0 = unranked / not available
float age = 0.0f;
};
static constexpr float PVP_HONOR_TOAST_DURATION = 3.5f;
std::vector<PvpHonorToastEntry> pvpHonorToasts_;
bool pvpHonorCallbackSet_ = false;
void renderPvpHonorToasts();
// Item loot toast — quality-coloured popup when an item is received
struct ItemLootToastEntry {
uint32_t itemId = 0;
uint32_t count = 0;
uint32_t quality = 1; // 0=grey,1=white,2=green,3=blue,4=purple,5=orange
std::string name;
float age = 0.0f;
};
static constexpr float ITEM_LOOT_TOAST_DURATION = 3.0f;
std::vector<ItemLootToastEntry> itemLootToasts_;
bool itemLootCallbackSet_ = false;
void renderItemLootToasts();
// Resurrection flash: brief "You have been resurrected!" overlay on ghost→alive transition
float resurrectFlashTimer_ = 0.0f;
static constexpr float kResurrectFlashDuration = 3.0f;
bool ghostStateCallbackSet_ = false;
bool appearanceCallbackSet_ = false;
bool ghostOpacityStateKnown_ = false;
bool ghostOpacityLastState_ = false;
uint32_t ghostOpacityLastInstanceId_ = 0;
void renderResurrectFlash();
// Zone discovery text ("Entering: <ZoneName>")
static constexpr float ZONE_TEXT_DURATION = 5.0f;
float zoneTextTimer_ = 0.0f;
std::string zoneTextName_;
std::string lastKnownZoneName_;
uint32_t lastKnownWorldStateZoneId_ = 0;
void renderZoneText(game::GameHandler& gameHandler);
void renderWeatherOverlay(game::GameHandler& gameHandler);
// Cooldown tracker
bool showCooldownTracker_ = false;
// DPS / HPS meter
bool showDPSMeter_ = false;
float dpsCombatAge_ = 0.0f; // seconds in current combat (for accurate early-combat DPS)
bool dpsWasInCombat_ = false;
float dpsEncounterDamage_ = 0.0f; // total player damage this combat
float dpsEncounterHeal_ = 0.0f; // total player healing this combat
size_t dpsLogSeenCount_ = 0; // log entries already scanned
public:
void triggerDing(uint32_t newLevel, uint32_t hpDelta = 0, uint32_t manaDelta = 0,
uint32_t str = 0, uint32_t agi = 0, uint32_t sta = 0,
uint32_t intel = 0, uint32_t spi = 0);
void triggerAchievementToast(uint32_t achievementId, std::string name = {});
void openDungeonFinder() { showDungeonFinder_ = true; }
void openDungeonFinder() { socialPanel_.showDungeonFinder_ = true; }
ToastManager& toastManager() { return toastManager_; }
};
} // namespace ui

View file

@ -0,0 +1,169 @@
#pragma once
#include <vulkan/vulkan.h>
#include <string>
#include <functional>
#include <cstdint>
namespace wowee {
namespace rendering { class Renderer; }
namespace ui {
class InventoryScreen;
class ChatPanel;
/**
* Settings panel (extracted from GameScreen)
*
* Owns all settings UI rendering, settings state variables, and
* graphics preset logic. Save/load remains in GameScreen since
* it serialises cross-cutting state (chat, quest tracker, etc.).
*/
class SettingsPanel {
public:
// ---- Settings UI visibility flags (written by EscapeMenu / Escape key) ----
bool showEscapeSettingsNotice = false;
bool showSettingsWindow = false;
bool settingsInit = false;
// ---- Pending video / graphics settings ----
bool pendingFullscreen = false;
bool pendingVsync = false;
int pendingResIndex = 0;
bool pendingShadows = true;
float pendingShadowDistance = 300.0f;
bool pendingWaterRefraction = true;
int pendingBrightness = 50; // 0-100, maps to 0.0-2.0 (50 = 1.0 default)
// ---- Pending audio settings ----
int pendingMasterVolume = 100;
int pendingMusicVolume = 30;
int pendingAmbientVolume = 100;
int pendingUiVolume = 100;
int pendingCombatVolume = 100;
int pendingSpellVolume = 100;
int pendingMovementVolume = 100;
int pendingFootstepVolume = 100;
int pendingNpcVoiceVolume = 100;
int pendingMountVolume = 100;
int pendingActivityVolume = 100;
// ---- Pending camera / controls ----
float pendingMouseSensitivity = 0.2f;
bool pendingInvertMouse = false;
bool pendingExtendedZoom = false;
float pendingCameraStiffness = 30.0f; // Camera smooth speed (higher = tighter, less sway)
float pendingPivotHeight = 1.6f; // Camera pivot height above feet (lower = less detached feel)
float pendingFov = 70.0f; // degrees, default matches WoW's ~70° horizontal FOV
// ---- Pending UI / interface ----
int pendingUiOpacity = 65;
bool pendingMinimapRotate = false;
bool pendingMinimapSquare = false;
bool pendingMinimapNpcDots = false;
bool pendingShowLatencyMeter = true;
bool pendingSeparateBags = true;
bool pendingShowKeyring = true;
// ---- Pending gameplay ----
bool pendingAutoLoot = false;
bool pendingAutoSellGrey = false;
bool pendingAutoRepair = false;
// ---- Pending soundtrack ----
bool pendingUseOriginalSoundtrack = true;
// ---- Pending action bar layout ----
bool pendingShowActionBar2 = true; // Show second action bar above main bar
float pendingActionBarScale = 1.0f; // Multiplier for action bar slot size (0.51.5)
float pendingActionBar2OffsetX = 0.0f; // Horizontal offset from default center position
float pendingActionBar2OffsetY = 0.0f; // Vertical offset from default (above bar 1)
bool pendingShowRightBar = false; // Right-edge vertical action bar (bar 3, slots 24-35)
bool pendingShowLeftBar = false; // Left-edge vertical action bar (bar 4, slots 36-47)
float pendingRightBarOffsetY = 0.0f; // Vertical offset from screen center
float pendingLeftBarOffsetY = 0.0f; // Vertical offset from screen center
// ---- Pending graphics quality ----
int pendingGroundClutterDensity = 100;
int pendingAntiAliasing = 0; // 0=Off, 1=2x, 2=4x, 3=8x
bool pendingFXAA = false; // FXAA post-process (combinable with MSAA)
bool pendingNormalMapping = true; // on by default
float pendingNormalMapStrength = 0.8f; // 0.0-2.0
bool pendingPOM = true; // on by default
int pendingPOMQuality = 1; // 0=Low(16), 1=Medium(32), 2=High(64)
bool pendingFSR = false;
int pendingUpscalingMode = 0; // 0=Off, 1=FSR1, 2=FSR3
int pendingFSRQuality = 3; // 0=UltraQuality, 1=Quality, 2=Balanced, 3=Native(100%)
float pendingFSRSharpness = 1.6f;
float pendingFSR2JitterSign = 0.38f;
float pendingFSR2MotionVecScaleX = 1.0f;
float pendingFSR2MotionVecScaleY = 1.0f;
bool pendingAMDFramegen = false;
// ---- Graphics quality presets ----
enum class GraphicsPreset : int {
CUSTOM = 0,
LOW = 1,
MEDIUM = 2,
HIGH = 3,
ULTRA = 4
};
GraphicsPreset currentGraphicsPreset = GraphicsPreset::CUSTOM;
GraphicsPreset pendingGraphicsPreset = GraphicsPreset::CUSTOM;
// ---- Applied-once flags (used by GameScreen::render() one-time-apply blocks) ----
bool fsrSettingsApplied_ = false;
float uiOpacity_ = 0.65f; // UI element transparency (0.0 = fully transparent, 1.0 = fully opaque)
bool minimapRotate_ = false;
bool minimapSquare_ = false;
bool minimapNpcDots_ = false;
bool showLatencyMeter_ = true; // Show server latency indicator
bool minimapSettingsApplied_ = false;
bool volumeSettingsApplied_ = false; // True once saved volume settings applied to audio managers
bool msaaSettingsApplied_ = false; // True once saved MSAA setting applied to renderer
bool fxaaSettingsApplied_ = false; // True once saved FXAA setting applied to renderer
bool waterRefractionApplied_ = false;
bool normalMapSettingsApplied_ = false; // True once saved normal map/POM settings applied
// ---- Mute state: mute bypasses master volume without touching slider values ----
bool soundMuted_ = false;
float preMuteVolume_ = 1.0f; // AudioEngine master volume before muting
// ---- Config toggles (read by GameScreen rendering, edited by Interface tab) ----
float nameplateScale_ = 1.0f; // Scale multiplier for nameplate bar dimensions
bool showFriendlyNameplates_ = true; // Shift+V toggles friendly player nameplates
bool showDPSMeter_ = false;
bool showCooldownTracker_ = false;
bool damageFlashEnabled_ = true;
bool lowHealthVignetteEnabled_ = true; // Persistent pulsing red vignette below 20% HP
// ---- Public methods ----
/// Render the settings window (call from GameScreen::render)
void renderSettingsWindow(InventoryScreen& inventoryScreen, ChatPanel& chatPanel,
std::function<void()> saveCallback);
/// Apply audio volume levels to all renderer sound managers
void applyAudioVolumes(rendering::Renderer* renderer);
/// Return the platform-specific settings file path
static std::string getSettingsPath();
private:
// Keybinding customization (private — only used in Controls tab)
int pendingRebindAction_ = -1; // -1 = not rebinding, otherwise action index
bool awaitingKeyPress_ = false;
// Settings tab rendering
void renderSettingsInterfaceTab(std::function<void()> saveCallback);
void renderSettingsGameplayTab(InventoryScreen& inventoryScreen,
std::function<void()> saveCallback);
void renderSettingsControlsTab(std::function<void()> saveCallback);
void renderSettingsAudioTab(std::function<void()> saveCallback);
void renderSettingsAboutTab();
void applyGraphicsPreset(GraphicsPreset preset);
void updateGraphicsPresetFromCurrentSettings();
};
} // namespace ui
} // namespace wowee

View file

@ -0,0 +1,77 @@
#pragma once
#include <imgui.h>
#include <vulkan/vulkan.h>
#include <string>
#include <vector>
#include <cstdint>
#include <functional>
namespace wowee {
namespace game { class GameHandler; }
namespace pipeline { class AssetManager; }
namespace ui {
class ChatPanel;
class SpellbookScreen;
class InventoryScreen;
/**
* Social panel manager (extracted from GameScreen)
*
* Owns all social/group-related rendering:
* party frames, boss frames, guild roster, social/friends frame,
* dungeon finder, who window, inspect window.
*/
class SocialPanel {
public:
SocialPanel() = default;
// ---- Callback type for spell icon lookup (stays in GameScreen) ----
using SpellIconFn = std::function<VkDescriptorSet(uint32_t spellId, pipeline::AssetManager*)>;
// ---- Toggle booleans (written by slash commands / escape handler / keybinds / UI buttons) ----
bool showSocialFrame_ = false; // O key toggles social/friends list
bool showGuildRoster_ = false;
bool showRaidFrames_ = true; // F key toggles raid/party frames
bool showWhoWindow_ = false;
bool showDungeonFinder_ = false;
bool showInspectWindow_ = false;
// ---- Guild roster state ----
std::string selectedGuildMember_;
bool showGuildNoteEdit_ = false;
bool editingOfficerNote_ = false;
char guildNoteEditBuffer_[256] = {0};
int guildRosterTab_ = 0; // 0=Roster, 1=Guild Info
char guildMotdEditBuffer_[256] = {0};
bool showMotdEdit_ = false;
char petitionNameBuffer_[64] = {0};
char addRankNameBuffer_[64] = {0};
bool showAddRankModal_ = false;
// ---- LFG state ----
uint8_t lfgRoles_ = 0x08; // default: DPS (0x02=tank, 0x04=healer, 0x08=dps)
uint32_t lfgSelectedDungeon_ = 861; // default: random dungeon (entry 861)
// ---- Public render methods ----
void renderPartyFrames(game::GameHandler& gameHandler,
ChatPanel& chatPanel,
SpellIconFn getSpellIcon);
void renderBossFrames(game::GameHandler& gameHandler,
SpellbookScreen& spellbookScreen,
SpellIconFn getSpellIcon);
void renderGuildRoster(game::GameHandler& gameHandler,
ChatPanel& chatPanel);
void renderSocialFrame(game::GameHandler& gameHandler,
ChatPanel& chatPanel);
void renderDungeonFinderWindow(game::GameHandler& gameHandler,
ChatPanel& chatPanel);
void renderWhoWindow(game::GameHandler& gameHandler,
ChatPanel& chatPanel);
void renderInspectWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen);
};
} // namespace ui
} // namespace wowee

View file

@ -0,0 +1,190 @@
#pragma once
#include <vulkan/vulkan.h>
#include <imgui.h>
#include <string>
#include <vector>
#include <cstdint>
namespace wowee {
namespace game { class GameHandler; }
namespace ui {
/**
* Toast / notification overlay manager
*
* Owns all toast state, callbacks, and rendering:
* level-up ding, achievement, area discovery, whisper, quest progress,
* player level-up, PvP honor, item loot, reputation, quest complete,
* zone entry, area trigger, resurrect flash, and zone text.
*/
class ToastManager {
public:
ToastManager() = default;
/// Register toast-related callbacks on GameHandler (idempotent — safe every frame)
void setupCallbacks(game::GameHandler& gameHandler);
/// Render "early" toasts (rep, quest-complete, zone, area-trigger) — called before action bars
void renderEarlyToasts(float deltaTime, game::GameHandler& gameHandler);
/// Render "late" toasts (ding, achievement, discovery, whisper, quest progress,
/// player level-up, PvP honor, item loot, resurrect flash, zone text) — called after escape menu
void renderLateToasts(game::GameHandler& gameHandler);
/// Fire level-up ding animation + sound
void triggerDing(uint32_t newLevel, uint32_t hpDelta = 0, uint32_t manaDelta = 0,
uint32_t str = 0, uint32_t agi = 0, uint32_t sta = 0,
uint32_t intel = 0, uint32_t spi = 0);
/// Fire achievement earned toast + sound
void triggerAchievementToast(uint32_t achievementId, std::string name = {});
// --- public state consumed by GameScreen for the golden burst overlay ---
float levelUpFlashAlpha = 0.0f;
uint32_t levelUpDisplayLevel = 0;
private:
// ---- Ding effect (own level-up) ----
static constexpr float DING_DURATION = 4.0f;
float dingTimer_ = 0.0f;
uint32_t dingLevel_ = 0;
uint32_t dingHpDelta_ = 0;
uint32_t dingManaDelta_ = 0;
uint32_t dingStats_[5] = {};
void renderDingEffect();
// ---- Achievement toast ----
static constexpr float ACHIEVEMENT_TOAST_DURATION = 5.0f;
float achievementToastTimer_ = 0.0f;
uint32_t achievementToastId_ = 0;
std::string achievementToastName_;
bool achievementCallbackSet_ = false;
void renderAchievementToast();
// ---- Area discovery toast ----
static constexpr float DISCOVERY_TOAST_DURATION = 4.0f;
float discoveryToastTimer_ = 0.0f;
std::string discoveryToastName_;
uint32_t discoveryToastXP_ = 0;
bool areaDiscoveryCallbackSet_ = false;
void renderDiscoveryToast();
// ---- Whisper toast ----
struct WhisperToastEntry {
std::string sender;
std::string preview;
float age = 0.0f;
};
static constexpr float WHISPER_TOAST_DURATION = 5.0f;
std::vector<WhisperToastEntry> whisperToasts_;
size_t whisperSeenCount_ = 0;
void renderWhisperToasts();
// ---- Quest objective progress toast ----
struct QuestProgressToastEntry {
std::string questTitle;
std::string objectiveName;
uint32_t current = 0;
uint32_t required = 0;
float age = 0.0f;
};
static constexpr float QUEST_TOAST_DURATION = 4.0f;
std::vector<QuestProgressToastEntry> questToasts_;
bool questProgressCallbackSet_ = false;
void renderQuestProgressToasts();
// ---- Nearby player level-up toast ----
struct PlayerLevelUpToastEntry {
uint64_t guid = 0;
std::string playerName;
uint32_t newLevel = 0;
float age = 0.0f;
};
static constexpr float PLAYER_LEVELUP_TOAST_DURATION = 4.0f;
std::vector<PlayerLevelUpToastEntry> playerLevelUpToasts_;
bool otherPlayerLevelUpCallbackSet_ = false;
void renderPlayerLevelUpToasts(game::GameHandler& gameHandler);
// ---- PvP honor toast ----
struct PvpHonorToastEntry {
uint32_t honor = 0;
uint32_t victimRank = 0;
float age = 0.0f;
};
static constexpr float PVP_HONOR_TOAST_DURATION = 3.5f;
std::vector<PvpHonorToastEntry> pvpHonorToasts_;
bool pvpHonorCallbackSet_ = false;
void renderPvpHonorToasts();
// ---- Item loot toast ----
struct ItemLootToastEntry {
uint32_t itemId = 0;
uint32_t count = 0;
uint32_t quality = 1;
std::string name;
float age = 0.0f;
};
static constexpr float ITEM_LOOT_TOAST_DURATION = 3.0f;
std::vector<ItemLootToastEntry> itemLootToasts_;
bool itemLootCallbackSet_ = false;
void renderItemLootToasts();
// ---- Reputation change toast ----
struct RepToastEntry {
std::string factionName;
int32_t delta = 0;
int32_t standing = 0;
float age = 0.0f;
};
std::vector<RepToastEntry> repToasts_;
bool repChangeCallbackSet_ = false;
static constexpr float kRepToastLifetime = 3.5f;
void renderRepToasts(float deltaTime);
// ---- Quest completion toast ----
struct QuestCompleteToastEntry {
uint32_t questId = 0;
std::string title;
float age = 0.0f;
};
std::vector<QuestCompleteToastEntry> questCompleteToasts_;
bool questCompleteCallbackSet_ = false;
static constexpr float kQuestCompleteToastLifetime = 4.0f;
void renderQuestCompleteToasts(float deltaTime);
// ---- Zone entry toast ----
struct ZoneToastEntry {
std::string zoneName;
float age = 0.0f;
};
std::vector<ZoneToastEntry> zoneToasts_;
std::string lastKnownZone_;
static constexpr float kZoneToastLifetime = 3.0f;
void renderZoneToasts(float deltaTime);
// ---- Area trigger message toast ----
struct AreaTriggerToast {
std::string text;
float age = 0.0f;
};
std::vector<AreaTriggerToast> areaTriggerToasts_;
void renderAreaTriggerToasts(float deltaTime, game::GameHandler& gameHandler);
// ---- Resurrection flash ----
float resurrectFlashTimer_ = 0.0f;
static constexpr float kResurrectFlashDuration = 3.0f;
bool ghostStateCallbackSet_ = false;
void renderResurrectFlash();
// ---- Zone discovery text ("Entering: <ZoneName>") ----
static constexpr float ZONE_TEXT_DURATION = 5.0f;
float zoneTextTimer_ = 0.0f;
std::string zoneTextName_;
std::string lastKnownZoneName_;
uint32_t lastKnownWorldStateZoneId_ = 0;
void renderZoneText(game::GameHandler& gameHandler);
};
} // namespace ui
} // namespace wowee

View file

@ -0,0 +1,182 @@
// ============================================================
// WindowManager — extracted from GameScreen
// Owns all NPC interaction windows, popup dialogs, and misc
// overlay UI: loot, gossip, quest, vendor, trainer, mail, bank,
// auction house, barber, stable, taxi, escape menu, death screen,
// instance lockouts, achievements, GM ticket, books, titles,
// equipment sets, skills.
// ============================================================
#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
#include <functional>
#include <vulkan/vulkan.h>
namespace wowee {
namespace game { class GameHandler; }
namespace pipeline { class AssetManager; }
namespace ui {
class ChatPanel;
class SettingsPanel;
class InventoryScreen;
class SpellbookScreen;
class WindowManager {
public:
// Callback type for resolving spell icons (spellId, assetMgr) → VkDescriptorSet
using SpellIconFn = std::function<VkDescriptorSet(uint32_t, pipeline::AssetManager*)>;
// ---- NPC interaction windows ----
void renderLootWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderGossipWindow(game::GameHandler& gameHandler,
ChatPanel& chatPanel);
void renderQuestDetailsWindow(game::GameHandler& gameHandler,
ChatPanel& chatPanel,
InventoryScreen& inventoryScreen);
void renderQuestRequestItemsWindow(game::GameHandler& gameHandler,
ChatPanel& chatPanel,
InventoryScreen& inventoryScreen);
void renderQuestOfferRewardWindow(game::GameHandler& gameHandler,
ChatPanel& chatPanel,
InventoryScreen& inventoryScreen);
void renderVendorWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderTrainerWindow(game::GameHandler& gameHandler,
SpellIconFn getSpellIcon);
void renderBarberShopWindow(game::GameHandler& gameHandler);
void renderStableWindow(game::GameHandler& gameHandler);
void renderTaxiWindow(game::GameHandler& gameHandler);
// ---- Mail and banking ----
void renderMailWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderMailComposeWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen);
void renderBankWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderGuildBankWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
void renderAuctionHouseWindow(game::GameHandler& gameHandler,
InventoryScreen& inventoryScreen,
ChatPanel& chatPanel);
// ---- Popup / overlay windows ----
void renderEscapeMenu(SettingsPanel& settingsPanel);
void renderLogoutCountdown(game::GameHandler& gameHandler);
void renderDeathScreen(game::GameHandler& gameHandler);
void renderReclaimCorpseButton(game::GameHandler& gameHandler);
void renderInstanceLockouts(game::GameHandler& gameHandler);
void renderAchievementWindow(game::GameHandler& gameHandler);
void renderGmTicketWindow(game::GameHandler& gameHandler);
void renderBookWindow(game::GameHandler& gameHandler);
void renderTitlesWindow(game::GameHandler& gameHandler);
void renderEquipSetWindow(game::GameHandler& gameHandler);
void renderSkillsWindow(game::GameHandler& gameHandler);
// ---- State owned by this manager ----
// Instance lockouts
bool showInstanceLockouts_ = false;
// Achievements
bool showAchievementWindow_ = false;
char achievementSearchBuf_[128] = {};
// Skills / Professions
bool showSkillsWindow_ = false;
// Titles
bool showTitlesWindow_ = false;
// Equipment Sets
bool showEquipSetWindow_ = false;
// GM Ticket
bool showGmTicketWindow_ = false;
bool gmTicketWindowWasOpen_ = false;
char gmTicketBuf_[2048] = {};
// Book / scroll reader
bool showBookWindow_ = false;
int bookCurrentPage_ = 0;
// Death screen
float deathElapsed_ = 0.0f;
bool deathTimerRunning_ = false;
static constexpr float kForcedReleaseSec = 360.0f;
// Escape menu
bool showEscapeMenu = false;
// Mail compose
char mailRecipientBuffer_[256] = "";
char mailSubjectBuffer_[256] = "";
char mailBodyBuffer_[2048] = "";
int mailComposeMoney_[3] = {0, 0, 0};
// Vendor
char vendorSearchFilter_[128] = "";
bool vendorConfirmOpen_ = false;
uint64_t vendorConfirmGuid_ = 0;
uint32_t vendorConfirmItemId_ = 0;
uint32_t vendorConfirmSlot_ = 0;
uint32_t vendorConfirmQty_ = 1;
uint32_t vendorConfirmPrice_ = 0;
std::string vendorConfirmItemName_;
bool vendorBagsOpened_ = false;
// Barber shop
int barberHairStyle_ = 0;
int barberHairColor_ = 0;
int barberFacialHair_ = 0;
int barberOrigHairStyle_ = 0;
int barberOrigHairColor_ = 0;
int barberOrigFacialHair_ = 0;
bool barberInitialized_ = false;
// Trainer
char trainerSearchFilter_[128] = "";
// Auction house
char auctionSearchName_[256] = "";
int auctionLevelMin_ = 0;
int auctionLevelMax_ = 0;
int auctionQuality_ = 0;
int auctionSellDuration_ = 2;
int auctionSellBid_[3] = {0, 0, 0};
int auctionSellBuyout_[3] = {0, 0, 0};
int auctionSelectedItem_ = -1;
int auctionSellSlotIndex_ = -1;
uint32_t auctionBrowseOffset_ = 0;
int auctionItemClass_ = -1;
int auctionItemSubClass_ = -1;
bool auctionUsableOnly_ = false;
// Guild bank money input
int guildBankMoneyInput_[3] = {0, 0, 0};
// ItemExtendedCost.dbc cache
struct ExtendedCostEntry {
uint32_t honorPoints = 0;
uint32_t arenaPoints = 0;
uint32_t itemId[5] = {};
uint32_t itemCount[5] = {};
};
std::unordered_map<uint32_t, ExtendedCostEntry> extendedCostCache_;
bool extendedCostDbLoaded_ = false;
private:
void loadExtendedCostDBC();
std::string formatExtendedCost(uint32_t extendedCostId, game::GameHandler& gameHandler);
};
} // namespace ui
} // namespace wowee

View file

@ -2935,7 +2935,7 @@ void Application::setupUICallbacks() {
// Level-up callback — play sound, cheer emote, and trigger UI ding overlay + 3D effect
gameHandler->setLevelUpCallback([this](uint32_t newLevel) {
if (uiManager) {
uiManager->getGameScreen().triggerDing(newLevel);
uiManager->getGameScreen().toastManager().triggerDing(newLevel);
}
if (renderer) {
renderer->triggerLevelUpEffect(renderer->getCharacterPosition());
@ -2945,7 +2945,7 @@ void Application::setupUICallbacks() {
// Achievement earned callback — show toast banner
gameHandler->setAchievementEarnedCallback([this](uint32_t achievementId, const std::string& name) {
if (uiManager) {
uiManager->getGameScreen().triggerAchievementToast(achievementId, name);
uiManager->getGameScreen().toastManager().triggerAchievementToast(achievementId, name);
}
});

View file

@ -1266,14 +1266,14 @@ struct EntityController::GameObjectTypeHandler : EntityController::IObjectTypeHa
struct EntityController::ItemTypeHandler : EntityController::IObjectTypeHandler {
EntityController& ctl_;
explicit ItemTypeHandler(EntityController& c) : ctl_(c) {}
void onCreate(const UpdateBlock& block, std::shared_ptr<Entity>&, bool& newItemCreated) override { ctl_.onCreateItem(block, newItemCreated); }
void onCreate(const UpdateBlock& block, std::shared_ptr<Entity>& /*entity*/, bool& newItemCreated) override { ctl_.onCreateItem(block, newItemCreated); }
void onValuesUpdate(const UpdateBlock& block, std::shared_ptr<Entity>& entity) override { ctl_.onValuesUpdateItem(block, entity); }
};
struct EntityController::CorpseTypeHandler : EntityController::IObjectTypeHandler {
EntityController& ctl_;
explicit CorpseTypeHandler(EntityController& c) : ctl_(c) {}
void onCreate(const UpdateBlock& block, std::shared_ptr<Entity>&, bool&) override { ctl_.onCreateCorpse(block); }
void onCreate(const UpdateBlock& block, std::shared_ptr<Entity>& /*entity*/, bool&) override { ctl_.onCreateCorpse(block); }
};
// ============================================================

1751
src/ui/action_bar_panel.cpp Normal file

File diff suppressed because it is too large Load diff

4898
src/ui/chat_panel.cpp Normal file

File diff suppressed because it is too large Load diff

1890
src/ui/combat_ui.cpp Normal file

File diff suppressed because it is too large Load diff

1115
src/ui/dialog_manager.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1258
src/ui/settings_panel.cpp Normal file

File diff suppressed because it is too large Load diff

2626
src/ui/social_panel.cpp Normal file

File diff suppressed because it is too large Load diff

1250
src/ui/toast_manager.cpp Normal file

File diff suppressed because it is too large Load diff

4264
src/ui/window_manager.cpp Normal file

File diff suppressed because it is too large Load diff