Replace text-only buttons with icon+draw-list rendering that matches
the style of the regular bank, loot, and vendor windows. Item icons are
looked up via inventoryScreen.getItemIcon(info->displayInfoId); falls
back to a coloured bordered square with two-letter abbreviation when
the texture is not yet cached. Stack count is overlaid in the
bottom-right corner. Withdraw still fires on left-click.
Opening the spellbook on a new tab, logging in with many auras/action slots, or
opening a full bag all triggered synchronous BLP-decode + GPU uploads for every
uncached icon in one frame, causing a visible stall. Apply the same 4-per-frame
upload cap that was added to talent_screen, so icons load progressively.
Switching from Arms to Fury (or any previously-unseen tab) caused a
multi-frame stall because getSpellIcon() loaded and uploaded all ~20
BLP textures synchronously in a single frame. Limit new icon GPU
uploads to 4 per frame; uncached icons return null and are loaded on
subsequent frames, spreading the cost over ~5 frames with no visible
hang.
Chests (and lockboxes, coffers, etc.) failed to open because CMSG_LOOT
was only sent on Classic/Turtle expansions, and only when GO type was
already cached as type 3. Fix: always send CMSG_LOOT after
CMSG_GAMEOBJ_USE (server silently ignores it for non-lootable objects).
Also broaden CMSG_GAMEOBJ_REPORT_USE to all non-mailbox WotLK GOs.
Latency meter: record pingTimestamp_ in sendPing() and compute RTT in
handlePong(); add toggleable "Show Latency Meter" checkbox in Interface
settings (saved to settings.cfg).
Proactively call ensureItemInfo() for both vendor items and buyback items
during rendering. This ensures item names and stats are available before
display, eliminating "Item <id>" placeholders and providing instant tooltip
info, matching the pattern used for quest rewards and action bar items.
When a player nameplate is about to render with an empty name (showing
"Player (level)" placeholder), actively re-request the name query. Since
queryPlayerName() is idempotent (won't duplicate pending queries), this
ensures that slow network responses don't cause players to permanently
display as "Player (67)" even after the response arrives. Rendering code
now triggers name queries to completion before falling back to placeholders.
Corpses no longer display nameplates or health bars unless they are the current
target (selected for loot or skinning). When selected, corpses show a minimal
grey nameplate with no health fill.
Weapon slots were positioned too far right (at rightColX) causing overlap with right
column equipment. Repositioned to center column area (contentStartX + slotSize + 8px),
after left column. 3D preview renders on top, no visual conflict.
Position weapon slots (main hand, off hand, ranged) to align with the right
column instead of appearing in the left column where they crowd the main
equipment slots (tabbard, bracers, etc.). Weapons now positioned consistently
with the 3-column layout at rightColX instead of appearing at the default left
cursor position.
Implement quick-access quality presets (Low, Medium, High, Ultra) that adjust multiple graphics settings at once for better user experience. Each preset configures:
- Shadow rendering and distance
- Anti-aliasing (MSAA) level
- Normal mapping and parallax mapping
- Ground clutter density
The system automatically detects when settings deviate from a preset and marks them as "Custom". Presets are persisted to settings.cfg for consistency across sessions. Users can quickly switch between performance and quality modes or tweak individual settings as needed.
- Add showMinimap_ and showRaidFrames_ visibility flags to GameScreen
- Wire up TOGGLE_MINIMAP (M key) to toggle minimap visibility
- Wire up TOGGLE_RAID_FRAMES (F key) to toggle party/raid frame visibility
- Conditional rendering of minimap markers and party frames
- Completes keybinding manager integration for all 15 customizable actions
- Replace hardcoded SDL_SCANCODE_ESCAPE with TOGGLE_SETTINGS keybinding
- Replace hardcoded SDL_SCANCODE_RETURN with TOGGLE_CHAT keybinding
- Allows customization of these keys through Settings UI
- Add TOGGLE_BAGS action to keybinding manager (B key default)
- Update inventory_screen.cpp to use keybinding manager for bag and character toggles
- Maintain consistent keybinding system across all UI windows
The keybinding manager had incorrect default key assignments:
- TOGGLE_SPELLBOOK was S (should be P - WoW standard)
- TOGGLE_TALENTS was K (should be N - WoW standard)
These mismatched the actual hardcoded keys in spellbook_screen.cpp (P) and
talent_screen.cpp (N), as well as user expectations from standard WoW.
Update keybinding defaults to align with WoW conventions and the actual UI
implementations that are using these keys.
The quest log screen was using a hardcoded SDL_SCANCODE_L key check instead of
the keybinding manager system, preventing users from customizing the keybinding.
Update to use KeybindingManager::Action::TOGGLE_QUESTS (bound to L by default),
allowing users to customize the quest log toggle key through the Settings UI
while maintaining the default WoW key binding.
This enables consistency with other customizable window toggles that already use
the keybinding system (Character Screen, Inventory, Spellbook, World Map, etc.).
The R key was previously assigned to TOGGLE_RAID_FRAMES in the keybinding
manager but was never actually implemented (raid frames had no visibility
toggle). Loop 10 implemented R for camera reset, creating a conflict.
Reassign TOGGLE_RAID_FRAMES to F (an unused key) to prevent the conflict.
This aligns with the intention that R is now the standard camera reset key.
Implement showWorldMap_ state variable and TOGGLE_WORLD_MAP keybinding
integration to allow players to customize the W key binding for opening/
closing the World Map, consistent with other window toggles like Nameplates
(V key) and Guild Roster (O key).
- Add World Map (W), Nameplates (V), Raid Frames (R), Quest Log (Q) to
KeybindingManager enum with customizable default bindings
- Replace hard-coded V key check for nameplate toggle with
KeybindingManager::isActionPressed() to support customization
- Update config file persistence to handle new bindings
- Infrastructure in place for implementing visibility toggles on other
windows (World Map, Raid Frames, Quest Log) with future UI refactoring
- Extended KeybindingManager enum with TOGGLE_GUILD_ROSTER (O) and
TOGGLE_DUNGEON_FINDER (J) to replace hard-coded key checks
- Added Controls tab in Settings UI for rebinding all 10 customizable actions
- Implemented real-time key capture and binding with visual feedback
- Integrated keybinding persistence with main settings.cfg file
- Replaced hard-coded O key (Guild Roster) and I key (Dungeon Finder) checks
with KeybindingManager::isActionPressed() calls
- Added Reset to Defaults button for restoring original keybindings
Implement KeybindingManager singleton class to support:
- Storing and loading keybinding configuration from ini files
- Querying whether an action's keybinding was pressed
- Runtime rebinding of actions to different keys
- Default keybinding set: C=Character, I=Inventory, S=Spellbook, K=Talents,
L=Quests, M=Minimap, Esc=Settings, Enter=Chat
This is the foundation for user-customizable keybindings. Integration with
UI controls and replacement of hard-coded ImGui::IsKeyPressed calls will
follow in subsequent improvements.
The spell icon loader was incorrectly assuming WotLK field 133 (IconID)
for any DBC with >= 200 fields. This breaks Classic/TBC where IconID
is at different fields:
- Classic: field 117
- TBC: field 124
- WotLK: field 133
Now always uses expansion-aware layout (spellL) when available, falling
back to hardcoded field 133 only if the layout is missing.
Fixes missing spell icons on Classic and TBC expansions.
When an attack is partially blocked, the server sends the remaining
damage in totalDamage and the blocked amount in data.blocked. Show
both: the damage taken and a 'Block N' entry. When block amount is
zero (full block with no damage), just show 'Block'.
handleSpellDamageLog now emits ABSORB/RESIST entries when data.absorbed
or data.resisted are nonzero, so players see 'Absorbed 123' alongside
damage numbers (e.g. vs. Power Word: Shield or Ice Barrier).
handleSpellHealLog does the same for heal absorbs (e.g. Vampiric Embrace
counter-absorbs). renderCombatText now formats amount when nonzero.
Adds dedicated CombatTextEntry::Type entries for ABSORB (miss type 7)
and RESIST (miss type 8), replacing the generic MISS display. Updates
missTypes arrays in SMSG_SPELLLOGMISS and SMSG_SPELL_GO, and adds
light-blue "Absorb" and grey "Resist" rendering in the combat text overlay.
- terrain_renderer: add FREE_DESCRIPTOR_SET_BIT flag and vkFreeDescriptorSets
in destroyChunkGPU so material descriptor sets are returned to the pool;
prevents GPU device lost from pool exhaustion near populated areas
- game_screen: fix projectToMinimap to use the exact inverse of the minimap
shader transform so quest objective markers appear at the correct position
and orientation regardless of camera bearing
- inventory_screen: fix item comparison tooltip to not compare equipped items
against themselves (character screen); add item level diff line; show (=)
indicator when stats are equal rather than bare value which looked identical
to the item's own tooltip
Yellow crossed-swords icon appears to the right of the unit name when
the creature's entry is an incomplete kill objective in a tracked quest.
Updated icon is suppressed once the kill count is satisfied.
Uses unit->getEntry() (Unit subclass method) rather than the base
Entity pointer, matching how questKillEntries keys are stored.
- SMSG_QUESTUPDATE_ADD_KILL: use absolute value of npcOrGoId when looking
up required count from killObjectives (negative values = game objects)
- applyPackedKillCountsFromFields: same fix — use abs(npcOrGoId) as map key
so GO objective counts are stored with the correct entry key
- SMSG_QUESTUPDATE_ADD_ITEM: also match quests via itemObjectives when
requiredItemCounts is not yet populated (race at quest accept time)
- Quest log and minimap sidebar: fall back to GO name cache for entries
that return empty from getCachedCreatureName (interact/loot objectives)
SMSG_QUEST_QUERY_RESPONSE uses 40 fixed uint32 fields + 4 strings for both
Classic/Turtle and TBC, but the isClassicLayout flag was only set for stride-3
expansions (Classic/Turtle). TBC (stride 4) was incorrectly using the WotLK
55-field path, causing objective parsing to fail.
- Extend isClassicLayout to cover stride <= 4 (includes TBC)
- Refactor extractQuestQueryObjectives to try both layouts with fallback,
matching the robustness of pickBestQuestQueryTexts
- Pre-fetch creature/GO/item name queries when quest objectives are parsed
so names are ready before the player opens the quest log
- Quest log detail view: show creature names instead of raw entry IDs for
kill objectives, and show required count (x/y) for item objectives
Add UNIT_FIELD_STAT0-4 (STR/AGI/STA/INT/SPI) to the UF enum and wire up
per-expansion indices in all four expansion JSON files (WotLK: 84-88,
Classic/Turtle: 138-142, TBC: 159-163). Read the values in both CREATE
and VALUES player update handlers and store in playerStats_[5].
renderStatsPanel now uses the server-authoritative totals when available,
falling back to the previous 20+level estimate only if the server hasn't
sent UNIT_FIELD_STAT* yet. Item-query bonuses are still shown as (+N)
alongside the server total for both paths.
Display 'Requires Level N' in red when the player does not meet the
item's level requirement, and in normal colour when they do. Applies
to both equipped-item and bag-item tooltip paths.
- Animation stutter: skip playAnimation(Run) for the local player in the
server movement callback — the player renderer state machine already manages
it; resetting animTime on every movement packet caused visible stutter
- Resolution crash: reorder swapchain recreation so old swapchain is only
destroyed after confirming the new build succeeded; add null-swapchain
guard in beginFrame to survive the retry window
- Memory cap: reduce cache budget from 80% uncapped to 50% hard-capped at
16 GB to prevent excessive RAM use on high-memory systems
- Spell tooltip: suppress "Drag to action bar / Double-click to cast" hints
when the tooltip is shown from the action bar (showUsageHints=false)
- M2 collision: add watermelon/melon/squash/gourd to foliage (no-collision);
exclude chair/bench/stool/seat/throne from smallSolidProp so invisible chair
bounding boxes no longer trap the player
Shows a centered modal when LfgState::Proposal is active regardless of
whether the Dungeon Finder window is open, matching WoW behaviour where
the accept/decline prompt always appears over the game world.
Mirrors the BG invite popup pattern; buttons call lfgAcceptProposal().
- Buff bar was at Y=140 which overlaps the minimap (Y=10 to Y=210);
moved to Y=215 (just below minimap bottom edge) with 8 icons per row
- Quest tracker moved from Y=200 (inside minimap area) to Y=320 to
leave space for up to 3 rows of buffs between minimap and tracker
- Both are right-anchored and no longer conflict with the minimap or
each other in typical usage (up to ~20 active auras)
- Relocates buff bar from top-left Y=145 (overlapping party frames) to
top-right (screenW - barW - 10, 140) where it doesn't conflict with
party/raid frames anchored on the left side
- Increases max shown auras from 16 to 40 (WotLK supports 48 slots)
- Two-pass rendering: buffs shown first, debuffs below with a spacing gap
between them; both still use green/red borders for visual distinction
- Widens row to 12 icons for better horizontal use of screen space
Right-clicking a party member name in the 5-man party frame opens
a context menu with: Target, Set Focus, Whisper, Trade, Inspect.
- Whisper switches chat type to WHISPER and pre-fills the target name
- Trade calls GameHandler::initiateTrade(guid)
- Inspect sets target then calls GameHandler::inspectTarget()
- Uses BeginPopupContextItem tied to the Selectable widget
- Party frames: gold star prefix and gold name color for group leader;
LFG role badges [T]/[H]/[D] shown inline after member name
- Raid frames: leader name rendered in gold with a corner star marker;
role letter (T/H/D) drawn in bottom-right corner of each compact cell;
uses partyData.leaderGuid already present in the function scope
- Minimap party dots already use gold for leader (unchanged)
- Add latency indicator below minimap (color-coded: green/yellow/orange/red)
using the lastLatency value measured via CMSG_PING/SMSG_PONG
- Add BG queue status indicator below minimap when in WAIT_QUEUE
(abbreviated name: AV/WSG/AB/EotS etc.)
- Target-of-Target frame: add level display and click-to-target support
- Expose getLatencyMs() accessor on GameHandler
Add a compact focus target frame on the right side of the screen
when the player has a focus target set via /focus.
- Shows [Focus] label, name (colored by hostility/level diff), level
- HP bar with green→yellow→red coloring; power bar with type colors
- Cast bar showing spell name and remaining time when focus is casting
- Clicking the frame targets the focus entity
- Clears automatically when focus is lost (/clearfocus)
Replace the text-only "/join to enter" message with an interactive
popup that shows the BG name, a live countdown progress bar, and
Enter/Leave Queue buttons.
- Parse STATUS_WAIT_JOIN timeout from SMSG_BATTLEFIELD_STATUS
- Store inviteReceivedTime (steady_clock) on the queue slot
- BgQueueSlot moved to public section so UI can read invite details
- Add declineBattlefield() that sends CMSG_BATTLEFIELD_PORT(action=0)
- acceptBattlefield() optimistically sets statusId=3 to dismiss popup
- renderBgInvitePopup: colored countdown bar (green→yellow→red),
named BG (Alterac Valley, Warsong Gulch, etc.), auto-dismisses on expiry
- Vendor window: replace manual stat-only tooltip with full renderItemTooltip
(now shows bind type, slot, weapon stats, armor, extra stats, spell effects,
flavor text, and sell price — consistent with inventory)
- Loot-roll popup: add item icon and hover tooltip via renderItemTooltip
- Loot-roll: pre-fetch item info via queryItemInfo when roll prompt appears
- Parse SMSG_ALL_ACHIEVEMENT_DATA on login to populate earnedAchievements_ set
- Pass achievement name through callback so toast shows name instead of ID
- Add renderItemTooltip(ItemQueryResponseData) overload for loot/non-inventory contexts
- Loot window now shows full item tooltip on hover (stats, sell price, bind type, etc.)
Fallback sphere for GameObjects without a loaded renderer instance was 2.5f,
causing invisible/unloaded chairs in Goldshire Inn to be accidentally targeted
during camera right-drag. This sent CMSG_GAMEOBJ_USE which set server stand
state to SIT, trapping the player until a stand-up packet was sent.
Reduce fallback radius to 1.2f and height offset to 1.0f so only deliberate
close-range direct clicks register on unloaded GO geometry.
Camera controller / sitting:
- Any movement key (WASD/QE/Space) pressed while sitting now clears the
sitting flag immediately, matching WoW's sit-to-stand-on-move behaviour
- Added StandUpCallback: when the player stands up via local input the
callback fires setStandState(0) → CMSG_STAND_STATE_CHANGE(STAND) so
the server releases the sit lock and restores normal movement
- Fixes character getting stuck in sit state after accidentally
right-clicking a chair GO in Goldshire Inn (or similar)
Nameplates:
- Use getRenderPositionForGuid() (renderer visual position) as primary
source for nameplate anchor, falling back to entity X/Y/Z only when
no render instance exists yet; keeps health bars in sync with the
rendered model instead of the parallel entity interpolator
Player buff bar and target debuff bar icons now show full spell tooltip
(school, cost, cast time, range, description) on hover, matching the
action bar and spellbook. Falls back to plain spell name if DBC is not
loaded. Remaining aura duration is shown below the spell body.
Expose SpellbookScreen::renderSpellInfoTooltip() as a public method,
then use it in the action bar slot tooltip. Action bar spell tooltips
now show the same full tooltip as the spellbook: spell school (colored),
mana/rage/energy cost, cast time, range, cooldown, and description.
Falls back to a plain spell name if DBC data is not yet loaded.
Hearthstone location note is appended after the rich body.
Cooldown text moved inside each branch for consistent styling.
- Use getQualityColor() for consistent quality coloring (choice+fixed)
- Show item icons for fixed rewards (previously text-only)
- Replace useless "Reward option" tooltip with real item name+description
- Render icon before selectable label (not after) for choice rewards
- Call ensureItemInfo for all reward items to trigger async fetch
- Use structured bindings (C++17) to unify icon+color resolution