Commit graph

328 commits

Author SHA1 Message Date
Kelsi
d0df6eed2c feat: show corpse skull marker on world map when player is a ghost
When the player dies and releases spirit, the world map now renders a
bone-white X cross at the corpse's location (matching the existing
minimap skull marker). The marker appears only when the player is a
ghost with an unclaimed corpse on the same map, and shows a "Your
corpse" tooltip on hover. Implemented via setCorpsePos() on WorldMap,
called from renderWorldMap() using getCorpseCanonicalPos().
2026-03-17 19:52:17 -07:00
Kelsi
d60d296b77 feat: show discovered taxi nodes as markers on the world map
Add gold diamond markers for every flight master the player has already
discovered (knownTaxiMask_), read from TaxiNodes.dbc and filtered to the
current continent/map being displayed:
- WorldMapTaxiNode struct carries canonical WoW coords + known flag
- WorldMap::setTaxiNodes() accepts the per-frame list from game_screen
- renderImGuiOverlay() projects each known node to UV, draws a gold
  diamond (AddQuadFilled) with a dark outline, and shows the node name
  as a tooltip on hover
- GameHandler::isKnownTaxiNode(id) checks knownTaxiMask_[] efficiently
- Markers update live — newly discovered nodes appear without reopening
  the map
2026-03-17 19:01:03 -07:00
Kelsi
36fed15d43 feat: separate cast/impact kit paths in spell visual DBC lookup
loadSpellVisualDbc() now builds two distinct maps:
  spellVisualCastPath_  — visualId → M2 via SpellVisual.CastKit chain
  spellVisualImpactPath_ — visualId → M2 via SpellVisual.ImpactKit chain

playSpellVisual() accepts useImpactKit=false (default, cast) / true (impact).
SMSG_PLAY_SPELL_IMPACT passes useImpactKit=true so impact effects (explosions,
debuff indicators) use the ImpactKit model instead of the CastKit model.
Added ImpactKit field to all four dbc_layouts.json files.
2026-03-17 18:30:11 -07:00
Kelsi
315adfbe93 feat: implement SMSG_PLAY_SPELL_VISUAL with SpellVisual DBC chain lookup
Parse SMSG_PLAY_SPELL_VISUAL (casterGuid + visualId) and spawn a
transient M2 spell effect at the caster's world position.

DBC chain: SpellVisual.dbc → SpellVisualKit.dbc → SpellVisualEffectName.dbc
Lookup priority: CastKit.SpecialEffect0, fallback to MissileModel.
Models are lazy-loaded and cached by path; instances auto-expire after 3.5s.
DBC layouts added to all four expansion layout files (Classic/TBC/WotLK/Turtle).
2026-03-17 18:23:05 -07:00
Kelsi
8b9d626aec feat: show directional arrow on world map player marker
Replace the static filled circle with a red triangle arrow that
rotates to match the character's current facing direction.
Uses the same render-space yaw convention as the 3D scene so
the arrow matches in-world orientation.
2026-03-17 14:10:56 -07:00
Kelsi
a43a43ed8e fix: evict oldest minimap tile textures when cache exceeds 128 entries
Prevents unbounded GPU memory growth in long play sessions where the player
visits many zones. Tiles are inserted into a FIFO deque; when the count of
successfully-loaded tiles exceeds MAX_TILE_CACHE (128), the oldest entry is
destroyed and removed from both the cache map and the deque.

At 256×256×4 bytes per tile this caps minimap GPU usage at ~32 MB.
2026-03-17 13:38:18 -07:00
Kelsi
192c6175b8 feat: add brightness slider to Video settings
Black overlay dims below 50%, white overlay brightens above 50%.
Persisted in settings.cfg, with restore-defaults support.
2026-03-17 09:04:53 -07:00
Kelsi
e3c2269b16 fix: increase descriptor pool sizes to prevent Vulkan crash
Terrain pool 16384→65536, WMO pool 8192→32768. The previous sizes
were too small for the load/unload radii, causing pool exhaustion
and a hard crash when streaming terrain on large maps.
2026-03-16 17:46:32 -07:00
Kelsi
b0fafe5efa fix: stabilize turtle world entry session handling 2026-03-15 01:21:23 -07:00
Kelsi
075b4c1772 fix(gameplay): tighten TB elevator bounds and reset stale combat visuals
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
2026-03-14 09:19:16 -07:00
Kelsi
cebca9a882 fix(gameplay): stabilize run animation and clean ghost/death visuals 2026-03-14 08:27:32 -07:00
Kelsi
6cfb439fd6 fix(vulkan): defer resource frees until frame fence 2026-03-14 03:32:31 -07:00
Kelsi
727dfa5c6c feat: integrate lightning system for storm weather and heavy rain
The lightning system (lightning.hpp/cpp) was fully implemented but never
wired into the renderer. Connect it now:
- Enable lightning during server storm weather (wType==3, intensity>0.1)
  and heavy rain (wType==1, intensity>0.7) as a bonus visual
- Scale lightning intensity proportionally to weather intensity
- Render in both parallel (SEC_POST) and fallback rendering paths
- Update and shutdown alongside the weather system
- Show active lightning info in the performance HUD weather section
2026-03-13 09:52:23 -07:00
Kelsi
85767187b1 fix: clear gameObjectDisplayIdWmoCache_ on world transition, add stale-entry guard
gameObjectDisplayIdWmoCache_ was not cleared on world unload/transition,
causing stale WMO model IDs (e.g. 40006, 40003) to be looked up after
the renderer cleared its model list, resulting in "Cannot create instance
of unloaded WMO model" errors on zone re-entry.

Changes:
- Clear gameObjectDisplayIdWmoCache_ alongside other GO caches on world reset
- Add WMORenderer::isModelLoaded() for cache-hit validation
- Inline GO WMO path now verifies cached model is still renderer-resident
  before using it; evicts stale entries and falls back to reload
2026-03-13 03:43:55 -07:00
Kelsi
1108aa9ae6 feat: implement M2 ribbon emitter rendering for spell trail effects
Parse M2RibbonEmitter data (WotLK format) from M2 files — bone index,
position, color/alpha/height tracks, edgesPerSecond, edgeLifetime,
gravity. Add CPU-side trail simulation per instance (edge birth at bone
world position, lifetime expiry, gravity droop). New m2_ribbon.vert/frag
shaders render a triangle-strip quad per emitter using the existing
particleTexLayout_ descriptor set. Supports both alpha-blend and additive
pipeline variants based on material blend mode. Fixes invisible spell
trail effects (~5-10%% of spell visuals) that were silently skipped.
2026-03-13 01:17:30 -07:00
Kelsi
acf99354b3 feat: add ghost mode grayscale screen effect
- FXAA path: repurpose _pad field as 'desaturate' push constant; when
  ghostMode_ is true, convert final pixel to grayscale with slight cool
  blue tint using luma(0.299,0.587,0.114) mix
- Non-FXAA path: apply a high-opacity gray overlay (rgba 0.5,0.5,0.55,0.82)
  over the scene for a washed-out look
- Both parallel (SEC_POST) and single-threaded render paths covered
- ghostMode_ flag set each frame from gameHandler->isPlayerGhost()
2026-03-13 00:59:36 -07:00
Kelsi
ebaf95cc42 fix: remove Y-flip counter-hacks in FSR shaders; invert mouse by default; FSR1 disables MSAA
FSR EASU and FSR2 sharpen fragment shaders had a manual Y-flip to undo
the now-removed postprocess.vert flip.  Strip those since the vertex
shader no longer flips, making all postprocess paths consistent.

Also flip the default mouse Y-axis to match user expectation (mouse
down = look up / flight-sim style) and make FSR1 disable MSAA on
enable, matching FSR2 behaviour (FSR provides its own spatial AA).
2026-03-12 21:59:41 -07:00
Kelsi
5684b16721 fix: talent screen hang (uint8_t overflow) and camera pitch limit
- Change maxRow/maxCol from uint8_t to int in renderTalentTree to prevent
  infinite loop: uint8_t col <= 255 never exits since col wraps 255→0.
  Add sanity cap of 15 rows/cols to guard against corrupt DBC data.
- Fix dangling reference warning in getFormattedTitle (lambda reference)
- Raise MAX_PITCH from 35° to 88° to match WoW standard upward look range
2026-03-12 20:52:58 -07:00
Kelsi
9aa4b223dc feat: implement SMSG_CAMERA_SHAKE with sinusoidal camera shake effect
- Add triggerShake(magnitude, frequency, duration) to CameraController
- Apply envelope-decaying sinusoidal XYZ offset to camera in update()
- Handle SMSG_CAMERA_SHAKE opcode in GameHandler dispatch
- Translate shakeId to magnitude (minor <50: 0.04, larger: 0.08 world units)
- Wire CameraShakeCallback from GameHandler through to CameraController
- Shake uses 18Hz oscillation with 30% fade-out envelope at end of duration
2026-03-12 19:37:53 -07:00
Kelsi
6e95709b68 feat: add FXAA post-process anti-aliasing, combinable with MSAA 2026-03-12 16:43:48 -07:00
Kelsi
e2f36f6ac5 feat: show party member dots on world map with name labels and class colors 2026-03-12 15:05:52 -07:00
Kelsi
4be7910fdf refactor: consolidate QueryTimer struct to shared header
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Move QueryTimer from m2_renderer.cpp and wmo_renderer.cpp to
vk_frame_data.hpp for reuse. Removes 13 lines of duplicate code.
2026-03-11 11:42:01 -07:00
Kelsi
b5a2175269 refactor: consolidate duplicate ShadowParamsUBO structure definition
Move ShadowParamsUBO from 5 separate shadow rendering functions (2 in
m2_renderer, 1 in terrain_renderer, 1 in wmo_renderer) into shared
vk_frame_data.hpp header. Eliminates 5 identical local struct definitions
and improves consistency across all shadow pass implementations. Structure
layout matches shader std140 uniform buffer requirements.
2026-03-11 11:37:58 -07:00
Kelsi
b3d8651db9 refactor: consolidate duplicate environment variable utility functions
Move envSizeMBOrDefault and envSizeOrDefault from 4 separate rendering
modules (character_renderer, m2_renderer, terrain_renderer, wmo_renderer)
into shared vk_utils.hpp header as inline functions. Use the most robust
version which includes overflow checking for MB-to-bytes conversion. This
eliminates 7 identical local function definitions and improves consistency
across all rendering modules.
2026-03-11 11:36:06 -07:00
Kelsi
cda703b0f4 refactor: consolidate duplicate ShadowPush structure definition
Move ShadowPush from 4 separate rendering modules (character_renderer,
m2_renderer, terrain_renderer, wmo_renderer) into shared vk_frame_data.hpp
header. This eliminates 4 identical local struct definitions and ensures
consistency across all shadow rendering passes. Add vk_frame_data.hpp include
to character_renderer.cpp.
2026-03-11 11:32:08 -07:00
Kelsi
3202c1392d refactor: extract shared attachment lookup logic into helper function
Consolidated duplicate attachment point resolution code used by both
attachWeapon() and getAttachmentTransform(). New findAttachmentBone()
helper encapsulates the complete lookup chain: attachment by ID, fallback
scan, key-bone fallback, and validation. Eliminates ~55 lines of duplicate
code while improving maintainability and consistency.
2026-03-11 11:15:06 -07:00
Kelsi
7c77c4a81e Fix per-frame particle descriptor set leak in M2 renderer
Pre-allocate one stable VkDescriptorSet per particle emitter at model
upload time (particleTexSets[]) instead of allocating a new set from
materialDescPool_ every frame for each particle group.  The per-frame
path exhausted the 8192-set pool in ~14 s at 60 fps with 10 active
particle emitters, causing GPU device-lost crashes.  The old path is
kept as an explicit fallback but should never be reached in practice.
2026-03-11 02:01:23 -07:00
Kelsi
00a939a733 fix: world map exploration fallback when server mask is unavailable
When the server has not sent SMSG_INIT_WORLD_STATES or the mask is
empty, fall back to locally-accumulated explored zones tracked by
player position. The local set is cleared when a real server mask
arrives so it doesn't persist stale data.
2026-03-10 22:27:00 -07:00
Kelsi
6928b8ddf6 feat: desaturate quest markers for trivial (gray) quests
Trivial/low-level quests now show gray '!' / '?' markers instead of
yellow, matching the in-game distinction between available and trivial
quests. Add grayscale parameter to QuestMarkerRenderer::setMarker and
the push-constant block; application sets grayscale=1.0 for trivial
markers and 0.0 for all others.
2026-03-10 22:26:56 -07:00
Kelsi
564a286282 fix: stand-up-on-move and nameplate position tracking
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
2026-03-10 19:49:33 -07:00
Kelsi
60ebb565bb rendering: fix WMO portal culling and chat message format
- wmo_renderer: pass character position (not camera position) to portal
  visibility traversal — the 3rd-person camera can orbit outside a WMO
  while the character is inside, causing interior groups to cull; render()
  now accepts optional viewerPos that defaults to camPos for compatibility
- renderer: pass &characterPosition to wmoRenderer->render() at both
  main and single-threaded call sites; reflection pass keeps camPos
- renderer: apply mount pitch/roll to rider during all flight, not just
  taxiFlight_ (fixes zero rider tilt during player-controlled flying)
- game_screen: format SAY/YELL/WHISPER/EMOTE using WoW-style "Name says:"
  instead of "[SAY] Name:" bracket prefix
2026-03-10 14:59:02 -07:00
Kelsi
132598fc88 physics: send MSG_MOVE_START/STOP_ASCEND and START_DESCEND during flight
When flyingActive_, detect Space/X key transitions and emit proper flight
vertical movement opcodes so the server (and other players) see the
correct ascending/descending animation state:

- MSG_MOVE_START_ASCEND  (Space pressed while flying)  → sets ASCENDING flag
- MSG_MOVE_STOP_ASCEND   (Space released while flying) → clears ASCENDING flag
- MSG_MOVE_START_DESCEND (X pressed while flying)      → clears ASCENDING flag
- MSG_MOVE_STOP_ASCEND   (X released while flying)     → clears vertical state

Track wasAscending_/wasDescending_ member state to detect transitions.
Also clear lingering vertical state when leaving flight mode.
2026-03-10 14:32:30 -07:00
Kelsi
a9ddfe70c2 physics: sync server turn rate and fix SPLINE speed handlers
- Add getServerTurnRate() accessor and turnRateOverride_ field so the
  keyboard turn speed respects SMSG_FORCE_TURN_RATE_CHANGE from server
- Convert rad/s → deg/s before applying to camera yaw logic
- Fix SMSG_SPLINE_SET_RUN_BACK/SWIM/FLIGHT/FLIGHT_BACK/SWIM_BACK/WALK/
  TURN_RATE handlers: all previously discarded the value; now update the
  corresponding serverXxxSpeed_ / serverTurnRate_ field when GUID matches
  playerGuid (camera controller syncs these every frame)
2026-03-10 14:18:25 -07:00
Kelsi
e2f65dfc59 physics: add server flight-back speed override to CameraController
SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE was already ACK'd and stored in
serverFlightBackSpeed_, but the value was never accessible or synced
to the CameraController. Backward flight movement always used forward
flight speed (flightSpeedOverride_), making it faster than the server
intended.

- Add getServerFlightBackSpeed() accessor in GameHandler
- Add flightBackSpeedOverride_ field and setter in CameraController
- Apply it in the fly movement block: backward-only flight uses the
  back speed; forward or strafing uses the forward speed as WoW does
- Fallback: 50% of forward flight speed when override is unset
- Sync per-frame in application.cpp alongside the other speed overrides
2026-03-10 14:05:50 -07:00
Kelsi
a33f635490 physics: add server swim-back speed override to CameraController
Backward swimming was using 50% of forward swim speed as a hardcoded
fallback. Wire up the server-authoritative swim back speed so Warlock
Dark Pact, buffs, and server-forced speed changes all apply correctly
when swimming backward.

- game_handler.hpp: add getServerSwimBackSpeed() accessor
- camera_controller.hpp: add swimBackSpeedOverride_ field + setter
- camera_controller.cpp: apply swimBackSpeedOverride_ when player
  swims backward without forward input; fall back to 50% of swim speed
- application.cpp: sync swim back speed each frame
2026-03-10 13:51:47 -07:00
Kelsi
23293d6453 physics: implement HOVER movement flag physics in CameraController
When the server sets MovementFlags::HOVER (SMSG_MOVE_SET_HOVER), the
player now floats 4 yards above the nearest ground surface instead of
standing on it. Uses the existing floor-snap path with a HOVER_HEIGHT
offset applied to the snap target.

- game_handler.hpp: add isHovering() accessor (reads HOVER flag from
  movementInfo.flags, which is already set by handleForceMoveFlagChange)
- camera_controller.hpp: add hoverActive_ field and setHoverActive()
- camera_controller.cpp: apply HOVER_HEIGHT = 4.0f offset at floor snap
- application.cpp: sync hover state each frame alongside other movement
  states (gravity, feather fall, water walk, flying)
2026-03-10 13:39:23 -07:00
Kelsi
56ec49f837 physics: sync all server movement speeds to CameraController
Previously only run speed was synced. Now all server-driven movement
speeds are forwarded to the camera controller each frame:
- runSpeedOverride_: server run speed (existing)
- walkSpeedOverride_: server walk speed (Ctrl key movement)
- swimSpeedOverride_: swim speed (Swim Form, Engineering fins)
- flightSpeedOverride_: flight speed (epic vs normal flying mounts)
- runBackSpeedOverride_: back-pedal speed

Each uses the server value when non-zero/sane, falling back to the
hardcoded WoW default constant otherwise.
2026-03-10 13:28:53 -07:00
Kelsi
a1ee9827d8 physics: apply server flight speed to flying mount movement
serverFlightSpeed_ (from SMSG_FORCE_FLIGHT_SPEED_CHANGE) was stored but
never synced to CameraController. Add getServerFlightSpeed() accessor,
flightSpeedOverride_ field, and use it in the flying physics path so
normal vs epic flying mounts actually move at their correct speeds.
2026-03-10 13:25:10 -07:00
Kelsi
27d18b2189 physics: implement player-controlled flying mount physics
When CAN_FLY + FLYING movement flags are both set (flying mounts, Druid
Flight Form), the CameraController now uses 3D pitch-following movement
instead of ground physics:
- Forward/back follows the camera's 3D look direction (ascend when
  looking up, descend when looking down)
- Space = ascend vertically, X (while mounted) = descend
- No gravity, no grounding, no jump coyote time
- Fall-damage checks suppressed (grounded=true)

Also wire up all remaining server movement state flags to CameraController:
- Feather Fall: cap terminal velocity at -2 m/s
- Water Walk: clamp to water surface, skip swim entry
- Flying: 3D movement with no gravity

All states synced each frame from GameHandler via isPlayerFlying(),
isFeatherFalling(), isWaterWalking(), isGravityDisabled().
2026-03-10 13:23:38 -07:00
Kelsi
1853e8aa56 physics: implement Water Walk movement state tracking and surface clamping
SMSG_MOVE_WATER_WALK / SMSG_MOVE_LAND_WALK now correctly set/clear
WATER_WALK (0x00008000) in movementInfo.flags, ensuring the flag is
included in movement ACKs sent to the server.

In CameraController, when waterWalkActive_ is set and the player is
at or above the water surface (within 0.5 units), clamp them to the
water surface and mark as grounded — preventing water entry and allowing
them to walk across the water surface as the spell intends.
2026-03-10 13:18:04 -07:00
Kelsi
0b99cbafb2 physics: implement feather fall and water walk movement flag tracking
Feather Fall (SMSG_MOVE_FEATHER_FALL / SMSG_MOVE_NORMAL_FALL):
- Add FEATHER_FALL = 0x00004000 to MovementFlags enum
- Fix handlers to set/clear the flag instead of passing flag=0
- Cap downward terminal velocity at -2.0 m/s in CameraController when
  feather fall is active (Slow Fall, Parachute, etc.)

All three handlers now correctly propagate server movement state flags
that were previously acknowledged without updating any local state.
2026-03-10 13:14:52 -07:00
Kelsi
701cb94ba6 physics: apply server walk and swim speed overrides to CameraController
serverWalkSpeed_ and serverSwimSpeed_ were stored in GameHandler but
never exposed or synced to the camera controller. The controller used
hardcoded WOW_WALK_SPEED and speed*SWIM_SPEED_FACTOR regardless of
server-sent speed changes.

Add getServerWalkSpeed()/getServerSwimSpeed() accessors, walkSpeedOverride_
and swimSpeedOverride_ fields in CameraController, and sync all three
server speeds each frame. Both swim speed sites (main and camera-collision
path) now use the override when set. This makes Slow debuffs (walk speed),
Swim Form, and Engineering fins actually affect movement speed.
2026-03-10 13:11:50 -07:00
Kelsi
f2337aeaa7 physics: disable gravity when server sends SMSG_MOVE_GRAVITY_DISABLE
SMSG_MOVE_GRAVITY_DISABLE/ENABLE now correctly set/clear the LEVITATING
movement flag instead of passing flag=0. GameHandler::isGravityDisabled()
reads the LEVITATING bit and is synced to CameraController each frame.

When gravity is disabled the physics loop bleeds off downward velocity
and skips gravity accumulation, so Levitate and similar effects actually
float the player rather than letting them fall through the world.
2026-03-10 13:07:34 -07:00
Kelsi
21604461fc physics: block client-side movement when server roots the player
When SMSG_FORCE_MOVE_ROOT sets ROOT in movementInfo.flags, the
camera controller was not aware and continued to accept directional
input. This caused position desync (client moves, server sees player
as rooted).

- Add movementRooted_ flag to CameraController with setter/getter.
- Block nowForward/nowBackward/nowStrafe when movementRooted_ is set.
- Sync isPlayerRooted() from GameHandler to CameraController each
  frame alongside the existing run-speed sync in application.cpp.
- Add GameHandler::isPlayerRooted() convenience accessor.
2026-03-10 13:01:44 -07:00
Kelsi
c622fde7be physics: implement knockback simulation from SMSG_MOVE_KNOCK_BACK
Previously the handler ACKed with current position and ignored the
velocity fields entirely (vcos/vsin/hspeed/vspeed were [[maybe_unused]]).
The server expects the client to fly through the air on knockback — without
simulation the player stays in place while the server models them as airborne,
causing position desync and rubberbanding.

Changes:
- CameraController: add applyKnockBack(vcos, vsin, hspeed, vspeed)
  that sets knockbackHorizVel_ and launches verticalVelocity = -vspeed
  (server sends vspeed as negative for upward launches, matching TrinityCore)
- Physics loop: each tick adds knockbackHorizVel_ to targetPos then applies
  exponential drag (KNOCKBACK_HORIZ_DRAG=4.5/s) until velocity < 0.05 u/s
- GameHandler: parse all four fields, add KnockBackCallback, call it for
  the local player so the camera controller receives the impulse
- Application: register the callback — routes server knockback to physics

The existing ACK path is unchanged; the server gets position confirmation
as before while the client now actually simulates the trajectory.
2026-03-10 12:28:11 -07:00
Kelsi
dd3f9e5b9e wmo: enable portal culling by default after AABB transform fix
The AABB transform bug (direct min/max transform was wrong for rotated
WMOs) was fixed in a prior commit. Portal culling now uses the correct
world-space AABB computed from all 8 corners, so frustum intersection
is valid.

The AABB-based test is conservative (no portal plane-side check): a
visible portal can only be incorrectly INCLUDED, never EXCLUDED. This
means no geometry can disappear, and any overdraw is handled by the
z-buffer. Enable by default to get the performance benefit inside WMOs
and dungeons.
2026-03-10 12:11:13 -07:00
Kelsi
137b25f318 rendering: fix NPC movement animation IDs and remove redundant player anim
The renderer's CharAnimState machine already drives player character
animations (Run=5, Walk=4, Jump, Swim, etc.) — remove the conflicting
camera controller code added in the previous commit.

Fix creature movement animations to use the correct WoW M2 IDs:
4=Walk, 5=Run. Both the per-frame sync loop and the SMSG_MONSTER_MOVE
spline callback now use Run (5) for NPC movement.
2026-03-10 10:19:13 -07:00
Kelsi
4e137c4061 rendering: drive Run/Stand animations from actual movement state
CameraController now transitions the player character to Run (anim 4)
on movement start and back to Stand (anim 0) on stop, guarded by a
prevPlayerMoving_ flag so animation time is not reset every frame.
Death animation (anim 1) is never overridden.

Application creature sync similarly switches creature models to Run (4)
when they move between server positions and Stand (0) when they stop,
with per-guid creatureWasMoving_ tracking to avoid per-frame resets.
2026-03-10 10:06:56 -07:00
Kelsi
c8d9d6b792 rendering/game: make player model semi-transparent in ghost form
Add GhostStateCallback to GameHandler, fired when PLAYER_FLAGS_GHOST
transitions on or off in UPDATE_OBJECT / login detection. Add
setInstanceOpacity() to CharacterRenderer to directly set opacity
without disturbing fade-in state. Application wires the callback to
set opacity 0.5 on ghost entry and 1.0 on resurrect.
2026-03-10 09:57:24 -07:00
Kelsi
366321042f rendering/game: sync camera sit state from server-confirmed stand state
Add CameraController::setSitting() and call it from the StandStateCallback
so the camera blocks movement when the server confirms the player is
sitting or kneeling (stand states 1-6, 8). This prevents the player
from sliding across the ground after sitting.

Death (state 7) deliberately leaves sitting=false so the player can
still respawn/move after death without input being blocked.
2026-03-10 09:51:15 -07:00