#pragma once #include "game/game_handler.hpp" #include "game/inventory.hpp" // WorldMap is now owned by Renderer, accessed via getWorldMap() #include "rendering/character_preview.hpp" #include "ui/inventory_screen.hpp" #include "ui/quest_log_screen.hpp" #include "ui/spellbook_screen.hpp" #include "ui/talent_screen.hpp" #include "ui/keybinding_manager.hpp" #include #include #include #include namespace wowee { namespace pipeline { class AssetManager; } namespace ui { /** * In-game screen UI * * Displays player info, entity list, chat, and game controls */ class GameScreen { public: GameScreen(); /** * Render the UI * @param gameHandler Reference to game handler */ void render(game::GameHandler& gameHandler); /** * Check if chat input is active */ bool isChatInputActive() const { return chatInputActive; } void saveSettings(); void loadSettings(); 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 int lastChatType = 0; // Track chat type changes bool chatInputMoveCursorToEnd = false; // Chat sent-message history (Up/Down arrow recall) std::vector chatSentHistory_; int chatHistoryIdx_ = -1; // -1 = not browsing history // Chat tabs int activeChatTab_ = 0; struct ChatTab { std::string name; uint32_t typeMask; // bitmask of ChatType values to show }; std::vector chatTabs_; void initChatTabs(); bool shouldShowMessage(const game::MessageChatData& msg, int tabIndex) const; // UI state bool showEntityWindow = false; bool showChatWindow = true; bool showMinimap_ = true; // M key toggles minimap bool showNameplates_ = true; // V key toggles 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; 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 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; }; std::vector uiErrors_; bool uiErrorCallbackSet_ = false; static constexpr float kUIErrorLifetime = 2.5f; // 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 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 questCompleteToasts_; bool questCompleteCallbackSet_ = false; static constexpr float kQuestCompleteToastLifetime = 4.0f; 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; 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 = false; 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 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 pendingAutoLoot = false; // Keybinding customization int pendingRebindAction = -1; // -1 = not rebinding, otherwise action index bool awaitingKeyPress = false; bool pendingUseOriginalSoundtrack = true; bool pendingShowActionBar2 = true; // Show second action bar above main bar float pendingActionBarScale = 1.0f; // Multiplier for action bar slot size (0.5–1.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 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 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 */ void renderPlayerInfo(game::GameHandler& gameHandler); /** * Render entity list window */ void renderEntityList(game::GameHandler& gameHandler); /** * Render chat window */ void renderChatWindow(game::GameHandler& gameHandler); /** * Send chat message */ void sendChatMessage(game::GameHandler& gameHandler); /** * 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) */ void renderPlayerFrame(game::GameHandler& gameHandler); /** * Render target frame */ void renderTargetFrame(game::GameHandler& gameHandler); void renderFocusFrame(game::GameHandler& gameHandler); /** * Render pet frame (below player frame when player has an active pet) */ void renderPetFrame(game::GameHandler& gameHandler); /** * Process targeting input (Tab, Escape, click) */ void processTargetInput(game::GameHandler& gameHandler); /** * Rebuild character geosets from current equipment state */ void updateCharacterGeosets(game::Inventory& inventory); /** * Re-composite character skin texture from current equipment */ void updateCharacterTextures(game::Inventory& inventory); // ---- New UI renders ---- void renderActionBar(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 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 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 renderTaxiWindow(game::GameHandler& gameHandler); void renderDeathScreen(game::GameHandler& gameHandler); void renderReclaimCorpseButton(game::GameHandler& gameHandler); void renderResurrectDialog(game::GameHandler& gameHandler); void renderTalentWipeConfirmDialog(game::GameHandler& gameHandler); void renderEscapeMenu(); void renderSettingsWindow(); 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 renderLfgProposalPopup(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 renderObjectiveTracker(game::GameHandler& gameHandler); void renderInstanceLockouts(game::GameHandler& gameHandler); void renderNameplates(game::GameHandler& gameHandler); void renderBattlegroundScore(game::GameHandler& gameHandler); void renderDPSMeter(game::GameHandler& gameHandler); /** * Inventory screen */ void renderWorldMap(game::GameHandler& gameHandler); InventoryScreen inventoryScreen; uint64_t inventoryScreenCharGuid_ = 0; // GUID of character inventory screen was initialized for QuestLogScreen questLogScreen; SpellbookScreen spellbookScreen; TalentScreen talentScreen; // WorldMap is now owned by Renderer (accessed via renderer->getWorldMap()) // Spell icon cache: spellId -> GL texture ID std::unordered_map spellIconCache_; // SpellIconID -> icon path (from SpellIcon.dbc) std::unordered_map spellIconPaths_; // SpellID -> SpellIconID (from Spell.dbc field 133) std::unordered_map spellIconIds_; bool spellIconDbLoaded_ = false; VkDescriptorSet getSpellIcon(uint32_t spellId, pipeline::AssetManager* am); // 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) // 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); // GM Ticket window bool showGmTicketWindow_ = false; char gmTicketBuf_[2048] = {}; void renderGmTicketWindow(game::GameHandler& gameHandler); // Inspect window bool showInspectWindow_ = false; void renderInspectWindow(game::GameHandler& gameHandler); // Threat window bool showThreatWindow_ = false; void renderThreatWindow(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 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] = ""; // 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) // 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 = 3.0f; float dingTimer_ = 0.0f; uint32_t dingLevel_ = 0; 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(); // Zone discovery text ("Entering: ") static constexpr float ZONE_TEXT_DURATION = 5.0f; float zoneTextTimer_ = 0.0f; std::string zoneTextName_; std::string lastKnownZoneName_; void renderZoneText(); // DPS / HPS meter bool showDPSMeter_ = false; float dpsCombatAge_ = 0.0f; // seconds in current combat (for accurate early-combat DPS) bool dpsWasInCombat_ = false; public: void triggerDing(uint32_t newLevel); void triggerAchievementToast(uint32_t achievementId, std::string name = {}); }; } // namespace ui } // namespace wowee