Left-click targeting, the right-click world picker and the hover cursor each
carried their own copy of the same traversal — build a hit sphere per entity,
ray-test it, rank units against objects by distance to the entity centre,
prefer a hostile. Around ninety lines apiece, agreeing on everything that
matters and requiring lockstep edits to stay that way. The corpse fix before
this had to be applied twice for exactly that reason, and the hover copy was
already out of step: it had no critter case, so the cursor promised a click
on something a click would miss.
pickScene does the traversal once and returns what it found; each caller
applies its own rules to the result. What genuinely differs is now stated as
parameters — the right-click picker's taller, tighter fallback sphere and its
chair skip — and what only one caller collects (a hooked bobber, quest
objective objects) comes through a per-hit callback rather than a third copy
of the loop.
Net 265 lines out of game_screen.cpp. The two fixed-radius bobber tests stay
where they are: those aim at one known GUID rather than searching the scene.
The auth screen decoded its background on the frame it first appeared: a
1408x768 PNG in a 2.4MB file, which is 2.4MB of zlib inflate plus the GPU
upload, measured at ~190ms. Start the decode on a worker when the screen
first renders and upload on whichever frame it lands; the existing
descriptor-set guard already skips drawing the background until then, so
the screen simply appears without it for a moment.
The GPU half stays on the main thread, since that is where it has to be —
loadBackgroundImage becomes uploadBackgroundImage and takes pixels the
worker already decoded.
Honest note on value: this is a static screen, so the hitch was hard to
perceive. The loading screen decodes its own image the same way and is
left alone, being a screen that is already a wait.
Auction results carry the rolled random property separately from the item
template, so browse-tab tooltips rendered only base stats. Build an
instance-aware ItemDef from the query response plus the auction's
randomPropertyId/suffixFactor, using the same bonus folding as inventory
tooltips, so "of the ..." suffix stats are visible before bidding.
Makes the ItemDef overload of renderItemTooltip public so callers outside
InventoryScreen can use it.
Add a 'Max Out Character' panel to the GM command screen that detects
your class + active expansion and queues a GM command sequence: set max
level (60/70/80), learn all class spells + talents, max skills, optional
1000g, and add a class/expansion-appropriate gear kit. Commands drain one
per frame to avoid server chat-flood protection. Per-slot toggles let you
pick which parts to apply. Gear lists live in bis_gear_data.cpp — a
curated, easily-extended starter set (anchored on class legendaries);
the server validates each .additem so unknown IDs are skipped.
Parse each command's syntax into labeled form fields instead of an
editable raw string: #x -> numeric input, $x -> text input, a/b ->
dropdown, [word] -> optional flag checkbox. Shows a live 'Will send'
preview, defaults player/name fields to the current target when blank,
and keeps an 'Edit manually' escape hatch. Add 12 commonly-used commands
(reset level/stats/spells/talents/achievements/all, repairitems,
additemset, modify arenapoints/drunk/faction/xp/phase) and de-duplicate
the scattered reset entries.
Add a searchable GM command browser (micro-menu 'GM' button) over the
existing 195-entry kGmCommands reference. Left pane lists commands
grouped by first token (flat filtered list while searching) with a
max-permission-level filter; right pane shows syntax/description, a
security badge, and an editable command line. Send (or Enter) dispatches
the command to the server as a SAY chat message ('.' prefix, AzerothCore
convention) — the server enforces the real permission level.
- The bank 'Combine bags' (contiguous vs per-bag sections) toggle was a
function-local static, so it reset to split on every relaunch. Make it
a persisted WindowManager member (bank_combine_bags in settings.cfg),
saved on toggle and loaded at startup.
- Guild bank showed no items or slots: the grid only iterated the slots
the server sent (sparse, often just occupied ones — nothing for an
empty tab). Render a fixed 98-slot grid (14x7) and look items up by
slotId, so slots always appear and items land in the right cells.
- Right-clicking a bag item while the guild bank is open now deposits it
into the first free slot of the viewed tab (guildBankDepositFromInventory).
Bags auto-open when the guild bank opens so items are reachable, and a
hint line documents left-click withdraw / right-click deposit.
- Fix latent bug: clicking a guild bank tab only sent a query and never
updated the active tab, so withdraw/deposit always targeted tab 0. Sync
guildBankActiveTab_ from each SMSG_GUILD_BANK_LIST (server tags it).
- Add ESC-to-close for the guild bank window.
- Bank: right-clicking a bank item now withdraws it to the bags. The
bank slot renderer had left-click drag and shift-link but no
right-click handler, so right-clicks did nothing. Routes through
withdrawItem(), which now uses CMSG_AUTOSTORE_BANK_ITEM so the server
places it in any free bag slot (retail), not just the backpack.
- Guild bank: interacting with a guild-vault GameObject (type 34) now
opens it. Nothing called openGuildBank(), and openGuildBank() never
sent CMSG_GUILD_BANKER_ACTIVATE (only queried a tab), so no bank list
arrived. Detect type 34 in interactWithGameObject and send the
activate; also mark the window open when SMSG_GUILD_BANK_LIST arrives.
- Crafting: the recipe list pane is now a draggable splitter (was fixed
width, then a fixed proportion) so long recipe names can be read.
Load Achievement.dbc's IconID (added to the WotLK DBC layout, field 42) into
the achievement cache and resolve it through SpellIcon.dbc to the artwork.
The earned list now renders a bordered 32px icon with the name and points
beside it, replacing the gold-star glyph; a star placeholder fills the slot
while an icon streams in or when one is absent. Icon textures are lazily
BLP-loaded and cached in WindowManager with a per-frame upload cap.
Move the WoW description token resolver onto GameHandler::formatSpellDescription
and route buff/aura tooltips, the spellbook, and item Use/Equip effect lines
through it, so 'increased by $s1' etc. no longer render raw. Add the $/N;
division token used by food regen ('Restores $/5;s1 health per second').
Prefer Spell.dbc's full Description column over the short Tooltip in both the
spell_handler cache and the spellbook loader, so food tooltips include the
'become well fed and gain N Stamina and Spirit' clause instead of a bare
one-liner. The talent screen now delegates to the shared formatter.
WotLK Tooltip (139->187) and TBC Rank (136->144) / Tooltip (154->178)
layout indices assumed the wrong locale-block stride and landed on empty
columns; point them at the verified enUS strings.
Rework the talent description formatter into a real $-token resolver: it
handles $s/$o/$m/$M base points and $d durations with cross-spell
$<spellId> references (e.g. $14201d), plus $l/$g plural/gender forms,
using live spell data via GameHandler. Unresolvable tokens ($h proc chance,
$t period) are stripped cleanly instead of rendering raw, so Enrage now
reads 'a 4% damage bonus for 12 sec' rather than '$14201s1% ... $14201d'.
The talent panel read Spell.dbc's Tooltip column, which the layouts pointed
at an empty locale field, so hovering a talent showed only name and rank. Add
the correct Description column to every expansion's DBC layout (verified
against each Spell.dbc's string blocks) and read it in the talent screen,
substituting $s/$o magnitude tokens with the rank's EffectBasePoints so
descriptions read with concrete numbers. Unlearned talents show their rank-1
effect under 'Effect:'.
The Create Auction dropdown only iterated the 16-slot base backpack, hiding
every item held in an equipped bag. It now enumerates the backpack plus all
equipped bags and posts the selected item by its server GUID via the new
auctionSellItemByGuid path, so any container can be listed.
It sat near the bottom of the screen, far from the combat it describes,
and was pinned there by NoMove/NoInputs. It now follows the bottom edge of
the target frame, and the first drag latches a position that persists
across sessions; right-click sends it home.
The marks were text symbols, but the ImGui font has no code points for
skull, cross, moon and most of the others, so they rendered as '?' boxes
beside the name. Load Blizzard's UI-RaidTargetingIcon art instead and hang
it over the unit's head like the original client, with the same textures
reused at small size in the target, focus, minimap and party displays.
Keyring slots were rendered with placeholder backpack addressing
(SlotKind::BACKPACK, index -1), so keys could not be picked up, moved, or
used. Add a real SlotKind::KEYRING / HeldSource::KEYRING with correct wire
addressing (bag 0xFF, slot 86+i) wired through pickup, place, swap-source,
cancel, and right-click-use paths across all inventory view modes. Also
show at least one keyring row whenever Show Key Ring is enabled so it stays
visible (and a drop target) even when empty, instead of hiding when it
holds no keys.
Add an AddOns button to the character-select footer that opens a window listing
every installed addon (title, version, author, notes) with a per-addon
enable/disable checkbox. Enabled state persists to <config>/addons.cfg and is
honored by loadAllAddons (disabled addons are skipped and hidden from the Lua
VM); /reload picks up changes. Toggles apply on the next world enter, matching
retail behavior.
Right-click interact/attack fired on button press with no drag detection, so
pressing RMB to rotate the view would raypick a nearby hostile unit and start
auto-attacking it. Mirror the left-click tap/drag pattern: record the press
position and only interact/attack on release when the cursor moved <5px (a tap),
treating a larger move as a camera rotate.
Pickup now removes the action from its slot so it visibly rides the cursor
instead of staying put. Fix a reference-invalidation bug in the swap where the
live slot reference was mutated before the displaced action was read, causing
the existing item to stay and the carried one to be lost. Displaced actions now
fall back into the emptied origin; cancel/off-bar paths restore or delete safely.
Add a 'Zone' filter mode to the on-screen quest tracker that shows only quests
whose zoneOrSort matches the player's current zone (getWorldStateZoneId), and
make it the default. Falls back to showing all quests when the zone isn't known
yet. Filter now cycles All / Active / Done / Zone.
Chat window resize was gated behind unlocking (locked forced NoResize); resize is
now decoupled from lock so the grips always work — locking only pins position and
blocks moving. Size persists in either state. Target frame now auto-sizes to its
widest line (name, subtitle, guild, level/classification row) with a 250px floor
instead of a fixed width that clipped the level line.
Hold left-click on a filled slot for 0.6s to lift its action onto the cursor
(a rising fill shows the charge); a quick click still casts/uses. While carried,
click another slot to swap, click the source again or empty space to cancel, or
right-click off the bar to remove. Wires up the previously dormant drag state.
Chat window size is now captured while unlocked and persisted (chat_window_w/h),
so a resized window keeps its size after locking or restart; a size constraint
keeps it from collapsing. Target frame grows to fit long names, guild tags, and
creature subtitles instead of clipping at a fixed 250px.
Adds an opt-in world-map layer that marks every rare (rank 4) and rare-elite
(rank 2) creature the client currently has loaded, so a rare that is out shows
as a gold/silver diamond with its name in the current zone. Entities only exist
near the player, so a marker means that rare is genuinely spawned right now.
New RareTrackerLayer follows the existing overlay-layer pattern; the HUD feeds it
the filtered live units each frame. Gated behind a new 'Show Rare Tracker'
Interface setting (persisted), off by default.
Adds a server-side inventory-type (slot) filter carried in the
CMSG_AUCTION_LIST_ITEMS auctionSlotID field, plus buyout-only and
max-price page filters applied client-side to the current results page.
Auction item tooltips now compare against the equipped slot item.
SMSG_CAST_RESULT and SMSG_CAST_FAILED append the SpellFocusObject id on
requires-spell-focus failures, but the parsers stopped at the result
byte, leaving only a generic 'Requires spell focus' message. Read the
trailing id on all three expansion parsers, resolve it through
SpellFocusObject.dbc, and report 'Requires Cooking Fire nearby.' /
'Requires Anvil nearby.' etc., with a clearer fallback when the id is
unavailable.
Casting a profession spell (Cooking, First Aid, Alchemy, Smelting, ...)
from the spellbook, action bar, or /cast now opens a dedicated crafting
window instead of sending a no-op cast. The window shows a skill progress
bar, recipes with item icons colored orange/yellow/green/gray by
difficulty, can-make counts, search and have-reagents filters, reagent
have/need lists, and quantity + Create All controls backed by the craft
queue. Trainer panel difficulty lambdas now delegate to the shared
recipeDifficultyColor/Label helpers.
renderBuffBar is ~400 lines and the layout maths was buried in the middle of it,
which is exactly where a silent bug lives: the row does not wrap, so it has a hard
capacity, and it has to stay clear of the minimap at every resolution and scale.
The arithmetic moves to buff_bar_layout.hpp as a pure function and combat_ui.cpp
calls it, so the tested maths is the maths that runs. 408 assertions sweep
resolutions from 1280x720 to 4K and scales from 0.75x to 1.5x, checking the row
never overlaps the minimap, never runs off screen, drops overflow rather than
wrapping, and always gives weapon enchants their slots before auras.
The slider only scales the buff bar, so the name overpromised. Renamed the
setting, its label and its config key (ui_scale -> buff_bar_scale), and moved it
under a Buff Bar heading in Settings > Interface.
The player effects bar now sits along the top of the screen, right of centre, as
one long row: it is right-aligned to end just left of the minimap and grows
leftwards as auras are gained, so buffs and debuffs no longer wrap onto extra
rows. Icons are 25% larger (32px -> 40px at the 1080p reference) and now scale
with the window size, with a new UI Scale slider in Settings > Interface layered
on top and persisted to the config.
New PlayerVoiceManager plays the player's spoken error lines ("Not enough
mana", "I'm out of range", ...) on cast failures, inventory/buy/trainer
errors, loaded lazily per race/gender and toggled via the Character Speech
checkbox in audio settings.
The bubble shows the quest count, shares the tracker's right-edge anchor,
can be dragged without expanding, and the collapsed state persists in
settings.
Show every required objective with current/required counts (including
0-progress ones from quest query data), add a per-quest untrack button and
an All/Active/Done filter, sort entries lowest level first with the level
shown in the title, and normalize $b text tokens at all quest-log text
entry points so markers no longer leak into titles and objectives.
Vanilla 1.12 servers disagree on the auth protocol byte — vmangos-derived
realms speak 8, stock mangos/cmangos speak 3 — and a profile can only name
one, so pointing the classic profile at the other kind hard-failed at login.
Classic/Turtle now retry once on the alternate byte when the failure looks
protocol-shaped (unparseable handshake, version rejection, drop during
challenge) and never when credentials were rejected, so a wrong password
can't trigger a second lockout-provoking attempt.
Also fixes the realm-list recovery heuristic from #100: it only fired when
the shifted parse left a non-empty address, so vanilla realms sending flags=0
(both name and address parse empty) silently produced garbage fields instead.
Keyed on the empty name alone, and covered by new realm-list parser tests.
Tabs get tooltips, right-click mark-as-read, Ctrl+wheel cycling, and now sync
the input to their destination (Whispers/Guild/Trade). Adds a history search
filter, an inline quick-settings menu, a colored availability-aware chat type
picker, Tab-cycling of chat types on an empty input, contextual input hints,
and numbered channel picker; message context menus show time and channel.
Share the backpack footer (Sort Bags + money) with the All Bags window, flag
quivers/ammo pouches/profession bags via Inventory::isBagSpecial so sorting
skips them, and tint/label their slots so restricted space isn't mistaken for
general-purpose slots. Also name container/quiver subclasses in tooltips.