Commit graph

1967 commits

Author SHA1 Message Date
Kelsi
2be793cfba feat: improve calendar lockout and char rename messages
- SMSG_CALENDAR_RAID_LOCKOUT_ADDED: show dungeon name (from Map.dbc)
  and difficulty label (Normal/Heroic/25-Man/25-Man Heroic)
- SMSG_CALENDAR_RAID_LOCKOUT_REMOVED: show dungeon name instead of raw
  map ID; now emits a chat message (was silent/LOG_DEBUG only)
- SMSG_CHAR_RENAME: map result codes 1-7 to human-readable strings
  ("Name already in use.", "Name too short.", etc.) instead of
  "Character rename failed (error N)."
2026-03-13 07:14:40 -07:00
Kelsi
2c72d8462d feat: add map name lookups and improve instance/RAF/misc feedback
- Add getMapName() helper backed by Map.dbc (parallel to getAreaName)
- SMSG_RAID_INSTANCE_MESSAGE: show dungeon name instead of raw map ID
- SMSG_INSTANCE_RESET: show dungeon name instead of raw map ID
- SMSG_INSTANCE_RESET_FAILED: show dungeon name instead of raw map ID
- SMSG_EQUIPMENT_SET_SAVED: show "Equipment set saved." confirmation
- SMSG_PROPOSE_LEVEL_GRANT: show mentor name offering a level grant (RAF)
- SMSG_REFER_A_FRIEND_EXPIRED: show link-expired message
- SMSG_REFER_A_FRIEND_FAILURE: show reason-mapped error message
- SMSG_REPORT_PVP_AFK_RESULT: show success/failure feedback
- SMSG_QUEST_CONFIRM_ACCEPT: add playerNameCache fallback for sharer name
2026-03-13 07:10:10 -07:00
Kelsi
28ce441214 feat: add missing player feedback for level-up, pet spells, and pet name errors
- SMSG_LEVELUP_INFO: show \"You have reached level N!\" chat message on level-up
  (was only calling the UI ding callback without any chat notification)
- SMSG_PET_LEARNED_SPELL: show \"Your pet has learned X.\" with spell name lookup
  (was LOG_DEBUG only)
- SMSG_PET_NAME_INVALID: show \"That pet name is invalid.\"
  (was silently consumed)
2026-03-13 07:02:20 -07:00
Kelsi
8e67a41983 fix: show battleground names instead of IDs in SMSG_BATTLEFIELD_STATUS messages
Replace raw \"Battleground #2\" with proper names (Warsong Gulch, Arathi Basin,
Eye of the Storm, Strand of the Ancients, Isle of Conquest, arena names, etc.)
for all three expansions' BG type ID space.
2026-03-13 06:59:02 -07:00
Kelsi
20b59c9d63 fix: correct LFG vote kick result logic and show item names in dungeon rewards
- handleLfgBootProposalUpdate: was using myAnswer (player's own vote) to determine
  if the boot passed — should use bootVotes >= votesNeeded instead. Player who voted
  yes would see "passed" even if the vote failed, and vice versa.
- handleLfgPlayerReward: look up item name from item cache instead of showing
  raw "item #12345" for dungeon reward items
2026-03-13 06:56:37 -07:00
Kelsi
cf88a960f4 feat: add missing SMSG_CHANNEL_NOTIFY feedback for 12 unhandled notification types
Previously, PLAYER_NOT_FOUND, ANNOUNCEMENTS_ON/OFF, MODERATION_ON/OFF,
PLAYER_BANNED, PLAYER_UNBANNED, PLAYER_NOT_BANNED, INVITE, WRONG_FACTION,
INVITE_WRONG_FACTION, NOT_MODERATED, PLAYER_INVITED, and PLAYER_INVITE_BANNED
all fell silently to the default log-only path. Now each shows an appropriate
system message in chat.
2026-03-13 06:48:12 -07:00
Kelsi
9216a6da28 fix: show zone names in hearthstone bind messages, add playerNameCache to duel challenger
- SMSG_BINDPOINTUPDATE: show zone name in \"Your home has been set to X.\" (was just \"Your home has been set.\")
- SMSG_PLAYERBOUND: replace \"map N, zone N\" raw IDs with zone name lookup
- SMSG_BINDER_CONFIRM: suppress redundant \"This innkeeper is now your home location.\" since SMSG_PLAYERBOUND fires immediately after with zone context
- SMSG_DUEL_REQUESTED: add playerNameCache fallback before hex GUID for challenger name
2026-03-13 06:46:56 -07:00
Kelsi
ecc02595de fix: improve guild command result messages and suppress repeated guild name announcements
- Map GuildCommandError codes to human-readable strings instead of showing raw
  error numbers (e.g. \"error 4\" → \"No player named X is online.\")
- Handle errorCode==0 for QUIT command: show \"You have left the guild.\" and
  clear guild state (name, ranks, roster) — previously silent
- Handle errorCode==0 for CREATE and INVITE commands with appropriate messages
- Substitute %s-style error messages with the player name from data.name
- Suppress repeated \"Guild: <Name>\" chat message on every SMSG_GUILD_QUERY_RESPONSE;
  only announce once when the guild name is first learned at login
2026-03-13 06:43:11 -07:00
Kelsi
e882110e7f fix: suppress repeated group-join chat spam on every SMSG_GROUP_LIST update
Previously "You are now in a group with N members." was shown on every
GROUP_LIST packet, which fires for each party stat update. Now only show
a message on actual state transitions: joining, leaving the group.
2026-03-13 06:38:50 -07:00
Kelsi
3a143b9b5b fix: use playerNameCache fallback and show zone name in summon/trade requests
- SMSG_SUMMON_REQUEST: fall back to playerNameCache when entity not in
  range; include zone name from getAreaName() in the summon message
  (e.g. "Bob is summoning you to Stormwind.")
- SMSG_TRADE_STATUS BEGIN_TRADE: fall back to playerNameCache when the
  trade initiator's entity is not visible
2026-03-13 06:37:26 -07:00
Kelsi
8a81ffa29c fix: show quest name in QUESTGIVER_QUEST_FAILED, use playerNameCache for achievements
- SMSG_QUESTGIVER_QUEST_FAILED: look up quest title from questLog_ and
  include it in the failure message (same pattern as QUESTUPDATE_FAILED
  fix from previous session)
- SMSG_ACHIEVEMENT_EARNED: fall back to playerNameCache for non-visible
  players before showing a raw hex GUID in the achievement message
2026-03-13 06:36:04 -07:00
Kelsi
b1015abffe fix: use actual pct in durability death message, fix pet cast failure feedback
- SMSG_DURABILITY_DAMAGE_DEATH: use the actual pct field from the packet
  instead of hardcoding "10%"
- SMSG_PET_CAST_FAILED: read reason as uint8 (not uint32), look up spell
  name and show human-readable failure reason to player
- Trade status 9 (REJECTED): show "Trade declined." instead of "Trade
  cancelled." to distinguish explicit decline from cancellation
2026-03-13 06:30:30 -07:00
Kelsi
2a52aedbf7 fix: show quest name instead of ID in failed/timed-out quest messages
SMSG_QUESTUPDATE_FAILED and SMSG_QUESTUPDATE_FAILEDTIMER were emitting
generic "Quest 12345 failed!" messages. Now looks up the title from
questLog_ and shows e.g. "\"Report to Gryan Stoutmantle\" failed!" for
a much more readable notification. Falls back to the generic form if
the title is not cached.
2026-03-13 06:24:16 -07:00
Kelsi
156ddfad9a fix: pass power type from POWER_DRAIN energize to color-code combat text
SMSG_SPELLLOGEXECUTE POWER_DRAIN reads drainPower but was not passing it
to addCombatText, so drained-resource returns showed as blue (mana) even
for rage or energy. Now correctly colored following the energize palette
added in the earlier commit.
2026-03-13 06:22:51 -07:00
Kelsi
43b007cdcd fix: only show SMSG_DISPEL_FAILED message when player is the caster
The dispel-failed handler was showing the failure notification for every
dispel attempt in the party/raid, regardless of who cast it. Now checks
casterGuid == playerGuid before showing "X failed to dispel." so only
the player's own failed dispels surface in chat.
2026-03-13 06:21:33 -07:00
Kelsi
d79c79e1bc feat: show chat messages for channel notification events
SMSG_CHANNEL_NOTIFY carries many event types that were silently dropped
in the default case: wrong password, muted, banned, throttled, kicked,
not owner, not moderator, password changed, owner changed, invalid name,
not in area, not in LFG. These are now surfaced as system chat messages
matching WoW-standard phrasing.
2026-03-13 06:18:23 -07:00
Kelsi
38ab1e0aea fix: show correct duel fled message when loser left the duel area
SMSG_DUEL_WINNER type=1 means the loser fled the duel zone rather than
being defeated; was previously treated the same as a normal win. Now
shows "X has fled from the duel. Y wins!" for the flee case vs the
standard "X has defeated Y in a duel!" for a normal outcome.
2026-03-13 06:16:19 -07:00
Kelsi
3b499d6871 fix: prefix SMSG_SPELL_FAILURE error message with spell name
Previously a spell failure like "Not in range" gave no context about
which spell failed. Now the message reads e.g. "Fireball: Not in range"
using the spell name from the DBC cache. Falls back to the bare reason
string if the spell name is not yet cached.
2026-03-13 06:14:28 -07:00
Kelsi
c58fc3073f fix: clear action bar slots when spells are removed or unlearned
SMSG_REMOVED_SPELL and SMSG_SEND_UNLEARN_SPELLS both erased spells from
knownSpells but left stale references on the action bar. After a respec
or forced spell removal, action bar buttons would show removed talents
and spells as still present. Now both handlers clear matching slots and
persist the updated bar layout.
2026-03-13 06:11:10 -07:00
Kelsi
d9b9d1d2f2 fix: show dodge/parry/block/immune combat text when enemy spell misses player
SMSG_SPELLLOGMISS contains miss events for both directions: spells the
player cast that missed, and enemy spells that missed the player. The
victim side (dodge/parry/block/immune/absorb/resist) was silently
discarded. Now both caster==player and victim==player generate the
appropriate combat text floater.
2026-03-13 06:09:42 -07:00
Kelsi
4507a223cc feat: color-code ENERGIZE combat text by power type
Mana (0)=blue, Rage (1)=red, Focus (2)=orange, Energy (3)=yellow,
Runic Power (6)=teal. Previously all energize events showed as blue
regardless of resource type, making it impossible to distinguish
e.g. a Warrior's Rage generation from a Mage's Mana return.

Power type is now captured from SMSG_SPELLENERGIZELOG (uint8) and
SMSG_PERIODICAURALOG OBS_MOD_POWER/PERIODIC_ENERGIZE (uint32 cast
to uint8) and stored in CombatTextEntry::powerType.
2026-03-13 06:08:21 -07:00
Kelsi
b9c16e9be5 fix: suppress duplicate "Upgraded to X" message on trainer rank-up
When buying a higher spell rank from a trainer, SMSG_TRAINER_BUY_SUCCEEDED
already announces "You have learned X", and SMSG_SUPERCEDED_SPELLS would
then also print "Upgraded to X" — two messages for one action.

Fix: check if the new spell ID was already in knownSpells before inserting
it in handleSupercededSpell. If so, the trainer handler already announced
it and we skip the redundant "Upgraded to" message. Non-trainer supersedes
(quest rewards, etc.) where the spell wasn't pre-inserted still show it.
2026-03-13 06:00:39 -07:00
Kelsi
dfe091473c fix: show action bar cooldown timers for spells on cooldown at login
SMSG_INITIAL_SPELLS delivers active cooldowns which were stored in
spellCooldowns but never propagated to the action bar slot
cooldownRemaining/cooldownTotal fields. This meant that spells with
remaining cooldowns at login time showed no countdown overlay on the
action bar. Sync the action bar slots from spellCooldowns after
loadCharacterConfig() to restore the correct timers.
2026-03-13 05:58:57 -07:00
Kelsi
2b131548aa fix: show descriptive party command error messages
Replace the generic "Party command failed (error N)" message with
WoW-standard error strings for each PartyResult code, matching what
the original client displays (e.g. "Your party is full.", "%s is
already in a group.", "%s is ignoring you.", etc.).
2026-03-13 05:54:01 -07:00
Kelsi
b34df01331 fix: suppress duplicate chat message when learning trainer spell
SMSG_TRAINER_BUY_SUCCEEDED pre-inserts the spell into knownSpells and
shows "You have learned X." The subsequent SMSG_LEARNED_SPELL packet
would then show a second "You have learned a new spell: X." message.

Fix: check if the spell was already in knownSpells before inserting in
handleLearnedSpell. If it was pre-inserted by the trainer handler, skip
the chat notification to avoid the duplicate.
2026-03-13 05:51:15 -07:00
Kelsi
7dc12bb35e fix: persist action bar config after superceded spell slot upgrade
After automatically upgrading action bar slots to the new spell rank
in handleSupercededSpell, save the character config so the upgraded
slot IDs persist across sessions.
2026-03-13 05:42:24 -07:00
Kelsi
2d587d0d4b feat: upgrade action bar slots to new spell rank on supercede
When a spell is superceded (e.g. Fireball Rank 1 -> Rank 2 after
training), update any action bar slots referencing the old spell ID
to point to the new rank. This matches WoW client behaviour where
training a new rank automatically upgrades your action bars so you
don't have to manually re-place the spell.
2026-03-13 05:37:15 -07:00
Kelsi
3c704088af fix: clear lastInteractedGoGuid_ on world transfer in handleNewWorld
Same-map teleports (dungeon teleporters, etc.) clear casting state but
were not clearing lastInteractedGoGuid_.  If a gather cast was in progress
when the teleport happened, the stale GO guid could theoretically trigger
a spurious CMSG_LOOT on the destination map.

Also clears lastInteractedGoGuid_ in handleNewWorld alongside the rest of
the casting-state teardown for consistency with other reset paths.
2026-03-13 05:26:27 -07:00
Kelsi
103bb5a513 fix: query item info in SMSG_LOOT_START_ROLL and use live name in roll popup
SMSG_LOOT_START_ROLL was not calling queryItemInfo(), so the roll popup
would display item IDs instead of names when the item had not been
previously cached (e.g. first time seeing that item in the session).

Also update renderLootRollPopup to prefer the live ItemQueryResponseData
name/quality over the snapshot captured at parse time, so the popup
shows the correct name once SMSG_ITEM_QUERY_SINGLE_RESPONSE arrives.
2026-03-13 05:23:31 -07:00
Kelsi
48bcee32b4 fix: match spell ID in handleSpellGo to prevent proc spells triggering gather loot
wasInTimedCast checked casting == true but not whether the completing spell
was actually the gather cast.  A triggered/proc spell (SMSG_SPELL_GO with
a different spellId) could arrive while a gather cast is active (casting==true),
satisfying the old guard and firing lootTarget prematurely.

Require data.spellId == currentCastSpellId so only the spell that started
the cast bar triggers the post-gather CMSG_LOOT dispatch.
2026-03-13 05:12:22 -07:00
Kelsi
f44defec38 feat: show fish-hooked notification when fishing bobber splashes
When the server sends SMSG_GAMEOBJECT_CUSTOM_ANIM with animId=0 for a GO
of type 17 (FISHINGNODE), a fish has been hooked and the player needs to
click the bobber quickly.  Add a system chat message and a UI sound to
alert the player — previously there was no visual/audio feedback beyond
the bobber animation itself.
2026-03-13 05:07:51 -07:00
Kelsi
01f4ef5e79 fix: clear lastInteractedGoGuid_ for non-lootable GO interactions
Mailboxes, doors, buttons, and other non-lootable GOs set shouldSendLoot=false
so no CMSG_LOOT is dispatched — but lastInteractedGoGuid_ was still set.
Without SMSG_LOOT_RESPONSE to clear it, a subsequent timed cast completion
(e.g. player buffs at the mailbox) would fire a spurious CMSG_LOOT for the
mailbox GUID.
2026-03-13 05:06:00 -07:00
Kelsi
6878f120e9 fix: clear lastInteractedGoGuid_ in handleCastFailed path
SMSG_CAST_FAILED is a direct rejection (e.g. insufficient range, no mana)
before the cast starts.  Missing this path meant a stale gather-node guid
could survive into the next timed cast if SMSG_CAST_FAILED fired instead
of SMSG_SPELL_FAILURE.
2026-03-13 05:03:50 -07:00
Kelsi
7a4347dbac fix: clear lastInteractedGoGuid_ on cast failure, cancel, and world reset
If a gather cast was interrupted by SMSG_SPELL_FAILURE (e.g. player took
damage during mining), lastInteractedGoGuid_ was left set.  A subsequent
timed cast completion would then fire CMSG_LOOT for the stale node even
though the gather never completed.

Clear lastInteractedGoGuid_ in all cast-termination paths:
- SMSG_SPELL_FAILURE (cast interrupted by server)
- SMSG_CAST_RESULT non-zero (cast rejected before it started)
- cancelCast() (player or system cancelled the cast)
- World reset / logout block (state-clear boundary)
2026-03-13 05:02:58 -07:00
Kelsi
cc2b413e22 fix: guard gather-node CMSG_LOOT dispatch against instant casts and proc spells
handleSpellGo fired lootTarget(lastInteractedGoGuid_) on ANY player spell
completion, including instant casts and proc/triggered spells that arrive
while the gather cast is still in flight.  Save the casting flag before
clearing it and only dispatch CMSG_LOOT when wasInTimedCast is true — this
ensures only the gather cast completion triggers the post-gather loot send,
not unrelated instant spells that also produce SMSG_SPELL_GO.
2026-03-13 04:59:05 -07:00
Kelsi
2c6902d27d fix: mining nodes no longer report invalid target and now open loot after gather
Two bugs fixed:
1. Retry logic (for Classic) re-sent CMSG_GAMEOBJ_USE at 0.15s while the
   gather cast was in-flight, causing SPELL_FAILED_BAD_TARGETS. Now clears
   pendingGameObjectLootRetries_ as soon as SMSG_SPELL_START shows the player
   started a cast (gather accepted).

2. CMSG_LOOT was sent immediately before the gather cast completed, then
   never sent again — so the loot window never opened. Now tracks the last
   interacted GO and sends CMSG_LOOT in handleSpellGo once the gather spell
   completes, matching how the real client behaves.
2026-03-13 04:37:36 -07:00
Kelsi
4272491d56 feat: send CMSG_SET_ACTION_BUTTON to server when action bar slot changes
Action bar changes (dragging spells/items) were only saved locally.
Now notifies the server via CMSG_SET_ACTION_BUTTON so the layout
persists across relogs. Supports Classic (5-byte) and TBC/WotLK
(packed uint32) wire formats.
2026-03-13 04:25:05 -07:00
Kelsi
8f08d75748 fix: cache player death position so corpse reclaim works in Classic
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
Classic 1.12 does not send SMSG_DEATH_RELEASE_LOC, leaving corpseMapId_=0
and preventing the 'Resurrect from Corpse' button from appearing.

- When health reaches 0 via VALUES update, immediately cache movementInfo
  as corpse position (canonical->server axis swap applied correctly)
- Do the same on UNIT_DYNFLAG_DEAD set path
- Clear corpseMapId_ when ghost flag is removed (corpse reclaimed)
- Clear corpseMapId_ in same-map spirit-healer resurrection path

The CORPSE object detection (UPDATE_OBJECT) and SMSG_DEATH_RELEASE_LOC
(TBC/WotLK) will still override with exact server coordinates when received.
2026-03-13 04:04:38 -07:00
Kelsi
499638142e feat: make quest tracker movable, resizable, and right-edge-anchored
- Remove NoDecoration flag to allow ImGui drag/resize
- Store questTrackerRightOffset_ instead of absolute X so tracker
  stays pinned to the right edge when the window is resized
- Persist position (right offset + Y) and size in settings.cfg
- Clamp to screen bounds after drag
2026-03-13 04:04:29 -07:00
Kelsi
85767187b1 fix: clear gameObjectDisplayIdWmoCache_ on world transition, add stale-entry guard
gameObjectDisplayIdWmoCache_ was not cleared on world unload/transition,
causing stale WMO model IDs (e.g. 40006, 40003) to be looked up after
the renderer cleared its model list, resulting in "Cannot create instance
of unloaded WMO model" errors on zone re-entry.

Changes:
- Clear gameObjectDisplayIdWmoCache_ alongside other GO caches on world reset
- Add WMORenderer::isModelLoaded() for cache-hit validation
- Inline GO WMO path now verifies cached model is still renderer-resident
  before using it; evicts stale entries and falls back to reload
2026-03-13 03:43:55 -07:00
Kelsi
0487d2eda6 fix: check loadModel return before createInstance for WMO doodads
When the M2 model cache is full (>6000 entries), loadModel() returns
false and the model is never added to the GPU cache. The WMO instance
doodad path was calling createInstanceWithMatrix() unconditionally,
generating hundreds of "Cannot create instance: model X not loaded"
warnings on zone entry. Add the same guard already present in the
terrain doodad path.
2026-03-13 03:41:42 -07:00
Kelsi
863faf9b54 fix: correct talent rank indexing — store 1-indexed, fix prereq and learn checks
SMSG_TALENTS_INFO wire format sends 0-indexed ranks (0=has rank 1). Both
handlers were storing raw 0-indexed values, but handleSpellLearnedServer
correctly stored rank+1 (1-indexed). This caused:
 - getTalentRank() returning 0 for both "not learned" and "has rank 1",
   making pointsInTree always wrong and blocking tier access
 - Prereq check `prereqRank < DBC_prereqRank` always met when not learned
   (0 < 0 = false), incorrectly unlocking talents
 - Click handler sending wrong desiredRank to server

Fixes:
 - Both SMSG_TALENTS_INFO handlers: store rank+1u (1-indexed)
 - talent_screen.cpp prereq check: change < to <= (DBC is 0-indexed,
   storage is 1-indexed; must use > for "met", <= for "not met")
 - talent_screen.cpp click handler: send currentRank directly (1-indexed
   value equals what CMSG_LEARN_TALENT requestedRank expects)
 - Tooltip: display prereqRank+1 so "Requires 1 point" shows correctly
2026-03-13 03:32:45 -07:00
Kelsi
952f36b732 fix: correct minimap player arrow orientation (was 90° off, E/W appeared flipped) 2026-03-13 03:19:05 -07:00
Kelsi
ebd9cf5542 fix: handle MSG_MOVE_SET_*_SPEED opcodes to suppress unhandled opcode warnings 2026-03-13 03:13:29 -07:00
Kelsi
64439673ce fix: show repair button when vendor has NPC_FLAG_REPAIR (0x40) set
Vendors that open directly (without gossip menu) never triggered the
armorer gossip path, so canRepair was always false and the Repair button
was hidden. Now also check the NPC's unit flags for NPC_FLAG_REPAIR when
the vendor list arrives, fixing armorers accessed directly.
2026-03-13 03:06:45 -07:00
Kelsi
8f3f1b21af fix: check vertices before skin load so WotLK (v264) character M2s parse correctly
TBC races like Draenei use version-264 M2 files with no embedded skin;
indices come from a separate .skin file loaded after M2::load().
The premature isValid() check (which requires non-empty indices) always
failed for WotLK-format character models, making Draenei (and Blood Elf)
players invisible.

Fix: only check vertices.empty() right after load(), then validate fully
with isValid() after the skin file is loaded.
2026-03-13 02:58:42 -07:00
Kelsi
27213c1d40 fix: robust SMSG_ATTACKERSTATEUPDATE parsing for WotLK format
Two issues in the WotLK SMSG_ATTACKERSTATEUPDATE parser:

1. subDamageCount could read a school-mask byte when a packed GUID is
   off by one byte, producing values like 32/40/44/48 (shadow/frost/etc
   school masks) as the count. The parser then tried to read 32-48
   sub-damages before hitting EOF. Fix: silently clamp subDamageCount to
   floor(remaining/20) so we only attempt entries that actually fit.

2. After sub-damages, AzerothCore sends victimState(4)+unk1(4)+unk2(4)+
   overkill(4) (16 bytes), not the 8-byte victimState+overkill the
   parser was reading. Fix: consume unk1 and unk2 before reading overkill.
   Also handle the hitInfo-conditional HITINFO_BLOCK/RAGE_GAIN/FAKE_DAMAGE
   fields at the end of the packet.
2026-03-13 02:47:40 -07:00
Kelsi
1cd8e53b2f fix: handle SPLINEFLAG_ANIMATION in UPDATE_OBJECT legacy spline layout
When SPLINEFLAG_ANIMATION (0x00400000) is set, AzerothCore inserts 5 bytes
(uint8 animationType + int32 animTime) between durationModNext and
verticalAccel in the SMSG_UPDATE_OBJECT MoveSpline block. The parser was
not accounting for these bytes, causing verticalAccel, effectStartTime,
and pointCount to be read from the wrong offset.

This produced garbage pointCount values (e.g. 3322451254) triggering the
"Spline pointCount invalid (legacy+compact)" fallback path and breaking
UPDATE_OBJECT parsing for animated-spline entities, causing all subsequent
update blocks in the same packet to be dropped.
2026-03-13 02:38:53 -07:00
Kelsi
61adb4a803 fix: free terrain descriptor sets when unloading mid-finalization tiles
When unloadTile() was called for a tile still in finalizingTiles_
(mid-incremental-finalization), terrain chunks already uploaded to the
GPU (terrainMeshDone=true) were not being cleaned up. The early-return
path correctly removed water and M2/WMO instances but missed calling
terrainRenderer->removeTile(), causing descriptor sets to leak.

After ~20 minutes of play the VkDescriptorPool (MAX_MATERIAL_SETS=16384)
filled up, causing all subsequent terrain material allocations to fail
and the log to flood with "failed to allocate material descriptor set".

Fix: check fit->terrainMeshDone before the early return and call
terrainRenderer->removeTile() to free those descriptor sets.
2026-03-13 02:33:02 -07:00
Kelsi
862d743f87 fix: WMO culling dead-end group fallback and minimap arrow direction
wmo_renderer: when portal BFS starts from a group with no portal refs
(utility/transition group), the rest of the WMO becomes invisible because
BFS only adds the starting group. Fix: if cameraGroup has portalCount==0,
fall back to marking all groups visible (same as camera-outside behavior).

renderer: minimap player-orientation arrow was pointing the wrong direction.
The shader convention is arrowRotation=0→North, positive→clockwise (West),
negative→East. The correct mapping from canonical yaw is arrowRotation =
-canonical_yaw. Fixed both render paths (cameraController and gameHandler)
in both the FXAA and non-FXAA minimap render calls.

World-map W-key: already corrected in prior commit (13c096f); users with
stale ~/.wowee/settings.cfg should rebind via Settings > Controls.
2026-03-13 02:25:06 -07:00