- drive shadow light direction from live LightingManager directionalDir
- normalize shadow light to downward-facing convention with grazing-angle guard
- make celestial/sky sun placement robust to directionalDir convention mismatches
- keep visible sun above horizon while preserving shadow alignment
- ensure player transform sync is not gated by third-person so player shadow stays consistent
- hold shadow projection center during movement and snap once on stop to remove delayed catch-up
- smooth foliage caster transitions using blended phase-shifted UV samples
- tune foliage motion frequencies/amplitudes for less popping
- increase shadow map resolution to 2048 and retune terrain PCF texel scale
- increase global shadow strength from 0.62 to 0.68 for stronger, clearer shadows
- keep shadow projection center fixed while moving to remove per-frame projection churn flicker
- replace delayed post-move catch-up with immediate stop transition and idle smoothing
- rework foliage shadow caster motion to use blended phase-shifted UV samples for continuous position transitions
- reduce high-frequency foliage threshold popping by removing threshold warping path
- sharpen terrain receive filtering with tuned 5-tap PCF weights/offset for more detailed shadows
- raise shadow map resolution to 1536 and keep light-space texel snapping for stable sampling
- set shadows enabled by default and lower global shadow strength from 0.65 to 0.62
- keep foliage animation speed consistent between moving and idle at 80%
Hide NPC cloak/object-skin mesh when no cape texture resolves by using a transparent texture fallback, preventing skin-texture bleed on cloaks. Tighten NPC equipment region compositing by slot and add safe humanoid geoset selection to avoid robe-over-pants conflicts and odd pants texturing.
Reduce login/runtime hitching by deferring non-critical world-system initialization across frames, lowering per-frame transport doodad spawn budget, and demoting high-volume transport/MO_TRANSPORT diagnostics to debug. Gate M2 glow diagnostics behind WOWEE_M2_GLOW_DIAG and make zone music prewarm opt-in via WOWEE_PREWARM_ZONE_MUSIC.
Keep the targeting ring anchored near target feet by filtering floor probes to a local Z window, preventing unrelated upper/lower WMO surfaces from hijacking ring height.
Also keeps the ring render pass after WMO geometry and before character/M2 passes so it remains visible through terrain/WMO while still rendering behind units.
- Render selection circle before character/WMO/M2 passes\n- Disable depth test during selection pass so terrain does not occlude it\n- Keep model passes after selection so monsters/M2s render over the ring
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.
Use actual WoW 3.3.5a PlayerExertions and Vox sound paths from MPQ
manifests for attack grunts, wounds, and death sounds. Handle Blizzard
naming quirks (HumanFeamle typo, OrcMale no Final suffix, Scourge→Undead).
Add COMBAT_IDLE animation state with ready weapon stance between swings.
Restore deleted MPQ sound manifest docs.
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.
Two bugs caused the client to look like a bot to server GMs:
1. Strafe animation played during forward+strafe (W+A) instead of the
walk/run animation. Added pureStrafe guard so strafe animations only
play when exclusively strafing (no forward key or auto-run active).
2. CMSG_MOVE_SET_FACING was never sent on mouse-look turns. The server
predicts movement from the last known facing; without SET_FACING the
heartbeat position appeared to teleport each time the player changed
direction. Now sent at up to 10 Hz whenever facing changes >3°,
skipped while keyboard-turning (handled server-side by TURN flags).
Both passes were rendering the entire loaded scene (17×17 tile radius)
into a shadow map that only covers 360×360 world units — submitting
10-50× more geometry than the shadow frustum can actually use.
- TerrainRenderer::renderShadow: skip chunks whose bounding sphere
doesn't overlap the shadow frustum AABB in XY. Reduces terrain draw
calls from O(all loaded chunks) to O(chunks within ~180 units).
- M2Renderer::renderShadow: skip instances whose world AABB doesn't
overlap the shadow frustum in XY. Reduces M2 draw calls similarly.
- Both functions now take shadowCenter + halfExtent parameters.
- SHADOW_MAP_SIZE 2048→1024: 4x fewer pixels rasterized in depth pass
- Replace 9-tap manual PCF loop with single hardware PCF tap in all 4 receiver
shaders (terrain.frag, wmo_renderer, m2_renderer, character_renderer).
GL_LINEAR + GL_COMPARE_REF_TO_TEXTURE already gives 2×2 bilinear PCF per
tap for free, so quality is maintained while doing 9x fewer texture fetches.
- Throttle shadow depth pass to every 2 frames; OpenGL depth texture persists
between frames so receivers always have a valid shadow map. 1-frame lag at
60 fps is invisible.
Add AssetManager hookup to AudioEngine so the path-based playSound2D/3D
overloads can load files on demand rather than requiring preloading.
- Add setAssetManager() to AudioEngine (called during world load alongside
other audio manager initializations)
- playSound2D(mpqPath) now calls assetManager->readFile() then delegates
to the vector<uint8_t> overload (removes the "not yet implemented" warning)
- playSound3D(mpqPath, position) same — delegates to the fully spatialized
vector overload (was previously silently falling back to 2D)
- 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
- Clear introActive/idleOrbit in externalFollow block so mouse panning works during taxi
- Skip full world reload on same-map teleports (taxi landing) by tracking loadedMapId
- Collect all model IDs for a path when resolving gryphon display ID (fixes displayId=0)
- Remove incorrect MountDisplayId fields from Vanilla/Turtle TaxiNodes DBC layouts
- Add Vanilla fly animation IDs (234/229/233) to taxi mount animation candidates
Use FlyForward/FlyIdle animations (IDs 158/159) for taxi mounts instead
of ground movement animations (Stand/Run). Add detailed mount texture
debug logging and improve fallback texture resolution for gryphon/wyvern
models with multiple skin slots.
11 original tracks in assets/Original Music/ now play on the login
screen and in thematically matched zones across Eastern Kingdoms and
Kalimdor. Added crossfadeToFile to MusicManager for local file
playback during zone transitions. New zones: Tirisfal, Undercity,
Barrens, STV, Duskwood, Burning Steppes, Searing Gorge, Ironforge,
Loch Modan, Orgrimmar, Durotar, Mulgore, Thunder Bluff, Darkshore,
Teldrassil, Darnassus.
- Per-family mount sounds (kodo, tallstrider, mechanostrider, etc.) detected from M2 model path
- Skip WMO groups with SHOW_SKYBOX flag or all-untextured batches (grey mesh in Orgrimmar)
- Freeze physics during taxi landing until terrain loads to prevent falling through void
- Disable bone animations on tropical vegetation (palm, bamboo, banana, etc.) to fix wiggling
- Snap player to final taxi waypoint on flight completion
- Extract mount aura spell ID from classic UNIT_FIELD_AURAS for CMSG_CANCEL_AURA dismount
- Increase /unstuck forward nudge to 5 units
Load third-person emote text templates (othersTarget/othersNoTarget)
from EmotesText.dbc fields 3 and 7 alongside existing sender text.
Build reverse lookup map from dbcId to EmoteInfo for incoming
SMSG_TEXT_EMOTE resolution. Other players now show proper emote
descriptions like "Player dances with Target" instead of generic
"Player performs an emote" text.
Chat tabs filter messages into General/Combat/Whispers/Trade tabs. Text
emotes now send CMSG_TEXT_EMOTE to server and display incoming emotes
from other players. Channel system auto-joins General/Trade on login with
/join, /leave, and /1-/9 shortcuts. Chat bubbles render as ImGui overlays
above entities for SAY/YELL messages with fade-out animation.
Replace hardcoded WotLK protocol constants with a data-driven architecture
supporting Classic 1.12.1, TBC 2.4.3, and WotLK 3.3.5a. Each expansion
has JSON profiles for opcodes, update fields, and DBC layouts, plus C++
polymorphic packet parsers for binary format differences (movement flags,
speed fields, transport data, spline format, char enum layout).
Key components:
- ExpansionRegistry: scans Data/expansions/*/expansion.json at startup
- OpcodeTable: logical enum <-> wire values loaded from JSON
- UpdateFieldTable: field indices loaded from JSON per expansion
- DBCLayout: schema-driven DBC field lookups replacing magic numbers
- PacketParsers: WotLK/TBC/Classic parsers with correct flag positions
- Multi-manifest AssetManager: layered manifests with priority ordering
- HDPackManager: overlay texture packs with expansion compatibility
- Auth screen expansion picker replacing hardcoded version dropdown
Fidget animations were continuing to play for a frame when movement started.
Now forces immediate switch to run animation.
Changes:
- Check for movement + active fidget at start of animation logic
- Force play run animation immediately to stop fidget
- Only check fidget completion when not moving (optimization)
- Removed duplicate mountActiveFidget_ = 0 in movement branch
Fidgets now stop instantly when player starts moving on mount.
Idle sounds were too frequent and strict criteria blocked all fidgets.
Changes:
- Idle sounds now 45-90 seconds apart (was 20-40)
- Fidget criteria back to OR (frequency OR replay) instead of AND
- Keeps all ID exclusions: 2-3, 5-9, 11-21 to prevent battle animations
- Should now discover proper fidgets while filtering problematic ones
Removed jump/land sounds (attack/wound sounds had pained growls).
Made fidget discovery much stricter to exclude jerky battle animations.
Changes:
- Disabled playJumpSound for ground mounts (attack sounds too aggressive)
- Disabled playLandSound for ground mounts (wound sounds have growls)
- Fidget criteria now requires BOTH frequency AND replay (not OR)
- Excluded IDs 11-15 (attacks) in addition to 16-21 (combat)
- Only animations with proper idle metadata will be selected
Added debug logging to show ALL non-looping, short, stationary animations
regardless of metadata, so we can identify hoof stamps and head tosses.
Shows:
- All potential fidgets (no metadata filter)
- Animation ID, duration, frequency, replay timers, flags, next animation
- Helps identify which IDs are the shuffles/stamps/tosses on Palomino
Horse was playing death animation on idle. Added explicit filtering
to exclude death (5-6), wounds (7-9), combat (16-21), and specials (2-3).
Changes:
- Check animation ID ranges before adding to fidget list
- Prevents death/wound animations from being selected as idle fidgets
- Keeps metadata-based discovery but adds safety exclusions
Created specific idle sound pool using only horse snorts and whinnies.
Re-enabled idle sounds with much longer interval (20-40 seconds).
Changes:
- Added horseIdleSounds_ pool: mHorseStand3A (snort) + mHorseAggroA (whinny)
- Updated playIdleSound() to use dedicated pool instead of mixed breath sounds
- Increased idle sound interval from 8-15s to 20-40s (less frequent)
- Removed flying mount idle sounds (too aggressive)
- Increased volume slightly (0.35x) for better audibility
Idle sounds were too frequent/upsetting, and strict criteria found no fidgets.
Changes:
- Disabled idle sounds entirely (commented out in updateCharacterAnimation)
- Relaxed fidget criteria: frequency OR replay (not both required)
- Keeps proper metadata-based discovery (frequency/replay fields)
- Comprehensive logging shows candidates and selections
Previous criteria caught combat animations with grunts instead of subtle fidgets.
Now using strict filtering and comprehensive logging to identify real fidgets.
Changes:
- Duration: 500-1200ms (very short movements only)
- Movement: <0.01 speed (nearly stationary)
- Exclude: IDs 2-3 (specials), 16-21 (combat/attack range)
- Added candidate logging: shows ALL potential fidgets for debugging
- Removed upper ID limit to catch fidgets at any position
Fidgets were stuttering because normal animation updates immediately overrode them.
Now tracks active fidget and prevents normal animation updates until fidget completes.
Changes:
- Added mountActiveFidget_ to track currently playing fidget animation
- Check fidget completion using getAnimationState before allowing normal updates
- Only trigger new fidgets when no fidget is active
- Cancel active fidget on movement
- Expanded fidget search criteria: duration up to 3000ms, ID range 1-20
- Added debug logging to show discovered fidgets and when they complete
Implements WoW-style mount idle behavior when player is stationary:
- Fidget animations: discovered via property search (non-looping, 500-1500ms, stationary, IDs 1-10)
- Triggers random fidget every 6-12 seconds when standing still
- Ambient idle sounds: snorts/breaths for ground mounts, soft wing sounds for flyers
- Triggers random idle sound every 8-15 seconds when stationary
- Both systems reset timers on movement to avoid triggering while riding
Mount Animation System:
- Property-based jump animation discovery using sequence metadata
- Chain linkage scoring (nextAnimation/aliasNext) for accurate detection
- Correct loop detection: flags & 0x01 == 0 means looping
- Avoids brake/stop animations via blendTime penalties
- Works on any mount model without hardcoded animation IDs
Mount Physics:
- Physics-based jump height: vz = sqrt(2 * g * h)
- Configurable MOUNT_JUMP_HEIGHT constant (1.0m default)
- Procedural lean into turns for ground mounts
- Smooth roll based on turn rate (±14° max, 6x/sec blend)
Audio Improvements:
- State-machine driven mount sounds (jump, land, rear-up)
- Semantic sound methods (no animation ID dependencies)
- Debug logging for missing sound files
Bug Fixes:
- Fixed mount animation sequencing (JumpStart → JumpLoop → JumpEnd)
- Fixed animation loop flag interpretation (0x20 vs 0x21)
- Rider bone attachment working correctly during all mount actions
Add SkySystem coordinator that follows WoW's actual architecture where skyboxes
are authoritative and procedural elements serve as fallbacks. Integrate lighting
system across all renderers (terrain, WMO, M2, character) with unified parameters.
Sky System:
- SkySystem coordinator manages skybox, celestial bodies, stars, clouds, lens flare
- Skybox is authoritative (baked stars from M2 models, procedural fallback only)
- skyboxHasStars flag gates procedural star rendering (prevents double-star bug)
Celestial Bodies (Lore-Accurate):
- Two moons: White Lady (30-day cycle, pale white) + Blue Child (27-day cycle, pale blue)
- Deterministic moon phases from server gameTime (not deltaTime toys)
- Sun positioning driven by LightingManager directionalDir (DBC-sourced)
- Camera-locked sky dome (translation ignored, rotation applied)
Lighting Integration:
- Apply LightingManager params to WMO, M2, character renderers
- Unified lighting: directional light, diffuse color, ambient color, fog
- Star occlusion by cloud density (70% weight) and fog density (30% weight)
Documentation:
- Add comprehensive SKY_SYSTEM.md technical guide
- Update MEMORY.md with sky system architecture and anti-patterns
- Update README.md with WoW-accurate descriptions
Critical design decisions:
- NO latitude-based star rotation (Azeroth not modeled as spherical planet)
- NO always-on procedural stars (skybox authority prevents zone identity loss)
- NO universal dual-moon setup (map-specific celestial configurations)
Wire DBC-driven lighting to terrain shaders:
- Add LightingManager to Renderer
- Initialize lighting manager with AssetManager
- Update lighting each frame with player position
- Feed lighting params to terrain shader uniforms:
* Ambient color
* Diffuse (sun) color and direction
* Fog color, start, end distances
- Fallback to skybox-based fog if lighting unavailable
TODOs for full integration:
- Wire actual map ID from game state
- Wire server game time from login packets
- Add weather/underwater state detection
- Apply lighting to WMO/M2/skybox shaders
Current state: Uses local time + Eastern Kingdoms map (0)
Next: Hook up SMSG_LOGIN_SETTIMESPEED for real game time
Replace 2D ImGui text markers with proper 3D billboard sprites using BLP textures.
Features:
- Billboard rendering using Interface\GossipFrame\ BLP textures (yellow !, yellow ?, grey ?)
- WoW-style visual effects: bob animation, distance-based scaling, glow pass, distance fade
- Proper NPC height positioning with bounding box detection
- Camera-facing quads with depth testing but no depth write
- Shader-based alpha modulation for glow and fade effects
Technical changes:
- Created QuestMarkerRenderer class with billboard sprite system
- Integrated into Renderer initialization for both online and offline terrain loading
- Rewrote updateQuestMarkers() to use billboard system instead of M2 models
- Disabled old 2D ImGui renderQuestMarkers() in game_screen.cpp
- Added debug logging for initialization and marker tracking
Quest markers now render with proper WoW visual fidelity.
Implemented three new ambient audio systems with automatic day/night transitions:
Weather ambience:
- Rain sounds (light/medium/heavy intensity based on weather system)
- Snow sounds (light/medium/heavy intensity)
- Automatically syncs with visual weather system in renderer
- Different loop intervals based on intensity (18-30s)
- Disabled indoors
Water ambience:
- Underwater swimming sounds (18s loop)
- Ocean surface sounds
- State tracking for entering/exiting water
Zone ambience:
- Forest (normal and snow variants)
- Beach sounds
- Grasslands
- Jungle
- Marsh/swamp
- Desert (canyon and plains variants)
- All zones have separate day/night sound files
- 30s loop interval for subtle background atmosphere
- Disabled indoors
Technical details:
- Added WeatherType enum (NONE, RAIN/SNOW LIGHT/MEDIUM/HEAVY)
- Added ZoneType enum (NONE, FOREST_NORMAL, FOREST_SNOW, BEACH, GRASSLANDS, JUNGLE, MARSH, DESERT_CANYON, DESERT_PLAINS)
- Loads 26 new sound files from Sound\Ambience\Weather and Sound\Ambience\ZoneAmbience
- Weather intensity thresholds: <0.33 = light, 0.33-0.66 = medium, >0.66 = heavy
- Renderer automatically converts Weather::Type + intensity to AmbientSoundManager::WeatherType
- All ambience respects volumeScale_ and indoor state
- State change logging for debugging transitions
These managers were only being initialized in loadTerrainArea (online mode).
Added initialization in loadTestTerrain as well so they work in both modes.
Now the file probing should actually run and show which sound files exist.
The cachedAssetManager was only set in loadTestTerrain() for single-player mode.
In online mode (loadTerrainArea), it was never set, so NPC voice and mount sound
managers never initialized. Now gets asset manager from Application instance if
not already cached. This will enable file probing and voice/sound loading.