Equipment: removed the visibleItemLayoutVerified_ gate from
updateOtherPlayerVisibleItems(). The default WotLK field layout (base=284,
stride=2) is correct and should be used immediately. The verification
heuristic was silently blocking ALL other-player equipment rendering by
queuing for auto-inspect (which doesn't return items in WotLK anyway).
Follow: auto-follow now uses run speed (autoRunning) instead of walk speed.
Also uses squared distance for the distance checks.
Commands: /quit, /exit aliases for /logout; /difficulty normal/heroic/25/25heroic
sends CMSG_CHANGEPLAYER_DIFFICULTY.
Inspect: CMSG_INSPECT was writing full uint64 GUID instead of packed GUID.
Server silently rejected the malformed packet. Fixed both InspectPacket and
QueryInspectAchievementsPacket to use writePackedGuid().
Follow: was a no-op (only stored GUID). Added client-side auto-follow system:
camera controller walks toward followed entity, faces target, cancels on
WASD/mouse input, stops within 3 units, cancels at 40+ units distance.
Party commands:
- /lootmethod (ffa/roundrobin/master/group/nbg) sends CMSG_LOOT_METHOD
- /lootthreshold (0-5 or quality name) sets minimum loot quality
- /raidconvert converts party to raid (leader only)
Equipment diagnostic logging still active for debugging naked players.
Target frame: add Follow, Clear Target, and Set Raid Mark submenu to the
right-click context menu (Inspect, Trade, Duel were already present).
Equipment diagnostics: add LOG_INFO traces to updateOtherPlayerVisibleItems()
and emitOtherPlayerEquipment() to debug why other players appear naked.
Logs the visible item entry IDs received from the server and the resolved
displayIds from itemInfoCache. Check the log for "emitOtherPlayerEquipment"
to see if entries arrive as zeros (server not sending fields) or if
displayIds are zero (item templates not cached yet).
Mail: change money/COD fields from uint32 to uint64 in CMSG_SEND_MAIL and
SMSG_MAIL_LIST_RESULT for WotLK 3.3.5a. Classic keeps uint32 on the wire.
Fixes money truncation and packet misalignment causing mail failures.
Other-player capes: add cape texture loading to setOnlinePlayerEquipment().
The cape geoset was enabled but no texture was loaded, leaving capes blank.
Now mirrors the local-player path: looks up ItemDisplayInfo.dbc, finds cape
texture candidates, applies via setGroupTextureOverride/setTextureSlotOverride.
Zone toasts: suppress duplicate zone toast when the zone text overlay is
already showing the same zone name. Fixes double "Entering: Stormwind City".
Network: enable TCP_NODELAY on both auth and world sockets after connect(),
disabling Nagle's algorithm to eliminate up to 200ms buffering delay on
small packets (movement, spell casts, chat).
Rendering: track material and bone descriptor sets in M2 renderer to skip
redundant vkCmdBindDescriptorSets calls between batches sharing same textures.
- Replace count()+operator[] double lookups with find() or try_emplace()
in gameObjectInstances_, playerTextureSlotsByModelId_, onlinePlayerAppearance_
- Add Entity::isUnit() helper; replace 5 dynamic_cast<Unit*> in per-frame
UI rendering (nameplates, combat text, pet frame) with isUnit()+static_cast
- Add constexpr kInv255 reciprocal for per-pixel normal map generation loops
in character_renderer and wmo_renderer
- Hoist DBC field index lookups before loops in game_handler (7 DBC iteration loops)
- Cache getSkybox()/getPosition() calls instead of redundant per-frame queries
- Merge textureHasAlphaByPtr_ + textureColorKeyBlackByPtr_ into single map
- Add constexpr for DEG_TO_RAD, reciprocal constants, physics delta
- Add reserve() for WMO/M2 collision grid queries and portal BFS
- Frustum plane normalize: inversesqrt instead of length+divide
- M2 particle emission: inversesqrt for direction normalization
- Parse creature display IDs from query response
- UI: show spell names/IDs as fallback instead of "Unknown"
Squared distance optimizations across 30 files:
- Convert glm::length() comparisons to glm::dot() (no sqrt)
- Use glm::inversesqrt() for check-then-normalize patterns (1 rsqrt vs 2 sqrt)
- Defer sqrt to after early-out checks in collision/movement code
- Hottest paths: camera_controller (21), weather particles, WMO collision,
transport movement, creature interpolation, nameplate culling
Container and algorithm improvements:
- std::map<string> → std::unordered_map for asset/DBC/MPQ/warden caches
- std::mutex → std::shared_mutex for asset_manager and mpq_manager caches
- std::sort → std::partial_sort in lighting_manager (top-2 of N volumes)
- Double-lookup find()+operator[] → insert_or_assign in game_handler
- Add reserve() for per-frame vectors: weather, swim_effects, WMO/M2 collision
Threading and synchronization:
- Replace 1ms busy-wait polling with condition_variable in character_renderer
- Move timestamp capture before mutex in logger
- Use memory_order_acquire/release for normal map completion signaling
API additions:
- DBC getStringView()/getStringViewByOffset() for zero-copy string access
- Parse creature display IDs from SMSG_CREATURE_QUERY_SINGLE_RESPONSE
Add kCastGreen (interruptible cast bar, 5 uses) and kQueueGreen
(queue status / talent met, 7 uses across game_screen + talent_screen).
Remove commented-out renderQuestMarkers call (replaced by 3D billboards).
The findMemType/findMemoryType helper in auth_screen, loading_screen,
and vk_context returned 0 on failure — a valid memory type index.
Changed to return UINT32_MAX and log an error, so vkAllocateMemory
receives an invalid index and fails cleanly rather than silently
using the wrong memory type.
Add [[nodiscard]] to VkBuffer::uploadToGPU/createMapped and
VkContext::initialize/recreateSwapchain so callers that ignore
failure are flagged at compile time. Suppress with (void) cast at
3 call sites where failure is non-actionable (resize best-effort).
game_screen: fsrScales, fsrScaleFactors, kTotemInfo, kRaidMarks,
kTimerInfo, kNPMarks, kCellMarks, kPartyMarks, kMMMarks, kCatOrder
keybinding_manager: actionMap
All static const arrays in UI files are now constexpr where possible.
- Move itemKeys/spellKeys/thrKeys to shared kItemSetItemKeys/
kItemSetSpellKeys/kItemSetThresholdKeys in ui_colors.hpp, removing
5 identical local definitions across game_screen and inventory_screen
- Widen totem timer snprintf buffer from 8 to 16 bytes (defensive)
- Promote kStatTooltips to constexpr
Deduplicate class/race bitmask arrays (3 copies each → 1 shared) and
socket type definitions (3 copies → 1 shared). Eliminates ~80 lines of
repeated struct definitions across game_screen.cpp and inventory_screen.cpp.
- Move kDispelNames to file-scope constexpr, removing 2 duplicate local
definitions in raid/party frame rendering
- Promote kTotemColors and kReactDimColors from static const to constexpr
- Replace std::to_string + string concat for ImGui widget IDs with
snprintf into stack buffers (avoids heap allocations in render loops)
Add kDarkRed, kSoftRed, kHostileRed, kMediumGray to ui_colors.hpp and
replace 31 inline ImVec4 literals across game_screen, character_screen,
inventory_screen, and performance_hud. Also replace local color aliases
in performance_hud with shared constants.
Wrap string-to-number conversions in try-catch where input comes from
external sources (realm address port, last_world.cfg, keybinding config,
ADT tile filenames) to prevent crashes on malformed data.
- Add kBrightGold, kPaleRed, kBrightRed, kLightBlue, kManaBlue, kCyan to ui_colors.hpp
- Replace 61 inline ImVec4 color literals across game_screen, inventory_screen,
talent_screen, and world_map with named constants
- Remove const_cast in character_renderer render loop by using non-const iteration
Replace remaining ImVec4(1.0f, 0.82f, 0.0f, 1.0f) gold color literals
in game_screen.cpp (19) and talent_screen.cpp (1) with the shared
colors::kTooltipGold constant. Zero inline gold literals remain.
Add colors::kTooltipGold to ui_colors.hpp and replace 14 inline
ImVec4(1.0f, 0.82f, 0.0f, 1.0f) literals in inventory_screen.cpp
for item set names, unique markers, and quest item indicators.
Add getInventorySlotName() and renderBindingType() to ui_colors.hpp,
replacing 3 copies of the 26-case slot name switch (2 inventory_screen
+ 1 game_screen) and 2 copies of the binding type switch. Removes ~80
lines of duplicate tooltip code.
Extract getEnchantmentNames() to share a single SpellItemEnchantment.dbc
cache between both renderItemTooltip overloads, replacing two identical
19-line lazy-load blocks with single-line references.
Extract renderSettingsInterfaceTab() (108 lines) from
renderSettingsWindow(). 6 of 7 tabs now have dedicated methods;
only Video remains inline (shares init state with parent).
Extract renderSettingsGameplayTab() (162 lines) and
renderSettingsControlsTab() (96 lines) from renderSettingsWindow().
5 of 7 settings tabs are now in dedicated methods; only Video and
Interface remain inline (they share resolution/display local state).
Extract renderSettingsAudioTab() (110 lines), renderSettingsChatTab()
(49 lines), and renderSettingsAboutTab() (48 lines) from the 1013-line
renderSettingsWindow(). Reduces it to ~806 lines.
Add renderCoinsFromCopper(uint64_t) overload in ui_colors.hpp that
decomposes copper into gold/silver/copper and renders. Replace 14
manual 3-line decomposition blocks across game_screen and
inventory_screen with single-line calls.
Use kRed, kBrightGreen, kDarkGray, kLightGray from ui_colors.hpp across
8 UI files, eliminating duplicate ImVec4 color definitions throughout
the UI layer.
Create shared include/ui/ui_colors.hpp with common ImGui color constants,
item quality color lookup, and renderCoinsText utility. Remove 3 duplicate
renderCoinsText implementations and 3 duplicate quality color switch
blocks across game_screen, inventory_screen, and quest_log_screen.
Replace ~37 remaining C-style casts with static_cast across 16 files.
Extract named color constants (kColorRed/Green/Yellow/Gray) and dialog
window flags (kDialogFlags) in game_screen.cpp, replacing 72 inline
literals. Normalize keybinding_manager.hpp to #pragma once.
Remove the isProfessionSpell sound suppression so crafting spells play
precast and cast-complete audio like combat spells. Crafting was
previously silent by design but users expect audio feedback.
Add "Create All" button to the tradeskill UI that queues 999 crafts.
The server automatically stops the queue when materials run out
(SPELL_FAILED_REAGENTS cancels the craft queue). This matches the
real WoW client's behavior for batch crafting.
Validation layers revealed 9965 VkSamplers allocated against a device
limit of 4000 — every VkTexture created its own sampler even when
configurations were identical. This exhausted NVIDIA's sampler pool
and caused intermittent SIGSEGV in vkCmdBeginRenderPass.
Add a thread-safe sampler cache in VkContext that deduplicates samplers
by FNV-1a hash of all 14 VkSamplerCreateInfo fields. All texture,
render target, renderer, water, and loading screen sampler creation
now goes through getOrCreateSampler(). Textures set ownsSampler_=false
so shared samplers aren't double-freed.
Also auto-disable anisotropy in the cache when the physical device
doesn't support the samplerAnisotropy feature, fixing the validation
error VUID-VkSamplerCreateInfo-anisotropyEnable-01070.
Inventory sort: clicking "Sort Bags" now generates CMSG_SWAP_ITEM packets
to move items server-side (one swap per frame to avoid race conditions).
Client-side sort runs immediately for visual preview; server swaps follow.
New Inventory::computeSortSwaps() computes minimal swap sequence using
selection-sort permutation on quality→itemId→stackCount comparator.
World map: fix continent bounds derivation that used intersection (max/min)
instead of union (min/max) of child zone bounds, causing continent views
to display zoomed-in/clipped.
Update README.md and docs/status.md with current features, release info,
and known gaps (v1.8.2-preview, 664 opcode handlers, NPC voices, bag
independence, CharSections auto-detect, quest GO server limitation).
Remove the forced backpack-open constraint that prevented closing the
backpack while other bags were open. Each bag window is now independently
closable regardless of which others are open.
Add off-screen position reset to individual bag windows (renderBagWindow)
so bags saved at positions outside the current resolution snap back to
their default stack position.
Chest-type GOs now send CMSG_GAMEOBJ_USE immediately followed by
CMSG_LOOT in the same frame. The USE handler opens the chest, then the
LOOT handler reads the contents — both processed sequentially by the
server. Previously only CMSG_LOOT was sent (no USE), which failed on
AzerothCore because the chest wasn't activated first.
Reset the Bags window position to bottom-right if the saved position
is outside the current screen resolution (e.g. after a resolution
change or moving between monitors).