Commit graph

1215 commits

Author SHA1 Message Date
Kelsi
1d33ebbfe4 Wire SMSG_MULTIPLE_MOVES to handleCompressedMoves and parse SMSG_PROCRESIST
- SMSG_MULTIPLE_MOVES uses the same uint8-size+uint16-opcode format as
  SMSG_COMPRESSED_MOVES; route it to handleCompressedMoves() so bundled
  monster movement updates are processed instead of dropped
- SMSG_PROCRESIST: parse caster/victim GUIDs and show MISS combat text
  when the player's proc was resisted by an enemy spell
2026-03-09 19:42:27 -07:00
Kelsi
e56d3ca7de Add spell impact sounds for player-targeted spells and improve achievement messages
- Play SpellSoundManager::playImpact() with correct school when the player
  is hit by another unit's spell (SMSG_SPELL_GO hitTargets check)
- Show achievement name in SMSG_SERVER_FIRST_ACHIEVEMENT notifications
  using the already-loaded achievementNameCache_
- playImpact was fully implemented but never called; now wired up
2026-03-09 19:36:58 -07:00
Kelsi
63c8dfa304 Show achievement names from Achievement.dbc in chat notifications
Previously "Achievement earned! (ID 1234)" was the only message. Now
loadAchievementNameCache() lazily loads Achievement.dbc (field 4 = Title,
verified against WotLK 3.3.5a binary) on first earned event and shows
"Achievement earned: Level 10" or "Player has earned the achievement: ..."
Falls back to ID if DBC is unavailable or entry is missing.
2026-03-09 19:34:33 -07:00
Kelsi
e12e399c0a Implement SMSG_TAXINODE_STATUS parsing and NPC route status cache
Parse the flight-master POI status packet (guid + uint8 status) and cache
it per-NPC in taxiNpcHasRoutes_. Exposes taxiNpcHasRoutes(guid) accessor
for future nameplate/interaction indicators. Previously this packet was
silently consumed without any state tracking.
2026-03-09 19:30:18 -07:00
Kelsi
d4ea416dd6 Fix spell cast audio to use correct magic school from Spell.dbc
Previously all player spell casts played ARCANE school sounds regardless
of the actual spell school. Now loadSpellNameCache() reads SchoolMask
(bitmask, TBC/WotLK) or SchoolEnum (Vanilla/Classic) from Spell.dbc and
stores it in SpellNameEntry. handleSpellStart/handleSpellGo look up the
spell's school and select the correct MagicSchool for cast sounds.

DBC field indices: WotLK SchoolMask=225 (verified), TBC=215, Classic/Turtle
SchoolEnum=1 (Vanilla enum 0-6 converted to bitmask).
2026-03-09 19:24:09 -07:00
Kelsi
3eded6772d Implement bird/cricket ambient sounds and remove stale renderer TODO
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
- Add birdSounds_ and cricketSounds_ AmbientSample vectors to
  AmbientSoundManager, loaded from WoW MPQ paths:
  BirdAmbience/BirdChirp01-06.wav (up to 6 variants, daytime) and
  Insect/InsectMorning.wav + InsectNight.wav (nighttime). Missing files
  are silently skipped so the game runs without an MPQ too.
- updatePeriodicSounds() now plays a randomly chosen loaded variant
  at the scheduled interval instead of the previous no-op placeholder.
- Remove stale "TODO Phase 6: Vulkan underwater overlay" comment from
  Renderer::initialize(); the feature has been fully implemented in
  renderOverlay() / the swim effects pipeline since that comment was
  written.
2026-03-09 19:00:42 -07:00
Kelsi
6cba3f5c95 Implement SMSG_MULTIPLE_PACKETS unpacking and fix unused variable warning
- Parse bundled sub-packets from SMSG_MULTIPLE_PACKETS using the WotLK
  standard wire format (uint16_be size + uint16_le opcode + payload),
  dispatching each through handlePacket() instead of silently discarding.
  Rate-limited warning for malformed sub-packet overruns.
- Remove unused cullRadiusSq variable in TerrainRenderer::renderShadow()
  that produced a -Wunused-variable warning.
2026-03-09 18:52:34 -07:00
Kelsi
18e6c2e767 Fix game object sign orientation and restrict nameplates to target only
Game object M2 models share the same default facing (+renderX) as
character models, so apply the same π/2 offset instead of π when
computing renderYawM2go from canonical yaw. This corrects street signs
and hanging shop signs that were 90° off after the server-yaw formula
fix.

Nameplates (health bar + name label) are now only rendered for the
currently targeted entity, matching WoW's default UI behaviour and
reducing visual noise.
2026-03-09 18:45:28 -07:00
Kelsi
6a681bcf67 Implement terrain shadow casting in shadow depth pass
Add initializeShadow() to TerrainRenderer that creates a depth-only
shadow pipeline reusing the existing shadow.vert/frag shaders (same
path as WMO/M2/character renderers). renderShadow() draws all terrain
chunks with sphere culling against the shadow coverage radius. Wire
both init and draw calls into Renderer so terrain now casts shadows
alongside buildings and NPCs.
2026-03-09 18:34:27 -07:00
Kelsi Rae Davis
2afd455d52
Replace (std::min + std::max) with std::clamp
Replace (std::min + std::max) with std::clamp
2026-03-09 18:34:14 -07:00
Kelsi
c887a460ea Implement Death Knight rune tracking and rune bar UI
Parse SMSG_RESYNC_RUNES, SMSG_ADD_RUNE_POWER, and SMSG_CONVERT_RUNE to
track the state of all 6 DK runes (Blood/Unholy/Frost/Death type,
ready flag, and cooldown fraction). Render a six-square rune bar below
the Runic Power bar when the player is class 6, with per-type colors
(Blood=red, Unholy=green, Frost=blue, Death=purple) and client-side
fill animation so runes visibly refill over the 10s cooldown.
2026-03-09 18:28:03 -07:00
vperus
163dc9618a Replace (std::min + std::max) with std::clamp 2026-03-10 03:18:18 +02:00
Kelsi
819a38a7ca Fix power bar visibility: include Runic Power (type 6) in fixed-max fallback
Death Knights with runic power (type 6) had no power bar visible until the
server explicitly sent UNIT_FIELD_MAXPOWER1, because the type-6 max was not
included in the 'assume 100' fallback. Runic Power has a fixed cap of 100,
same as Rage (1), Focus (2), and Energy (3).
2026-03-09 18:18:07 -07:00
Kelsi
caea24f6ea Fix animated M2 flicker: free bone descriptor sets on instance removal
The boneDescPool_ had MAX_BONE_SETS=2048 but sets were never freed when
instances were removed (only when clear() reset the whole pool on map load).
As tiles streamed in/out, each new animated instance consumed 2 pool slots
(one per frame index) permanently. After ~1024 animated instances created
total, vkAllocateDescriptorSets began failing silently and returning
VK_NULL_HANDLE. render() skips instances with null boneSet[frameIndex],
making them invisible — appearing as per-frame flicker as the culling pass
included them but the render pass excluded them.

Fix: destroyInstanceBones() now calls vkFreeDescriptorSets() for each
non-null boneSet before destroying the bone SSBO. The pool already had
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT set for this purpose.
Also increased MAX_BONE_SETS from 2048 to 8192 for extra headroom.
2026-03-09 18:09:33 -07:00
Kelsi
7b3b33e664 Fix NPC orientation (server yaw convention) and nameplate Y projection
coordinates.hpp: serverToCanonicalYaw now computes s - π/2 instead of π/2 - s.
The codebase uses atan2(-dy, dx) as its canonical yaw convention, where server
direction (cos s, sin s) in (server_X, server_Y) becomes (sin s, cos s) in
canonical after the X/Y swap, giving atan2(-cos s, sin s) = s - π/2.
canonicalToServerYaw is updated as its proper inverse: c + π/2.
The old formula (π/2 - s) was self-inverse and gave the wrong east/west facing
for any NPC not pointing north or south.

game_screen.cpp: Nameplate NDC→screen Y no longer double-inverts. The camera
bakes the Vulkan Y-flip into the projection matrix (NDC y=-1 = screen top,
y=+1 = screen bottom), so sy = (ndc.y*0.5 + 0.5) * screenH is correct.
The previous formula subtracted from 1.0 which reflected nameplates vertically.
2026-03-09 17:59:55 -07:00
Kelsi
a335605682 Fix pet frame position to avoid overlap with party frames
When in a group, push the pet frame below the party frame stack
(120px + members × 52px). When solo, keep it at y=125 (just below
the player frame at ~110px).
2026-03-09 17:25:46 -07:00
Kelsi
8b495a1ce9 Add pet frame UI below player frame
Shows active pet name, level, health bar, and power bar (mana/focus/rage/energy)
when the player has an active pet. Clicking the pet name targets it. A Dismiss
button sends CMSG_PET_ACTION to dismiss the pet. Frame uses green border to
visually distinguish it from the player/target frames.
2026-03-09 17:23:28 -07:00
Kelsi
f43277dc28 Fix SMSG_ENVIRONMENTAL_DAMAGE_LOG to use uint64 GUID (not packed)
WotLK 3.3.5a sends a raw uint64 victim GUID in this packet, not a
packed GUID. Update the handler format to match (uint64 + uint8 type
+ uint32 damage + uint32 absorb). Remove the now-dead SMSG_ENVIRONMENTALDAMAGELOG
handler since the opcode alias always routes to SMSG_ENVIRONMENTAL_DAMAGE_LOG.
2026-03-09 17:22:07 -07:00
Kelsi
70dcb6ef43 Parse SMSG_ENVIRONMENTAL_DAMAGE_LOG and color nameplate names by hostility
- Implement SMSG_ENVIRONMENTAL_DAMAGE_LOG: show fall/lava/fire/drowning
  damage as ENVIRONMENTAL combat text (orange -N) for the local player
- Color nameplate unit names: hostile units red, non-hostile yellow
  (matches WoW's standard red=enemy / yellow=neutral convention)
2026-03-09 17:18:18 -07:00
Kelsi
18a3a0fd01 Add XP_GAIN combat text type; show '+N XP' in purple on kills
XP gain was previously shown as a HEAL entry (green +N) which conflates
it with actual healing.  New XP_GAIN type renders as purple '+N XP' in the
outgoing column, matching WoW's floating XP style.
2026-03-09 17:13:31 -07:00
Kelsi
6e03866b56 Handle BLOCK (victimState 4) in melee hit combat text
Block rolls previously fell through to the damage case and were shown as
a 0-damage hit.  Now correctly emitted as a BLOCK combat text entry, which
renderCombatText already handles with 'Block' / 'You Block' label.
2026-03-09 17:10:57 -07:00
Kelsi
068deabb0e Add missing power bar colours for all WoW power types
Focus (2, orange), Happiness (4, green), Runic Power (6, crimson), and
Soul Shards (7, purple) were falling through to the default blue colour.
Applied consistently to the player frame, target frame, and party frames.
2026-03-09 17:09:48 -07:00
Kelsi
ea1af87266 Show aura charge/stack count on buff bar and target frame icons
When an aura has more than 1 charge, render the count in gold in the
upper-left corner of the icon (with drop shadow) — same position as WoW's
stack counter.  Applied to both the player buff bar and target frame auras.
2026-03-09 17:08:14 -07:00
Kelsi
6d1f3c4caf Add zone discovery text: 'Entering: <ZoneName>' fades in on zone change
Polls the renderer's currentZoneName each frame and triggers a 5-second
fade-in/hold/fade-out toast at the upper-centre of screen when the zone
changes.  Matches WoW's standard zone transition display.
2026-03-09 17:06:12 -07:00
Kelsi
c14bb791a0 Show level in nameplate labels; use '??' for skull-level targets
Prefix each nameplate name with the unit's level number.  When the unit
is more than 10 levels above the player (skull-equivalent) display '??'
instead of the raw level, matching WoW's UI convention.
2026-03-09 17:04:14 -07:00
Kelsi
9d26f8c29e Add V key toggle for nameplates (WoW default binding)
nameplates default to visible; pressing V in the game world toggles them
off/on while the keyboard is not captured by a UI element.
2026-03-09 17:03:06 -07:00
Kelsi
01e0c2f9a3 Add world-space unit nameplates projected to screen via camera VP matrix
For each visible Unit entity within 40 yards, projects the canonical WoW
position (converted to render space) through the camera view-projection
matrix to screen pixels.  Draws a health bar (hostile=red, friendly=green,
target=gold border) and name label with drop shadow using ImGui's background
draw list.  Fades out smoothly in the last 5 yards of range.
2026-03-09 17:01:38 -07:00
Kelsi
f1d31643fc Implement SMSG_SPELLENERGIZELOG and fix missing combat text cases
Parse SPELLENERGIZELOG (victim/caster packed GUIDs + spellId + powerType +
amount) and emit ENERGIZE combat text for mana/energy gains.  Add ENERGIZE
to CombatTextEntry::Type enum (blue +N text).

Also add explicit renderCombatText cases for BLOCK, PERIODIC_DAMAGE,
PERIODIC_HEAL, and ENVIRONMENTAL — previously all fell through to the
colourless default handler.
2026-03-09 16:55:23 -07:00
Kelsi
22bc5954d7 Fix opcode handler grouping: separate SET_PROFICIENCY/ENERGIZE from ACTION_BUTTONS
SMSG_SPELLENERGIZELOG, SMSG_ENVIRONMENTAL_DAMAGE_LOG, and
SMSG_SET_PROFICIENCY were incorrectly grouped with the
SMSG_ACTION_BUTTONS case block introduced in the previous commit,
causing their payloads to be misinterpreted as action button data
which could corrupt the action bar. Each now safely consumes
its packet.
2026-03-09 16:51:54 -07:00
Kelsi
52507b1f74 Add target-of-target (ToT) mini frame below target frame
Shows the name and health bar of whoever your current target is
targeting. Reads UNIT_FIELD_TARGET_LO/HI update fields which are
populated from SMSG_UPDATE_OBJECT. Frame is positioned below and
right-aligned with the main target frame.
2026-03-09 16:49:50 -07:00
Kelsi
941b2c4894 Load server action bar from SMSG_ACTION_BUTTONS on login
Previously the 144-button server payload was silently dropped.
Now parses the first 12 buttons (one bar) and populates the local
action bar with server-side spells and items. Macros and unknown
button types are skipped. Empty/zero slots are preserved as-is to
avoid wiping hardcoded Attack/Hearthstone defaults.
2026-03-09 16:45:53 -07:00
Kelsi
4db686a652 Parse SMSG_PERIODICAURALOG to show DoT/HoT numbers in combat text
Previously all periodic aura ticks were silently discarded.
Now parses victim/caster GUIDs, auraType, and damage/heal value
for the two most common types (PERIODIC_DAMAGE=3 and PERIODIC_HEAL=8)
and generates PERIODIC_DAMAGE/PERIODIC_HEAL combat text entries.
Falls back safely to consume-all on unknown aura types.
2026-03-09 16:43:33 -07:00
Kelsi
c57182627f Respond to SMSG_REALM_SPLIT with CMSG_REALM_SPLIT ack
Previously the packet was silently consumed. Some servers send
SMSG_REALM_SPLIT during login and expect a CMSG_REALM_SPLIT
acknowledgement, otherwise they may time out the session.
Responds with splitType echoed back and patchVersion "3.3.5".
2026-03-09 16:39:52 -07:00
Kelsi
f0d1702d5f Add duration countdown overlay to target frame aura icons
Matches the same fix applied to the player buff bar: icons in the
target frame now show their remaining duration at the icon bottom edge
with a drop shadow, shared between the always-visible overlay and the
hover tooltip.
2026-03-09 16:37:55 -07:00
Kelsi
088a11e62a Add duration countdown overlay to buff/debuff icons in buff bar
Icons now show remaining time (e.g. "1:30", "45") rendered directly
on the icon bottom edge with a drop shadow, matching WoW's standard
buff display. Tooltip still shows full name + seconds on hover.
Deduplicates the nowMs/remainMs computation that was previously
recomputed in the tooltip-only path.
2026-03-09 16:36:58 -07:00
Kelsi
13e3e5ea35 Implement MusicManager fade-out in stopMusic() — was a stub
stopMusic(fadeMs) previously had (void)fadeMs with no fade logic.
Added fadingOut/fadeOutTimer/fadeOutDuration/fadeOutStartVolume state
and wired update() to interpolate volume to zero then stop playback.
Also clean up DuelProposedPacket comment (removed misleading TODO label).
2026-03-09 16:30:42 -07:00
Kelsi
f2eabc87ef Add notification for SMSG_BINDER_CONFIRM (innkeeper bind set)
SMSG_BINDER_CONFIRM confirms the bind point was set. Previously silently
consumed; now shows "This innkeeper is now your home location." in system
chat so the player gets feedback after using an innkeeper.
2026-03-09 16:26:31 -07:00
Kelsi
68bf3d32b0 Wire ambient sound zone detection: setZoneType/setCityType was never called
Add AmbientSoundManager::setZoneId() that maps WoW zone IDs to the
appropriate ZoneType (forest/grasslands/desert/jungle/marsh/beach) or
CityType (Stormwind/Ironforge/Darnassus/Orgrimmar/Undercity/ThunderBluff)
and delegates to setZoneType/setCityType. Call it from the renderer's
zone transition handler so zone ambience (looping sounds, city bells,
etc.) actually activates when the player enters a zone.
2026-03-09 16:24:12 -07:00
Kelsi
4ac32a1206 Parse SMSG_GAMETIME_SET/UPDATE/GAMESPEED_SET for sky clock accuracy
Server sends periodic game time corrections via SMSG_GAMETIME_SET and
SMSG_GAMETIME_UPDATE (uint32 gameTimePacked). SMSG_GAMESPEED_SET also
sends an updated timeSpeed float. Applying these keeps gameTime_/timeSpeed_
in sync with the server, preventing day/night drift in the sky renderer
over long play sessions.
2026-03-09 16:21:06 -07:00
Kelsi
6583ce9c57 Use server zone ID (SMSG_INIT_WORLD_STATES) for zone music selection
In online mode, SMSG_INIT_WORLD_STATES delivers the server-authoritative
zone ID when entering a new area. Prefer this over the tile-based fallback
so music transitions are accurate for small zones (city districts, caves,
dungeon entrances) that don't align with 533-unit tile boundaries.
2026-03-09 16:19:38 -07:00
Kelsi
a654dd5e99 Ensure zone music DBC enrichment runs at world load time
Call enrichFromDBC() again when loadOnlineWorld() sets cachedAssetManager,
so enrichment is guaranteed even when the asset manager was null at renderer
construction. enrichFromDBC() is idempotent (skips duplicate paths).
2026-03-09 16:18:08 -07:00
Kelsi
97192ab2a4 Upgrade SMSG_PLAY_OBJECT_SOUND/SPELL_IMPACT to 3D positional audio
Add PlayPositionalSoundCallback that carries both soundId and sourceGuid.
In Application, look up the source entity position and play via
AudioEngine::playSound3D(); fall back to playSound2D() when the entity
is unknown. Also read the 8-byte sourceGuid field from the packet
(previously the full 12-byte payload was ignored).
2026-03-09 16:16:39 -07:00
Kelsi
0913146f54 Play SMSG_PLAY_OBJECT_SOUND and SMSG_PLAY_SPELL_IMPACT audio via DBC lookup
Both opcodes send uint32 soundId as first field. Extend PlaySoundCallback to
cover them so environmental object sounds and spell impact sounds are audible
in-game (resolved through SoundEntries.dbc → AudioEngine::playSound2D).
2026-03-09 16:12:52 -07:00
Kelsi
a2c2675039 Wire SMSG_PLAY_SOUND to AudioEngine via SoundEntries.dbc lookup
Add PlaySoundCallback to GameHandler (same pattern as PlayMusicCallback).
When SMSG_PLAY_SOUND arrives, resolve the soundId through SoundEntries.dbc
(fields 3-12 = files, field 23 = DirectoryBase) and play the first found
file as a 2-D sound effect via AudioEngine::playSound2D(). Previously the
opcode was parsed and dropped.
2026-03-09 16:11:19 -07:00
Kelsi
55082a0925 Remove unused baseZ/hasHeights variables in WaterRenderer::loadFromWMO
These were declared to handle per-vertex WMO liquid height variation but
never actually used below — the surface is built with a flat adjustedZ
height throughout. Remove to eliminate -Wunused-variable warnings.
2026-03-09 16:09:44 -07:00
Kelsi
b23cf06f1c Remove dead legacy GL Texture class
texture.hpp / texture.cpp implemented an unfinished OpenGL texture loader
(loadFromFile was a TODO stub) that had no callers — the project's texture
loading is entirely handled by VkTexture (vk_texture.hpp/cpp) after the
Vulkan migration. Remove both files and their CMakeLists entries.
2026-03-09 16:07:08 -07:00
Kelsi
43b9ecd857 Enrich zone music from AreaTable/ZoneMusic/SoundEntries DBC chain
Add ZoneManager::enrichFromDBC() which walks AreaTable.dbc (field 8 = ZoneMusicId)
→ ZoneMusic.dbc (fields 6/7 = day/night SoundEntryIds) → SoundEntries.dbc
(fields 3-12 = files, field 23 = DirectoryBase) and appends MPQ music paths for
all zones in the DBC, covering ~2300+ areas vs the previous ~15 hardcoded entries.

Existing hardcoded paths are preserved as the primary pool; DBC paths are added
only if not already present. Called from Renderer::init() after initialize().
2026-03-09 16:04:52 -07:00
Kelsi
46f2c0df85 Fix SoundEntries.dbc field indices for SMSG_PLAY_MUSIC and remove dead NpcVoiceManager code
Correct SoundEntries.dbc field access in the PlayMusic callback: file names are at
fields 3-12 (not 2-11) and DirectoryBase is at field 23 (not 22). Field 2 is the
Name label string, not a file path.

Remove dead detectVoiceType(creatureEntry) from NpcVoiceManager — it was never
called; actual voice detection uses detectVoiceTypeFromDisplayId() in Application.
2026-03-09 16:01:29 -07:00
Kelsi
9c3faa0e16 Clarify World stub methods: terrain/entity state lives in subsystems
Remove TODO comments from World::update() and World::loadMap() and
replace with explanatory comments. World is an intentional thin token;
the actual work happens in Application (TerrainManager, camera) and
GameHandler (packet processing). This reflects the current architecture
rather than implying missing work.
2026-03-09 15:54:43 -07:00
Kelsi
e8d068c5cb Add Instance Lockouts window and fix three compiler warnings
- Add Escape Menu → Instance Lockouts button opening a new panel
  that lists active lockouts with instance name (from Map.dbc),
  difficulty, time-until-reset countdown, and locked/extended status.
  map name lookup is cached on first open.
- Fix uninitialized ChatType in sendChatMessage (default to SAY)
- Remove unused startWorld variable in handleMonsterMoveTransport
- Remove unused modelCached variable in spawnOnlineCreature
  Eliminates all -Wunused-but-set-variable and -Wmaybe-uninitialized
  warnings in the main translation units.
2026-03-09 15:52:58 -07:00