Commit graph

1223 commits

Author SHA1 Message Date
Kelsi
0562139868 Implement SMSG_LOOT_ITEM_NOTIFY: show party loot notifications in chat
Parse loot item notify packets to display messages like "Thrall loots
[Item Name]." in system chat when a group member loots an item.
2026-03-09 20:31:57 -07:00
Kelsi
cc61732106 Implement SMSG_PARTYKILLLOG: show kill credit messages in chat
- Parse uint64 killerGuid + uint64 victimGuid
- Resolve names from playerNameCache (players) and entity manager (NPCs)
- Show "[Killer] killed [Victim]." as system chat when both names are known
2026-03-09 20:27:02 -07:00
Kelsi
5024e8cb32 Implement SMSG_SET_PROFICIENCY: track weapon/armor proficiency bitmasks
- Parse uint8 itemClass + uint32 subClassMask from SMSG_SET_PROFICIENCY
- Store weaponProficiency_ (itemClass=2) and armorProficiency_ (itemClass=4)
- Expose getWeaponProficiency(), getArmorProficiency(), canUseWeaponSubclass(n),
  canUseArmorSubclass(n) on GameHandler for use by equipment UI
- Enables future equipment slot validation (grey out non-proficient items)
2026-03-09 20:23:38 -07:00
Kelsi
926bcbb50e Implement SMSG_SPELLDISPELLOG dispel feedback and show dispel/steal notifications
- SMSG_SPELLDISPELLOG: parse packed caster/victim + dispel spell + isStolen +
  dispelled spell list; show system message when player dispels or has a buff
  dispelled/stolen (e.g. "Shadow Word: Pain was dispelled." / "You dispelled Renew.")
- SMSG_SPELLSTEALLOG: separated from SPELLDISPELLOG consume group with comment
  explaining the relationship (same wire format, player-facing covered by SPELLDISPELLOG)
2026-03-09 20:20:24 -07:00
Kelsi
151303a20a Implement SMSG_SPELLDAMAGESHIELD, SMSG_SPELLORDAMAGE_IMMUNE; route MSG_MOVE in SMSG_MULTIPLE_MOVES
- SMSG_SPELLDAMAGESHIELD: parse victim/caster/damage fields and show SPELL_DAMAGE
  combat text for player-relevant events (damage shields like Thorns)
- SMSG_SPELLORDAMAGE_IMMUNE: parse packed caster/victim guids and show new
  IMMUNE combat text type when player is involved in an immunity event
- Add CombatTextEntry::IMMUNE type to spell_defines.hpp and render it as
  white "Immune!" in the combat text overlay
- handleCompressedMoves: add MSG_MOVE_* routing so SMSG_MULTIPLE_MOVES
  sub-packets (player movement batches) are dispatched to handleOtherPlayerMovement
  instead of logged as unhandled; fix runtime-opcode lookup (non-static array)
2026-03-09 20:15:34 -07:00
Kelsi
1c1cdf0f23 Fix Windows socket WSAENOTCONN disconnect; add boss encounter frames
Socket fixes (fixes Windows-only connection failure):
- WorldSocket::connect() now waits for non-blocking connect to complete with
  select() before returning, preventing WSAENOTCONN on the first recv() call
  on Windows (Linux handles this implicitly but Windows requires writability
  poll after non-blocking connect)
- Add net::isConnectionClosed() helper: treats WSAENOTCONN/WSAECONNRESET/
  WSAESHUTDOWN/WSAECONNABORTED as graceful peer-close rather than recv errors
- Apply isConnectionClosed() in both WorldSocket and TCPSocket recv loops

UI:
- Add renderBossFrames(): displays boss unit health bars in top-right corner
  when SMSG_UPDATE_INSTANCE_ENCOUNTER_UNIT has active slots; supports
  click-to-target and color-coded health bars (red→orange→yellow as HP drops)
2026-03-09 20:05:09 -07:00
Kelsi
b6dfa8b747 Implement SMSG_RESUME_CAST_BAR, SMSG_THREAT_UPDATE, SMSG_UPDATE_INSTANCE_ENCOUNTER_UNIT
- SMSG_RESUME_CAST_BAR: parse packed_guid caster/target + spellId + remainingMs +
  totalMs; restores cast bar state when server re-syncs a cast in progress
- SMSG_THREAT_UPDATE: properly consume packed_guid host/target + threat entries
  to suppress unhandled packet warnings
- SMSG_UPDATE_INSTANCE_ENCOUNTER_UNIT: track up to 5 boss encounter unit guids
  per slot; expose via getEncounterUnitGuid(slot); clear on world transfer
  These guids identify active boss units for raid/boss frame display.
2026-03-09 19:54:32 -07:00
Kelsi
9f340ef456 Fix SMSG_SPELL_DELAYED/EQUIPMENT_SET_SAVED incorrectly sharing PERIODICAURALOG handler
These two opcodes were accidentally falling through to the PERIODICAURALOG
handler which expects packed_guid+packed_guid+uint32+uint32 — wrong format.
Now:
- SMSG_SPELL_DELAYED: parse caster guid + delayMs, extend castTimeRemaining
  on player cast pushback (spell cast bar stays accurate under pushback)
- SMSG_EQUIPMENT_SET_SAVED: simple acknowledge log (no payload needed)
2026-03-09 19:46:52 -07:00
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