Commit graph

1239 commits

Author SHA1 Message Date
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
6d21f77d32 game: route aura/spell-list parsing through virtual packet dispatch
AuraUpdateParser and InitialSpellsParser were called as static functions
in the game handler, bypassing the expansion-specific overrides added to
TbcPacketParsers.  Switch them to packetParsers_->parseAuraUpdate() and
packetParsers_->parseInitialSpells() so TBC 2.4.3 servers get the correct
parser for each.
2026-03-09 21:38:14 -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
a49c013c89 Fix SMSG_RESUME_CAST_BAR: separate from unrelated opcodes in fallthrough group
SMSG_LOOT_LIST, SMSG_COMPLAIN_RESULT, SMSG_ITEM_REFUND_INFO_RESPONSE,
and SMSG_ITEM_ENCHANT_TIME_UPDATE were incorrectly falling through to the
SMSG_RESUME_CAST_BAR handler, causing those packets to be parsed as
cast bar resume data with a completely different wire format.
2026-03-09 20:40:58 -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
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