Allow equipped bags to be moved through the shared inventory pickup/drop flow, including dragging from bag contents to bag bar and back from bag bar into bag/backpack slots.
Changes:
- Add InventoryScreen APIs to begin pickup from an equipment slot and drop held item into a target equipment slot.
- Treat inventory type 18 (bags) as valid drops on BAG1-BAG4 during slot validation.
- Route equipment placement in online mode through swapContainerItems with explicit src/dst addressing for deterministic bag slot moves.
- Update bag bar hover-drop path to use InventoryScreen drop API instead of direct local slot mutation.
- On bag bar drag start, hand off to inventory held-item drag when inventory/character UI is open so drops into bag/backpack slots work.
- Use interpolated moveInstanceTo() for normal server NPC position deltas so walk/run animations play instead of visual sliding
- Keep hard snaps only for dead units and large corrections/teleports
- Add per-creature render position cache to drive interpolation safely across frames and clear it on spawn/despawn/reset
- Keep minimap controls visible regardless of quest-status availability
- Correct minimap NPC/quest marker projection mapping to match minimap shader transform
- Add optional nearby NPC minimap dots setting (default OFF), exposed in Gameplay > Interface and persisted in settings
- Right-click attack fallback for non-interactable hostile creatures
- Robust creature skin path resolution for WotLK/non-humanoid display skin fields
- Strengthened client-side anti-overlap spacing for active melee targets (including wolf/worg models)
- Minimap questgiver markers now use live minimap view radius and exact minimap center to prevent player-relative drift
Selection ring rendering has been upgraded to better match WoW-like target feedback and remain readable across complex geometry.
Visual updates:
- Reworked ring mesh/shader from a simple two-band strip to a unit-disc + radial fragment shaping.
- Implemented a thinner outer ring profile.
- Added an inward color/alpha gradient that fades from the ring toward the center.
Placement/anchoring updates:
- Added CharacterRenderer::getInstanceFootZ() to query model foot plane from instance bounds.
- Added Application::getRenderFootZForGuid() to resolve per-GUID foot height via live instance mapping.
- Updated GameScreen target selection placement to anchor the effect at target foot Z.
Ground/surface stability:
- In renderSelectionCircle(), added floor clamping against terrain, WMO, and M2 floor probes at target XY.
- Raised final placement offset to reduce residual clipping on uneven surfaces.
Depth/visibility behavior:
- Added polygon offset during ring draw to reduce z-fighting.
- Disabled depth testing for the selection effect draw pass (with state restore) so the ring remains visible through terrain/WMO occluders, per requested behavior.
State safety:
- Restored modified GL state after selection pass (depth test / polygon offset / depth mask / cull).
Build validation:
- Verified with cmake build target "wowee" after each stage; final build succeeds.
Combat state/UI:
- Split attack intent from server-confirmed auto attack in GameHandler.
- Add explicit combat state helpers (intent, confirmed, combat-with-target).
- Update player/target frame and renderer in-combat indicators to use confirmed combat.
- Add intent-only visual state in UI to avoid false combat feedback.
Animation correctness:
- Correct combat-ready animation IDs to canonical ready stances (22/23/24/25).
- Remove hit/wound-like stance behavior caused by incorrect ready IDs.
Classic/TBC/Turtle melee reliability:
- Tighten melee sync while attack intent is active: faster swing resend, tighter facing threshold, and faster facing cadence for classic-like expansions.
- Send immediate movement heartbeat after facing corrections and during stationary melee sync windows.
- Handle melee error opcodes explicitly (NOTINRANGE/BADFACING/NOTSTANDING/CANT_ATTACK) and immediately resync facing/swing where appropriate.
- On ATTACKSTOP with persistent intent, immediately reassert ATTACKSWING.
- Increase movement heartbeat cadence during active classic-like melee intent.
Server compatibility/anti-cheat stability:
- Restrict legacy force-movement/speed/teleport ACK behavior for classic-like flows to avoid unsolicited order acknowledgements.
- Preserve local movement state updates while preventing bad-order ACK noise.
Bag bar: left-click drag bags to swap positions using CMSG_SWAP_ITEM
with INVENTORY_SLOT_BAG_0 (255). Local optimistic swap for instant
feedback. Camera controller now respects ImGui WantCaptureMouse.
Vendor auto-open bags only triggers once per session.
Fix opcodes: CMSG_GAMEOBJECT_USE 0x01B→0x0B1 (typo caused
SMSG_FORCEACTIONSHOW spam), CMSG_CANCEL_AURA 0x033→0x136,
SMSG_SELL_ITEM 0x1A4→0x1A1.
Replace 2D screen-space ding rings with real WoW LevelUp.m2 particle/geometry
effect. Fix FBlock particle color parsing (C3Vector floats, not CImVector bytes)
which was producing blue/red instead of golden yellow. Spell effect models bypass
particle dampeners, glow sprite conversion, Mod→Additive blend override, and all
collision (floor/wall/camera) to prevent camera zoom-in. Other players' level-ups
trigger the 3D effect at their position with group chat notification. F7 hotkey
for testing.
- getSpellCastResultString now returns "Not enough rage/energy/focus/runic power" based on player power type instead of always "Not enough mana"
- Fix NPC combat messages concatenating onto previous lines by combining prefix+body into single renderTextWithLinks call instead of TextWrapped+SameLine
- Improve generic error strings to be more WoW-authentic (Invalid target, Target not in line of sight, etc.)
Warriors (rage) and rogues/druids (energy) had no power bar rendered
when maxPower was 0 — the server often omits UNIT_FIELD_MAXPOWER1 for
rage-based classes since rage starts at 0. Rage and energy always cap
at 100, so default maxPower to 100 when the server sends 0 for those
power types. Also unified bar height to 18px (matching health bar) and
added proper power-type colour to the target frame bar (was always blue).
- game_handler.cpp: use-after-move on node.id after std::move(node)
(save nodeId before the move)
- tcp_socket.cpp, world_socket.cpp: virtual call in destructor bypasses
dispatch; use qualified TCPSocket::disconnect() / WorldSocket::disconnect()
to make intent explicit
- wmo_renderer.cpp: float loop counters risk precision drift; replace with
integer step counts and reconstruct float from index
- game_screen.cpp: (float + 0.5) cast to int is incorrect rounding;
use std::lround instead
- use interpolated target position (getX/Y/Z) for selection circle placement
- avoids drawing circle at movement destination before creature arrives
- keeps previous target interpolation update guarantees
- use movement animation (prefer run, fallback walk) for server-driven creature moves
- synthesize short movement duration for duration-less movement deltas to avoid glide/attack-pose sliding
- return to idle after both walk/run movement states
- drive target circle from entity latest position and always interpolate selected/engaged targets
- parse and cache item class/subclass, damage range, and attack delay from item query responses
- render weapon damage, speed, and DPS in the shared item-link tooltip
- render weapon damage, speed, and DPS in vendor hover tooltips
- keep armor and primary stat lines intact
PushID(item.slot) causes conflicting IDs when the server sends items with
duplicate or zero slot values. Use the loop index instead, which is always
unique regardless of what the server puts in the slot field.
Auto-detect whether SMSG_LIST_INVENTORY has 7 fields (28 bytes/item, no
extendedCost) or 8 fields (32 bytes/item) per item from packet size. Servers
that omit extendedCost caused every item after the first to have garbage prices
due to misaligned field reads.
Also remove the [+Tokens] hybrid indicator; only show [Tokens] on pure
token-purchased items (buyPrice==0 && extendedCost!=0).
- Trigger ding when UNIT_FIELD_LEVEL increases for player: plays LevelUp sound,
cheer emote animation, and shows screen overlay
- renderDingEffect(): 3 expanding golden rings + "LEVEL X!" / "DING!" text
drawn via ImDrawList foreground (3s duration, fades out last 0.8s)
- triggerDing() wires sound (UiSoundManager::playLevelUp) + emote + overlay
- LevelUpCallback in GameHandler fires on genuine level increase (newLevel > oldLevel)
- "Test: Level Up" button in escape menu triggers ding at current player level
Checkbox in Settings > Gameplay > Loot section. When enabled, all items
are automatically sent CMSG_AUTOSTORE_LOOT_ITEM on loot response (same
as right-click looting each item). Gold always auto-loots regardless.
Setting persists in settings.cfg as auto_loot=0/1.
- Mute button: small [M] button alongside minimap zoom controls, turns red when active; directly sets AudioEngine master volume to 0, restores on unmute; persists in settings.cfg
- Original Soundtrack: checkbox in Settings > Audio that controls whether custom original music tracks (file: prefix) are included in zone music rotation; when disabled, only WoW MPQ tracks play; persists in settings.cfg
- ZoneManager.getRandomMusic() now filters file: paths when OST is disabled, falling back to full list if zone has no non-file tracks
- Handle uppercase \$N in replaceGenderPlaceholders (WoW servers send \$N for player name, we only handled \$n)
- Fix SMSG_QUESTGIVER_REQUEST_ITEMS required item field order: packet sends [displayInfoId, count, itemId] not [itemId, count, displayInfoId], causing wrong items to display (e.g. Ring of Pure Silver instead of Tough Wolf Meat)
Root cause: OpcodeTable::loadFromJson() cleared all mappings before
loading the expansion JSON, so any WotLK opcode absent from Turtle WoW's
opcodes.json (including SMSG_AURA_UPDATE and SMSG_AURA_UPDATE_ALL) was
permanently lost. Changed loadFromJson to patch/merge on top of existing
defaults so only explicitly listed opcodes are overridden.
Also fix isBuff border color: was testing flag 0x02 (effect 2 active)
instead of 0x80 (negative/debuff flag).
Add client-side duration countdown: AuraSlot.receivedAtMs is stamped
when the packet arrives; getRemainingMs(nowMs) subtracts elapsed time so
buff tooltips show accurate remaining duration instead of stale snapshot.
The action bar's inline Spell.dbc loader had a broken field index lookup:
operator[] on the layout map would silently insert 0 if 'Name' wasn't a
key, causing all names to be read from field 0 (the spell ID). Also the
once-only 'attempted' flag meant if assetMgr wasn't ready, it never retried.
Replace the duplicated broken DBC loading in the action bar and buff bar
with SpellbookScreen::lookupSpellName(), which reuses the spellbook's
already-correct loading (expansion layout + WotLK fallback). Remove the
now-unused actionSpellNames/actionSpellDbAttempted/actionSpellDbLoaded fields.
Outgoing events (player attacks enemy: damage dealt, Dodge/Parry when enemy
evades) now float on the right side of screen (near target) instead of above
the player character. Incoming events (enemy attacks player) remain at
center-left (near player).
Dodge/Parry labels now show direction: "Dodge"/"Parry" when the enemy evades
the player's attack (outgoing, gray), and "You Dodge"/"You Parry" when the
player evades an incoming attack (cyan). Crit damage now has distinct colors
for outgoing (bright yellow) vs incoming (orange).
- audio_engine.cpp: cache key was based on vector pointer address, not
content — cache never hit since each asset load produces a new
allocation. Replace with FNV-1a over head+tail bytes + size, giving
correct content-based identity at O(1) cost. Add 256-entry cap with
eviction to prevent unbounded growth over long sessions.
- game_handler.hpp/cpp, spellbook_screen, game_screen: knownSpells was
a std::vector with O(n) std::find lookups scattered across packet
handlers and UI code. Switch to std::unordered_set for O(1) insert,
erase, and membership checks. Update all push_back/erase-remove/find
call sites to use set operations.
Turtle/Classic Spell.csv files are garbled (field 0 is a string rather
than a numeric ID) so loadDBC() falls back to the WotLK binary Spell.dbc
(234 fields). getSpellIcon() was still using the Turtle layout (IconID at
field 117) to read the WotLK binary, producing garbage values and no icons.
When the loaded Spell.dbc has >=200 fields it is the WotLK binary; use the
WotLK IconID field (133) directly, matching the same approach already used
in spellbook_screen.cpp.
- Add TurtlePacketParsers with dedicated movement block parser (Classic format + transport timestamp)
- Fix quest giver status: read uint32 and translate vanilla enum values for Classic/Turtle
- Fix quest accept packet: remove trailing uint32 that vanilla servers reject
- Fix quest details parser: auto-detect vanilla vs WotLK format (informUnit field)
- Fix spellbook and action bar icons: fallback to WotLK DBC field indices when expansion layout fails
- Fix spell cast failure messages: translate vanilla SpellCastResult codes (+1 offset)
- Fix realm list: correct type values (6=RP, 8=RP-PvP) and population thresholds
- Fix music: disable looping for zone music, auto-advance to next random track when finished
- Add music anti-repeat: avoid playing the same track back-to-back
- Make TBC update block parsing resilient (keep parsed blocks on failure instead of aborting)
- Add right-click attack on hostile mobs
- Add name query diagnostic logging
- Fix action bar, bag bar, chat window to track window resize (Always pos)
- Show spell names in cast bar instead of spell IDs
- Play precast/cast-complete sounds via SpellSoundManager
- Fix hearthstone to use CMSG_CAST_SPELL directly (avoids slot sync issues)
- Show map name instead of coordinates in hearthstone tooltip
- Show cooldown time remaining in action bar tooltips
- Search equipped slots and bags for action bar item icons
- Redesign realm screen: back button, larger table/buttons, auto-select
realm with characters, double-click to enter, proportional columns
Only show "Your home has been set" when the player actively changes their
bind point (via innkeeper), not on the initial login sync from the server.
Add "Local" to the auto-join channel list for Turtle WoW compatibility,
with a settings checkbox and persistence.
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
Guild: add disband, leader transfer, public/officer note commands with
roster context menu showing rank names and officer notes column. Auto-refresh
roster after guild events.
Channels: fix city/region channels not working by accepting SMSG_CHANNEL_NOTIFY
during ENTERING_WORLD state (server auto-joins before VERIFY_WORLD) and handling
PLAYER_ALREADY_MEMBER notification.
Whisper: /r now switches to whisper tab and sets target to last sender,
matching WoW behavior.
Camera: extend WMO collision raycasting to work outside WMOs too.
Fix mailbox right-click (transposed CMSG_GAMEOBJECT_USE opcode, missing
mail opcodes in Turtle WoW JSON, decorative GO type filtering). Add
expansion-aware mail packet handling via PacketParsers: Classic format
(single item, no msgSize prefix, Vanilla field order) vs WotLK format
(attachment arrays, enchant slots). Fix CMSG_MAIL_TAKE_ITEM and
CMSG_MAIL_DELETE for Vanilla (no trailing fields). Add pulsing "New
Mail" indicator below minimap, SMSG_RECEIVED_MAIL and
MSG_QUERY_NEXT_MAIL_TIME handlers, and async sender name backfill.
Corrected CharGeosets group assignments verified via vertex bounding boxes:
- Group 4 (401+) = gloves/forearms, Group 5 (501+) = boots/shins,
Group 8 (801+) = sleeves (chest-controlled), Group 9 = kneepads,
Group 13 (1301+) = pants/trousers, Group 20 (2002) = bare feet
- Changed bare shin default from 501 to 502 for better width match
with thigh mesh (0.39 vs 0.32, thighs are 0.42)
- Added clearCompositeCache() to prevent stale composite textures
from being reused across equipment changes
- Fixed character preview geoset defaults to match corrected mapping
Implement full mail send/receive: SMSG_SHOW_MAILBOX, CMSG_GET_MAIL_LIST,
SMSG_MAIL_LIST_RESULT, CMSG_SEND_MAIL, SMSG_SEND_MAIL_RESULT, mail take
money/item/delete/mark-as-read, and inbox/compose UI windows.
Fix periodic stuttering in Stormwind caused by synchronous per-line disk
flushes in the logger — remove fileStream.flush() and std::endl, downgrade
high-volume per-packet/per-model/per-texture LOG_INFO to LOG_DEBUG.
Fix 9 PopStyleVar(2) calls that should be PopStyleVar(1) across
player frame, target frame, cast bar, party frames, buff bar, escape
menu, death dialog, and resurrect dialog. Fix action bar from
PopStyleVar(2) to PopStyleVar(4) to match 4 pushes.
Fix character geoset defaults: 301→302 (bare hands), 701→702 (ears),
1501→1502 (back/cloak), add 802 (wristbands). No WoW character model
uses geoset 301/701/1501; all use 302/702/1502 as base. This fixes
missing hands/arms on undead and other races with separate hand meshes.