Commit graph

632 commits

Author SHA1 Message Date
Kelsi
06a628dae2 game: implement SMSG_PET_SPELLS/MODE/BROKEN and pet action plumbing
SMSG_PET_SPELLS: Parse full packet — pet GUID, react/command state,
10 action bar slots, per-spell entries with autocast flags.  Previously
only read the GUID.

SMSG_PET_MODE: Parse petGuid + mode uint32 (command low byte, react
high byte) to keep stance state in sync after server updates.

SMSG_PET_BROKEN: Clear pet state and show "Your pet has died." chat
message.

SMSG_PET_LEARNED_SPELL / SMSG_PET_UNLEARNED_SPELL: Maintain pet spell
list incrementally.

SMSG_PET_CAST_FAILED: Parse and log cast count + spell + reason.

New state accessors: getPetActionSlot(), getPetCommand(), getPetReact(),
getPetSpells(), isPetSpellAutocast().

CMSG_PET_ACTION: Add targetGuid (uint64) field — the wire format
requires petGuid(8)+action(4)+targetGuid(8).  Was sending an 12-byte
packet instead of the required 20 bytes.

sendPetAction(): New method that builds and sends CMSG_PET_ACTION with
the correct target guid.
2026-03-09 22:53:09 -07:00
Kelsi
52c1fed6ab game: implement dual-spec switch via CMSG_SET_ACTIVE_TALENT_GROUP (0x4C3)
switchTalentSpec() was only updating local state without notifying the
server, leaving the server out of sync with the client's active talent
group. Now sends CMSG_SET_ACTIVE_TALENT_GROUP_OBSOLETE (WotLK wire
opcode 0x4C3) with the target group index (0=primary, 1=secondary),
prompting the server to apply the spec swap and respond with a fresh
SMSG_TALENTS_INFO for the newly active group.

Also adds ActivateTalentGroupPacket::build() to world_packets for the
packet construction.
2026-03-09 22:49:23 -07:00
Kelsi
3e5760aefe ui: add battleground score frame for WSG/AB/AV/EotS/SotA
Renders a compact top-centre overlay showing Alliance vs Horde scores
when the player is in a recognised battleground map.  Score values are
read directly from the world state map maintained by SMSG_INIT_WORLD_STATES
and SMSG_UPDATE_WORLD_STATE, so no extra server packets are needed.

Supported maps:
  489 – Warsong Gulch    (flag captures, max 3)
  529 – Arathi Basin     (resources, max 1600)
   30 – Alterac Valley   (reinforcements, max 600)
  566 – Eye of the Storm (resources, max 1600)
  607 – Strand of Ancients
2026-03-09 22:42:44 -07:00
Kelsi
c44477fbee Implement corpse reclaim: store death position and show Resurrect button
When a player releases spirit, the server sends SMSG_DEATH_RELEASE_LOC
with the corpse map and position. Store this so the ghost can reclaim.

New flow:
- SMSG_DEATH_RELEASE_LOC now stores corpseMapId_/corpseX_/Y_/Z_ instead
  of logging and discarding
- canReclaimCorpse(): true when ghost is on same map within 40 yards of
  stored corpse position
- reclaimCorpse(): sends CMSG_RECLAIM_CORPSE (no payload)
- renderReclaimCorpseButton(): shows "Resurrect from Corpse" button at
  bottom-center when canReclaimCorpse() is true
2026-03-09 22:31:56 -07:00
Kelsi
c6e39707de Fix resurrect: correct packet routing and show caster name in dialog
Two bugs fixed:

1. acceptResurrect() was always sending CMSG_SPIRIT_HEALER_ACTIVATE even
   for player-cast resurrections (Priest/Paladin/Druid). That opcode is
   only the correct response to SMSG_SPIRIT_HEALER_CONFIRM. For
   SMSG_RESURRECT_REQUEST the server expects CMSG_RESURRECT_RESPONSE
   with accept=1. Added resurrectIsSpiritHealer_ to track which path
   triggered the dialog and send the right packet per type.

2. The resurrect dialog showed a generic "Return to life?" string
   regardless of who cast the resurrection. Parse the optional CString
   name from SMSG_RESURRECT_REQUEST (or fall back to playerNameCache)
   and display "X wishes to resurrect you." when the caster is known.
2026-03-09 22:27:24 -07:00
Kelsi
edd7e5e591 Fix shadow flashing: per-frame shadow depth images and framebuffers
Single shadow depth image shared across MAX_FRAMES=2 in-flight GPU frames
caused a race: frame N's main pass reads shadow map while frame N+1's
shadow pass clears and writes it, producing visible flashing standing
still and while moving.

Fix: give each in-flight frame its own VkImage, VmaAllocation, VkImageView,
and VkFramebuffer for the shadow depth attachment. renderShadowPass() now
indexes all shadow resources by getCurrentFrame(), and layout transitions
track per-frame state in shadowDepthLayout_[frame]. Cleanup loops over
MAX_FRAMES=2. Descriptor sets already written per-frame; updated shadow
image view binding to use the matching per-frame view.
2026-03-09 22:14:32 -07:00
Kelsi
d5de031c23 tbc: fix quest log stride and CMSG_QUESTGIVER_QUERY_QUEST format
TBC 2.4.3 quest log update fields use 4 fields per slot
(questId, state, counts, timer) vs WotLK's 5 (extra counts field).
The wrong stride (5) caused all quest log reads to use wrong indices
beyond the first slot, breaking quest tracking on TBC servers.

TBC 2.4.3 CMSG_QUESTGIVER_QUERY_QUEST is guid(8) + questId(4) = 12
bytes. WotLK added a trailing isDialogContinued(u8) byte that TBC
servers don't expect; sending it caused quest details to not be sent
back on some emulators.
2026-03-09 22:04:18 -07:00
Kelsi
8f0d2cc4ab terrain: pre-load bind point tiles during Hearthstone cast
When the player starts casting Hearthstone (spell IDs 6948/8690),
trigger background terrain loading at the bind point so tiles are
ready when the teleport fires.

- Add HearthstonePreloadCallback to GameHandler, called from
  handleSpellStart when a Hearthstone cast begins.
- Application callback enqueues a 5×5 tile grid around the bind
  point via precacheTiles() (same-map) or starts a file-cache warm
  via startWorldPreload() (cross-map) during the ~10 s cast time.
- On same-map teleport arrival, call processAllReadyTiles() to
  GPU-upload any tiles that finished parsing during the cast before
  the first frame at the new position.

Fixes: player landing in unloaded terrain and falling after Hearthstone.
2026-03-09 21:57:42 -07:00
Kelsi
0a6f88e8ad tbc: fix SMSG_SPELL_START and SMSG_SPELL_GO for TBC 2.4.3
TBC 2.4.3 SMSG_SPELL_START and SMSG_SPELL_GO send full uint64 GUIDs for
casterGuid/casterUnit and hit targets.  WotLK uses packed (variable-length)
GUIDs.  Using readPackedGuid() on a full uint64 reads the first byte as the
bitmask, consuming 1-8 wrong bytes, which shifts all subsequent fields
(spellId, castFlags, castTime) and causes:
  - Cast bar to never show for the player's own spells
  - Sound effects to use the wrong spell ID
  - Hit/miss target tracking to be completely wrong

Additionally, TBC SMSG_SPELL_GO lacks the WotLK timestamp field after
castFlags.

Add TbcPacketParsers::parseSpellStart and ::parseSpellGo using full GUIDs,
add virtual base methods, and route both handlers through virtual dispatch.
2026-03-09 21:48:41 -07:00
Kelsi
921c83df2e tbc: fix SMSG_CAST_RESULT — no castCount prefix in TBC 2.4.3
TBC 2.4.3 SMSG_CAST_RESULT sends spellId(u32) + result(u8) = 5 bytes.
WotLK 3.3.5a added a castCount(u8) prefix making it 6 bytes.  Without
this fix the WotLK parser was reading spellId[0] as castCount, then the
remaining 3 spellId bytes plus result byte as spellId (wrong), and then
whatever follows as result — producing incorrect failure messages and
potentially not clearing the cast bar on TBC.

Add TbcPacketParsers::parseCastResult override and a virtual base method,
then route SMSG_CAST_RESULT through virtual dispatch in the game handler.
2026-03-09 21:46:18 -07:00
Kelsi
1b2c7f595e classic: fix SMSG_CREATURE_QUERY_RESPONSE — no iconName field in 1.12
Classic 1.12 SMSG_CREATURE_QUERY_RESPONSE has no iconName CString between
subName and typeFlags.  The TBC/WotLK parser was reading the typeFlags
uint32 bytes as the iconName string, then reading the remaining bytes as
typeFlags — producing garbage creature type/family/rank values and
corrupting target frame display for all creatures on Classic servers.

Add ClassicPacketParsers::parseCreatureQueryResponse without the iconName
read, and route the game handler through virtual dispatch so the override
is called.
2026-03-09 21:44:07 -07:00
Kelsi
63d8200303 tbc: fix heal log GUID parsing and route combat through virtual dispatch
Add TbcPacketParsers::parseSpellHealLog override using full uint64 GUIDs
(TBC) instead of packed GUIDs (WotLK).  Route handleAttackerStateUpdate,
handleSpellDamageLog, and handleSpellHealLog through the virtual
packetParsers_ interface so expansion-specific overrides are actually
called.  Previously the game handler bypassed virtual dispatch with
direct static parser calls, making all three TBC overrides dead code.
2026-03-09 21:36:12 -07:00
Kelsi
b4f744d000 tbc: fix combat damage parsing for TBC 2.4.3
TBC 2.4.3 SMSG_ATTACKERSTATEUPDATE and SMSG_SPELLNONMELEEDAMAGELOG send
full uint64 GUIDs for attacker/target, while WotLK 3.3.5a uses packed
(variable-length) GUIDs.  Using the WotLK reader on TBC packets consumes
1-8 bytes where a fixed 8 are expected, shifting all subsequent reads
and producing completely wrong damage/absorbed/resisted values.

Add TbcPacketParsers overrides that read plain uint64 GUIDs.  Also note
that TBC SMSG_SPELLNONMELEEDAMAGELOG lacks the WotLK overkill field.
2026-03-09 21:34:02 -07:00
Kelsi
1c967e9628 tbc: fix SMSG_MAIL_LIST_RESULT parsing for TBC 2.4.3
TBC 2.4.3 differs from WotLK in four ways:
- Header: uint8 count only (WotLK: uint32 totalCount + uint8 shownCount),
  so the WotLK parser was reading 4 garbage bytes before the count
- No extra unknown uint32 between itemTextId and stationery in each entry
- Attachment item GUID: full uint64 (WotLK uses uint32 low GUID)
- Attachment enchants: 7 × uint32 id only (WotLK: 7 × {id+duration+charges})

The resulting mis-parse would scramble subject/money/cod/flags for every
mail entry and corrupt all attachment reads.  Add TbcPacketParsers::parseMailList
with the correct TBC 2.4.3 format.
2026-03-09 21:30:45 -07:00
Kelsi
4d1be18c18 wmo: apply MOHD ambient color to interior group lighting
Read the ambient color from the MOHD chunk (BGRA uint32) and store it
on WMOModel as a normalized RGB vec3.  Pass it through ModelData into
the per-batch WMOMaterialUBO (replacing the unused pad[3] bytes, keeping
the struct at 64 bytes).  The GLSL interior branch now floors vertex
colors against the WMO ambient instead of a hardcoded 0.5, so dungeon
interiors respect the artist-specified ambient tint from the WMO root
rather than always clamping to grey.
2026-03-09 21:27:01 -07:00
Kelsi
8561d5c58c tbc: fix gossip message quest parsing for TBC 2.4.3
SMSG_GOSSIP_MESSAGE quest entries in TBC 2.4.3 do not include
questFlags(u32) or isRepeatable(u8) that WotLK 3.3.5a added.
The WotLK default parser reads these 5 bytes, causing all quest titles
in gossip dialogs to be shifted/corrupted on TBC servers.

Add TbcPacketParsers::parseGossipMessage() which parses quest entries
without those fields, fixing NPC quest list display.
2026-03-09 21:20:37 -07:00
Kelsi
38333df260 tbc: fix spell cast format and NPC movement parsing for TBC 2.4.3
CMSG_CAST_SPELL: WotLK adds a castFlags(u8) byte after spellId that TBC
2.4.3 does not have. Add TbcPacketParsers::buildCastSpell() to omit it,
preventing every spell cast from being rejected by TBC servers.

CMSG_USE_ITEM: WotLK adds a glyphIndex(u32) field between itemGuid and
castFlags that TBC 2.4.3 does not have. Add buildUseItem() override.

SMSG_MONSTER_MOVE: WotLK adds a uint8 unk byte after the packed GUID
(MOVEMENTFLAG2_UNK7 toggle) that TBC 2.4.3 does not have. Add
parseMonsterMove() override to fix NPC movement parsing — without this,
all NPC positions, durations, and waypoints parse from the wrong byte
offset, making all NPC movement appear broken on TBC servers.
2026-03-09 21:14:06 -07:00
Kelsi
9d1616a11b audio: stop precast sound on spell completion, failure, or interrupt
Add AudioEngine::playSound2DStoppable() + stopSound() so callers can
hold a handle and cancel playback early. SpellSoundManager::playPrecast()
now stores the handle in activePrecastId_; stopPrecast() cuts the sound.

playCast() calls stopPrecast() before playing the release sound, so the
channeling audio never bleeds past cast time. SMSG_SPELL_FAILURE and
SMSG_CAST_FAILED both call stopPrecast() so interrupted casts silence
immediately.
2026-03-09 21:04:24 -07:00
Kelsi
e0d47040d3 Fix main-thread hang from terrain finalization; two-pass M2 rendering; tile streaming improvements
Hang/GPU device lost fix:
- M2_INSTANCES and WMO_INSTANCES finalization phases now create instances
  incrementally (32 per step / 4 per step) instead of all at once, eliminating
  the >1s main-thread stalls that caused GPU fence timeouts and device loss

M2 two-pass transparent rendering:
- Opaque/alpha-test batches render in pass 1, transparent/additive in pass 2
  (back-to-front sorted) to fix wing transparency showing terrain instead of
  trees — adds hasTransparentBatches flag to skip models with no transparency

Tile streaming improvements:
- Sort new load queue entries nearest-first so critical tiles load before
  distant ones during fast taxi flight
- Increase taxi load radius 6→8 tiles, unload 9→12 for better coverage

Water refraction gated on FSR:
- Disable water refraction when FSR is not active (bugged without upscaling)
- Auto-disable refraction if FSR is turned off while refraction was on
2026-03-09 20:58:49 -07:00
Kelsi
3f64f81ec0 Implement MSG_RAID_READY_CHECK_CONFIRM and MSG_RAID_READY_CHECK_FINISHED
Track individual member ready/not-ready responses in chat and summarize
results when all members have responded to the ready check.
2026-03-09 20:38:44 -07:00
Kelsi
95e8fcb88e Implement minimap ping: parse MSG_MINIMAP_PING and render animated ping circles
Parse party member minimap pings (packed GUID + posX + posY), store with
5s lifetime, and render as expanding concentric circles on the minimap.
2026-03-09 20:36:20 -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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Kelsi
4e3d50fc20 Wire SMSG_PLAY_MUSIC to MusicManager via SoundEntries.dbc lookup
Add PlayMusicCallback to GameHandler so SMSG_PLAY_MUSIC (and the
vanilla 0x0103 alias) dispatch a soundId to the registered handler
instead of being silently consumed. Application.cpp registers the
callback, loads SoundEntries.dbc, resolves the first non-empty
Name+DirectoryBase into an MPQ path, and passes it to MusicManager
for non-looping playback. Resolves the TODO in the SMSG_PLAY_MUSIC
handler.
2026-03-09 15:46:19 -07:00
Kelsi
28c755040f Request completed quests on world entry and expose via public API
- Send CMSG_QUERY_QUESTS_COMPLETED on initial world entry so
  completedQuests_ is populated from the server response
- Clear completedQuests_ on world entry to avoid stale data across sessions
- Add isQuestCompleted(questId) and getCompletedQuests() public accessors
  to allow UI layers to filter NPC quest offers by completion state
2026-03-09 15:32:11 -07:00
Kelsi
99f2b30594 Handle 35+ more SMSG opcodes for quests, combat, pet system, and protocol
- SMSG_QUESTGIVER_QUEST_FAILED: show specific failure reason in chat
- SMSG_SUSPEND_COMMS: reply with CMSG_SUSPEND_COMMS_ACK (required by server)
- SMSG_PRE_RESURRECT: consume packed GUID
- SMSG_PLAYERBINDERROR: show bind error message
- SMSG_RAID_GROUP_ONLY: show instance requires raid group message
- SMSG_RAID_READY_CHECK_ERROR: show ready check error message
- SMSG_RESET_FAILED_NOTIFY: show instance reset blocked message
- SMSG_REALM_SPLIT / SMSG_REAL_GROUP_UPDATE: consume
- SMSG_PLAY_MUSIC: consume (hook point for future music integration)
- SMSG_PLAY_OBJECT_SOUND / SMSG_PLAY_SPELL_IMPACT: consume
- SMSG_RESISTLOG: consume
- SMSG_READ_ITEM_OK / SMSG_READ_ITEM_FAILED: show result messages
- SMSG_QUERY_QUESTS_COMPLETED_RESPONSE: parse and cache completed quest IDs
- SMSG_QUESTUPDATE_ADD_PVP_KILL: show PVP kill progress in chat
- SMSG_NPC_WONT_TALK: show "creature can't talk" message
- SMSG_OFFER_PETITION_ERROR: show specific petition error
- SMSG_PETITION_QUERY_RESPONSE / SHOW_SIGNATURES / SIGN_RESULTS: consume
- SMSG_PET_GUIDS / MODE / BROKEN / CAST_FAILED / SOUND / LEARN / UNLEARN / etc: consume
- SMSG_INSPECT: consume (character inspection)
- SMSG_MULTIPLE_MOVES / SMSG_MULTIPLE_PACKETS: consume
- SMSG_SET_PLAYER_DECLINED_NAMES_RESULT / PROPOSE_LEVEL_GRANT: consume
- SMSG_REFER_A_FRIEND_* / REPORT_PVP_AFK_RESULT / REDIRECT_CLIENT: consume
- SMSG_PVP_QUEUE_STATS / NOTIFY_DEST_LOC_SPELL_CAST / RESPOND_INSPECT_ACHIEVEMENTS: consume
- SMSG_PLAYER_SKINNED / QUEST_POI_QUERY_RESPONSE / PLAY_TIME_WARNING: consume
- SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA / RESET_RANGED_COMBAT_TIMER: consume
- SMSG_PROFILEDATA_RESPONSE: consume

Adds completedQuests_ set for tracking server-reported completed quest IDs.
2026-03-09 15:27:20 -07:00