Commit graph

1501 commits

Author SHA1 Message Date
Kelsi
79c8d93c45 fix: use expansion-aware field indices for spell icon loading
The spell icon loader was incorrectly assuming WotLK field 133 (IconID)
for any DBC with >= 200 fields. This breaks Classic/TBC where IconID
is at different fields:
- Classic: field 117
- TBC: field 124
- WotLK: field 133

Now always uses expansion-aware layout (spellL) when available, falling
back to hardcoded field 133 only if the layout is missing.

Fixes missing spell icons on Classic and TBC expansions.
2026-03-11 05:26:38 -07:00
Kelsi
593f06bdf7 fix: correct Classic/TBC loot packet format parsing (missing randomSuffix/randomPropId)
SMSG_LOOT_START_ROLL, SMSG_LOOT_ALL_PASSED, and loot roll handlers unconditionally
read randomSuffix and randomPropertyId fields. These fields only exist in WotLK 3.3.5a
and NOT in Classic 1.12 / TBC 2.4.3, causing packet stream corruption on Classic/TBC servers.

Packet format differences:
- WotLK: includes randomSuffix (4) + randomPropId (4) fields
- Classic/TBC: no random property fields

Fix gates the field reads based on active expansion:
- SMSG_LOOT_START_ROLL: WotLK 33 bytes vs Classic/TBC 25 bytes
- SMSG_LOOT_ALL_PASSED: WotLK 24 bytes vs Classic/TBC 16 bytes
- SMSG_LOOT_ROLL: WotLK 34 bytes vs Classic/TBC 26 bytes
- SMSG_LOOT_ROLL_WON: WotLK 34 bytes vs Classic/TBC 26 bytes

This prevents packet stream desynchronization when loot rolls occur on Classic/TBC servers.
2026-03-11 05:09:43 -07:00
Kelsi
dd67c88175 fix: conditionally include trailing byte in CMSG_BUY_ITEM for Classic/TBC
CMSG_BUY_ITEM format differs by expansion:
- WotLK 3.3.5a / AzerothCore: includes trailing uint8(0) after count field (17 bytes)
- Classic 1.12 / TBC 2.4.3: no trailing byte (16 bytes)

The static BuyItemPacket::build() helper always adds the byte (AzerothCore compat).
GameHandler::buyItem() now gates the byte based on active expansion, allowing
Classic/TBC servers to receive correctly-sized packets.
2026-03-11 04:49:18 -07:00
Kelsi
ed48a3c425 fix: replace fragile heuristic in SMSG_INITIAL_SPELLS with explicit Classic format flag
Classic 1.12 uses uint16 spellId + uint16 slot (4 bytes/spell); TBC and WotLK
use uint32 spellId + uint16 unknown (6 bytes/spell). The old size-based heuristic
could misdetect TBC packets that happened to fit both layouts. Add a vanillaFormat
parameter to InitialSpellsParser::parse and override parseInitialSpells in
ClassicPacketParsers to always pass true, eliminating the ambiguity.
2026-03-11 04:38:30 -07:00
Kelsi
9d0da6242d fix: correct Classic/TBC MSG_MOVE_TELEPORT_ACK movement info parsing
Classic 1.12 and TBC 2.4.3 movement packets omit the moveFlags2 (uint16)
field present in WotLK 3.3.5a. The prior handler unconditionally read 2 bytes
for moveFlags2, shifting the timestamp and position reads by 2 bytes and
producing garbage coordinates after a teleport. Now gated by expansion.
2026-03-11 04:32:00 -07:00
Kelsi
d3241dce9e fix: handle Classic 1.12 SMSG_WEATHER missing isAbrupt byte
Classic 1.12 sends weatherType(4)+intensity(4) with no trailing isAbrupt byte;
TBC/WotLK append uint8 isAbrupt. The prior check required >= 9 bytes, so weather
never updated on Classic servers. Now accept >= 8 bytes and read isAbrupt only if
the byte is present.
2026-03-11 04:25:00 -07:00
Kelsi
fed03f970c fix: correct SMSG_BATTLEFIELD_STATUS Classic 1.12 packet layout
Classic uses queueSlot(4)+bgTypeId(4)+unk(2)+instanceId(4)+isReg(1)+statusId(4);
TBC/WotLK prefixes arenaType(1)+unk(1) before bgTypeId. Reading TBC format on
Classic caused bgTypeId to be read from wrong offset, corrupting BG queue state.
2026-03-11 04:22:18 -07:00
Kelsi
8493729a10 fix: use uint16 spellId in Classic 1.12 SMSG_LEARNED/REMOVED/SUPERCEDED_SPELL
Classic 1.12 (vmangos/cmangos) sends uint16 spellIds in SMSG_LEARNED_SPELL,
SMSG_REMOVED_SPELL, and SMSG_SUPERCEDED_SPELL. TBC 2.4.3 and WotLK 3.3.5a
use uint32. The handlers were unconditionally reading uint32, causing the
first byte of the next field to be consumed as part of the spellId on
Classic, producing garbage spell IDs and breaking known-spell tracking.

Apply the same Classic/TBC+WotLK gate used by the SMSG_INITIAL_SPELLS
heuristic: read uint16 for Classic, uint32 for all others.
2026-03-11 04:08:16 -07:00
Kelsi
750b270502 fix: use expansion-aware item size in LootResponseParser for Classic/TBC
The previous per-iteration heuristic (remaining >= 22 → 22 bytes, >= 14 → 14 bytes)
incorrectly parsed Classic/TBC multi-item loots: 2+ items × 14 bytes would
trigger the 22-byte WotLK path for the first item, corrupting subsequent items.

Classic 1.12 and TBC 2.4.3 use 14 bytes/item (slot+itemId+count+displayInfo+slotType).
WotLK 3.3.5a uses 22 bytes/item (adds randomSuffix+randomPropertyId).

Add isWotlkFormat bool parameter to LootResponseParser::parse and pass
isActiveExpansion('wotlk') from handleLootResponse.
2026-03-11 04:01:07 -07:00
Kelsi
dd7d74cb93 fix: correct SMSG_SPELL_FAILURE Classic format and result enum shift
Classic 1.12 SMSG_SPELL_FAILURE omits the castCount byte that TBC/WotLK
include (format: uint64 GUID + uint32 spellId + uint8 failReason).
The previous code read a castCount for all expansions, misaligning
spellId and failReason for Classic by one byte.

Also apply the same +1 enum shift used in parseCastFailed/parseCastResult:
Classic result 0=AFFECTING_COMBAT maps to WotLK 1=AFFECTING_COMBAT,
so Classic failReason=0 now correctly shows an error instead of being
silently swallowed.
2026-03-11 03:54:33 -07:00
Kelsi
d6e398d814 fix: add Classic parseCastResult override with result enum +1 shift
Classic 1.12 SMSG_CAST_RESULT uses an enum starting at 0=AFFECTING_COMBAT
(no SUCCESS entry), while WotLK starts at 0=SUCCESS, 1=AFFECTING_COMBAT.
Without this override, Classic result codes were handled by TBC's
parseCastResult which passed them unshifted, causing result 0
(AFFECTING_COMBAT) to be silently treated as success with no error shown.

This applies the same +1 shift used in parseCastFailed so all Classic
spell failure codes map correctly to getSpellCastResultString.
2026-03-11 03:53:18 -07:00
Kelsi
2f0809b570 fix: correct TBC aura entry minimum-size guard from 13 to 15 bytes
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
Each SMSG_INIT/SET_EXTRA_AURA_INFO entry is 15 bytes:
  uint8 slot(1) + uint32 spellId(4) + uint8 effectIndex(1)
  + uint8 flags(1) + uint32 durationMs(4) + uint32 maxDurMs(4) = 15

The previous guard of 13 would allow the loop to start reading a
partial entry, silently returning zeroes for durationMs/maxDurMs
when 13-14 bytes remained in the packet.
2026-03-11 03:49:54 -07:00
Kelsi
144c87a72f feat: show spell failure reason in chat from SMSG_SPELL_FAILURE
SMSG_SPELL_FAILURE carries a failReason byte (same enum as SMSG_CAST_RESULT)
that was previously ignored. Now parse castCount+spellId+failReason and
display the localized reason string for the player's interrupted casts
(e.g. 'Interrupted', 'Stunned', 'Can\'t do that while moving').
2026-03-11 03:42:41 -07:00
Kelsi
1446d4fddd fix: pass player power type to getSpellCastResultString for result 85
Result 85 is 'not enough power' — the message should say 'Not enough
rage', 'Not enough energy', 'Not enough runic power', etc. based on
the player's actual power type rather than always showing 'Not enough
mana'.
2026-03-11 03:41:49 -07:00
Kelsi
84a6ee4801 fix: surface absorb/resist in SMSG_ENVIRONMENTAL_DAMAGE_LOG (Classic/TBC)
The Classic/TBC variant handler was discarding the resisted field entirely
(only reading absorbed but discarding it). Now reads and shows both as
ABSORB/RESIST combat text, matching the WotLK SMSG_ENVIRONMENTALDAMAGELOG
fix from the previous commit.
2026-03-11 03:40:41 -07:00
Kelsi
00db93b7f2 fix: show RESIST (not MISS) for SMSG_PROCRESIST combat text
SMSG_PROCRESIST is sent when a proc effect is resisted. Show 'Resisted'
rather than 'Miss' to correctly communicate what happened to the player.
2026-03-11 03:38:39 -07:00
Kelsi
fb01361837 feat: show blocked amount and reduced damage on VICTIMSTATE_BLOCKS
When an attack is partially blocked, the server sends the remaining
damage in totalDamage and the blocked amount in data.blocked. Show
both: the damage taken and a 'Block N' entry. When block amount is
zero (full block with no damage), just show 'Block'.
2026-03-11 03:36:45 -07:00
Kelsi
d1c5e09127 fix: correct SMSG_PERIODICAURALOG packet format for WotLK 3.3.5a
WotLK adds an overkill(4) field between damage and school for aura type
3/89 (periodic damage), and adds absorbed(4)+isCrit(1) after overHeal
for aura type 8/124/45 (periodic heal). Without these fields the absorb
and resist values were reading out-of-alignment, producing garbage data.

Also surfaces the heal-absorbed amount as ABSORB combat text (e.g. when
a HoT tick is partially absorbed by Vampiric Embrace counter-healing).
2026-03-11 03:34:27 -07:00
Kelsi
f50cb04887 feat: surface absorb/resist from SMSG_ENVIRONMENTALDAMAGELOG
Environmental damage (drowning, lava, fire) also carries absorb/resist
fields. Show these as ABSORB/RESIST combat text so players see the full
picture of incoming environmental hits, consistent with spell/melee.
2026-03-11 03:31:33 -07:00
Kelsi
031448ec6d feat: show absorb/resist on periodic damage (DoT) ticks
SMSG_PERIODICAURALOG already parsed abs/res fields for type 3/89 but
discarded them. Surface these as ABSORB/RESIST combat text so players
see when DoT ticks are being partially absorbed (e.g. vs. PW:Shield).
2026-03-11 03:30:24 -07:00
Kelsi
dfc78572f5 feat: show melee absorb/resist in combat text from SMSG_ATTACKERSTATEUPDATE
Sub-damage entries carry absorbed/resisted per school. Accumulate these
and emit ABSORB/RESIST combat text alongside the hit damage when nonzero,
matching the behavior just added for SMSG_SPELLNONMELEEDAMAGELOG.
2026-03-11 03:29:37 -07:00
Kelsi
d2ae4d8215 feat: show partial absorb/resist amounts in spell combat text
handleSpellDamageLog now emits ABSORB/RESIST entries when data.absorbed
or data.resisted are nonzero, so players see 'Absorbed 123' alongside
damage numbers (e.g. vs. Power Word: Shield or Ice Barrier).
handleSpellHealLog does the same for heal absorbs (e.g. Vampiric Embrace
counter-absorbs). renderCombatText now formats amount when nonzero.
2026-03-11 03:28:19 -07:00
Kelsi
e902375763 feat: add ABSORB and RESIST combat text types for spell misses
Adds dedicated CombatTextEntry::Type entries for ABSORB (miss type 7)
and RESIST (miss type 8), replacing the generic MISS display. Updates
missTypes arrays in SMSG_SPELLLOGMISS and SMSG_SPELL_GO, and adds
light-blue "Absorb" and grey "Resist" rendering in the combat text overlay.
2026-03-11 03:23:01 -07:00
Kelsi
d5196abaec fix: show IMMUNE text for miss type 5 in SMSG_SPELL_GO and SMSG_SPELLLOGMISS
IMMUNE misses (spell miss type 5) were shown as generic MISS text in both
spell cast feedback handlers. Now consistently shows IMMUNE combat text
to match the fix already applied to SMSG_ATTACKERSTATEUPDATE.
2026-03-11 03:13:14 -07:00
Kelsi
35683920ff fix: handle EVADE/IMMUNE/DEFLECT victimStates in melee combat text
SMSG_ATTACKERSTATEUPDATE victimState values 5 (EVADE), 6 (IMMUNE), and
7 (DEFLECT) were previously falling through to the damage display path,
showing incorrect damage numbers instead of the proper miss/immune feedback.
Now correctly shows MISS for evade/deflect and IMMUNE for immune hits.
2026-03-11 03:09:39 -07:00
Kelsi
1646bef1c2 fix: add size guards to spell learn/remove handlers and implement SMSG_SPELLSTEALLOG
handleLearnedSpell, handleRemovedSpell, handleSupercededSpell, and
handleUnlearnSpells all lacked size checks before reading packet fields.
Also implements SMSG_SPELLSTEALLOG (previously silently consumed) with
proper player feedback showing the stolen spell name when the local player
is the caster, matching the same expansion-conditional packed-guid format
as SPELLDISPELLOG.
2026-03-11 03:03:44 -07:00
Kelsi
ae6c2aa056 fix: correct SMSG_SPELLDISPELLOG entry size from 8 to 5 bytes
Each dispelled spell entry is uint32(spellId) + uint8(isPositive) = 5 bytes,
not uint32 + uint32 = 8 bytes as the loop previously assumed.

The incorrect stride caused the second and subsequent entries to be read at
wrong offsets, potentially showing the wrong spell name for multi-dispels.
2026-03-11 02:57:05 -07:00
Kelsi
603e52e5b0 fix: add size check and skip WotLK guid suffix in handleCooldownEvent
SMSG_COOLDOWN_EVENT in WotLK appends an 8-byte unit guid after the spellId.
The handler was reading without a size check and not consuming the trailing
guid, which could misalign subsequent reads.
2026-03-11 02:51:58 -07:00
Kelsi
0a17683545 fix: correct WotLK packed guid format in SMSG_PROCRESIST and SMSG_TOTEM_CREATED
Both opcodes use packed GUIDs in WotLK 3.3.5a but were reading full uint64,
causing incorrect GUID parsing and potentially matching wrong player entities.

SMSG_PROCRESIST: caster + victim guids (packed in WotLK, uint64 in TBC/Classic)
SMSG_TOTEM_CREATED: totem guid (packed in WotLK, uint64 in TBC/Classic)
2026-03-11 02:50:53 -07:00
Kelsi
9cd7e7978d fix: correct SMSG_DISPEL_FAILED packet format and improve message
WotLK sends spellId(4) + packed_guid caster + packed_guid victim, while
TBC/Classic sends full uint64 caster + uint64 victim + spellId(4).
The previous handler assumed TBC format unconditionally, causing incorrect
reads in WotLK mode.

Also use the spell name cache to display "Purge failed to dispel." rather
than a raw "Dispel failed! (spell N)" message.
2026-03-11 02:49:37 -07:00
Kelsi
1f4880985b fix: correct SMSG_SPELLLOGMISS packet format for all expansions
All expansions send spellId(4) before the caster guid — the previous
handler was missing this field entirely, causing the caster guid read
to consume spellId bytes and corrupt all subsequent parsing.

Additionally, in WotLK mode, victim guids inside the per-miss loop are
packed guids (not full uint64), matching the caster guid format.

Also handle the REFLECT (missInfo=11) extra payload in WotLK: the server
appends reflectSpellId(4) + reflectResult(1) for reflected spells, which
previously caused the following loop entries to be mis-parsed.
2026-03-11 02:47:15 -07:00
Kelsi
d696da9227 fix: also use school mask for pre-cast melee range/facing check in castSpell()
Same DBC-driven physical school detection replaces the brittle hardcoded
warrior spell list in the pre-cast range check, so rogue, DK, paladin,
feral druid, and hunter melee abilities get correct range/facing enforcement.
2026-03-11 02:40:27 -07:00
Kelsi
21c55ad6b4 fix: detect melee abilities via spell school mask instead of hardcoded spell ID list
Replace the brittle warrior-only hardcoded spell ID list for melee ability
detection with a DBC-driven check: physical school mask (1) from spellNameCache_
covers warrior, rogue, DK, paladin, feral druid, and all other physical-school
instant abilities generically. Instant detection: spellId != currentCastSpellId.
2026-03-11 02:39:25 -07:00
Kelsi
643d48ee89 fix: show reason-specific messages for SMSG_TRANSFER_ABORTED
Replace generic 'Transfer aborted' message with WotLK TRANSFER_ABORT_*
reason codes: difficulty, expansion required, instance full, too many
instances, zone in combat, etc.
2026-03-11 02:36:55 -07:00
Kelsi
3082df2ac0 fix: use packed guids in SMSG_SPELLDAMAGESHIELD for WotLK and read absorbed field
WotLK 3.3.5a format uses packed guids (not full uint64) for victim and caster,
and adds an absorbed(4) field before schoolMask. Classic/TBC use full uint64 guids.
Previously the handler always read full uint64 guids, causing misparse on WotLK
(e.g. Thorns and Shield Spike damage shield combat text was garbled/wrong).
2026-03-11 02:27:57 -07:00
Kelsi
c4b2089d31 fix: handle OBS_MOD_POWER and PERIODIC_ENERGIZE aura types in SMSG_PERIODICAURALOG
Add PERIODIC_ENERGIZE (91) and OBS_MOD_POWER (46) handling so mana/energy/rage
restore ticks from common WotLK auras (Replenishment, Mana Spring Totem, Divine
Plea, etc.) appear as ENERGIZE in floating combat text. Also handle PERIODIC_MANA_LEECH
(98) to properly consume its 12 bytes instead of halting mid-event parse.
2026-03-11 02:25:42 -07:00
Kelsi
a67feb6d93 Fix handleInstanceDifficulty to handle variable-length packet formats
MSG_SET_DUNGEON_DIFFICULTY sends 4 or 12 bytes (difficulty + optional
isInGroup + savedBool) while SMSG_INSTANCE_DIFFICULTY sends 8 bytes
(difficulty + heroic).  The previous guard of < 8 caused the handler
to silently return for 4-byte variants, leaving instanceDifficulty_
unchanged.  Now reads as much as available and infers heroic flag from
the field count.
2026-03-11 02:08:38 -07:00
Kelsi
7c77c4a81e Fix per-frame particle descriptor set leak in M2 renderer
Pre-allocate one stable VkDescriptorSet per particle emitter at model
upload time (particleTexSets[]) instead of allocating a new set from
materialDescPool_ every frame for each particle group.  The per-frame
path exhausted the 8192-set pool in ~14 s at 60 fps with 10 active
particle emitters, causing GPU device-lost crashes.  The old path is
kept as an explicit fallback but should never be reached in practice.
2026-03-11 02:01:23 -07:00
Kelsi
570465f51a fix: handle MSG_SET_DUNGEON_DIFFICULTY and suppress SMSG_LEARNED_DANCE_MOVES warnings 2026-03-11 01:48:18 -07:00
Kelsi
f043077746 fix: free WMO and M2 material descriptor sets on group/model destroy to prevent pool exhaustion 2026-03-11 01:44:12 -07:00
Kelsi
4393798409 fix: parse WotLK areaId field in SMSG_INIT_WORLD_STATES to fix truncation warning 2026-03-11 01:40:33 -07:00
Kelsi
ce36171000 fix: prefer variationIndex=0 run animation and silence spurious compositeWithRegions warns
- character_renderer: playAnimation now prefers the primary variation
  (variationIndex==0) when multiple sequences share the same animation ID;
  this fixes hitching on human female run where a variation sequence was
  selected first before the base cycle
- character_renderer: move the compositeWithRegions size-mismatch warning
  inside the else branch so it only fires when sizes genuinely don't match,
  not for every successful 1:1 or scaled blit
2026-03-11 01:35:37 -07:00
Kelsi
f462db6bfa fix: terrain descriptor pool leak, minimap quest markers, and item comparison UI
- terrain_renderer: add FREE_DESCRIPTOR_SET_BIT flag and vkFreeDescriptorSets
  in destroyChunkGPU so material descriptor sets are returned to the pool;
  prevents GPU device lost from pool exhaustion near populated areas
- game_screen: fix projectToMinimap to use the exact inverse of the minimap
  shader transform so quest objective markers appear at the correct position
  and orientation regardless of camera bearing
- inventory_screen: fix item comparison tooltip to not compare equipped items
  against themselves (character screen); add item level diff line; show (=)
  indicator when stats are equal rather than bare value which looked identical
  to the item's own tooltip
2026-03-11 01:29:56 -07:00
Kelsi
568c566e1a fix: correct quest offer reward parser and trade slot trail size
- QuestOfferRewardParser: replace 4-variant heuristic with 0..16 byte
  prefix scan × fixed/variable arrays (34 candidates total).  AzerothCore
  WotLK 3.3.5a sends uint32 autoFinish + uint32 suggestedPlayers = 8 bytes
  before emoteCount; old uint8 read caused 3-byte misalignment, producing
  wrong item IDs and missing icons on quest reward windows.  Scoring now
  strongly favours the 8-byte prefix and exact byte consumption.
- Quest reward tooltip: delegate to InventoryScreen::renderItemTooltip()
  for full stats (armor, DPS, stats, bind type, etc.); show "Loading…"
  while item data is still fetching instead of showing nothing.
- SMSG_TRADE_STATUS_EXTENDED: fix SLOT_TRAIL 49→52 bytes.  AC 3.3.5a
  sends giftCreatorGuid(8) + 6 enchant slots(24) + randPropId(4) +
  suffixFactor(4) + durability(4) + maxDurability(4) + createPlayedTime(4)
  = 52 bytes after isWrapped; wrong skip misaligned all subsequent slots.
2026-03-11 01:00:08 -07:00
Kelsi
170ff1597c fix: prefetch item info for trade slot items in SMSG_TRADE_STATUS_EXTENDED
After parsing the peer's trade window state, query item info for all
occupied slots so item names display immediately rather than showing
'Item 12345' until the cache is populated on the next frame.
2026-03-11 00:46:11 -07:00
Kelsi
06facc0060 feat: implement trade window UI with item slots and gold offering
Previously trade only showed an accept/decline popup with no way to
actually offer items or gold. This commit adds the complete trade flow:

Packets:
- CMSG_SET_TRADE_ITEM (tradeSlot, bag, bagSlot) — add item to slot
- CMSG_CLEAR_TRADE_ITEM (tradeSlot) — remove item from slot
- CMSG_SET_TRADE_GOLD (uint64 copper) — set gold offered
- CMSG_UNACCEPT_TRADE — unaccept without cancelling
- SMSG_TRADE_STATUS_EXTENDED parser — updates trade slot/gold state

State:
- TradeSlot struct: itemId, displayId, stackCount, bag, bagSlot
- myTradeSlots_/peerTradeSlots_ arrays (6 slots each)
- myTradeGold_/peerTradeGold_ (copper)
- resetTradeState() helper clears all state on cancel/complete/close

UI (renderTradeWindow):
- Two-column layout: my offer | peer offer
- Each column shows 6 item slots with item names
- Double-click own slot to remove; right-click empty slot to open
  backpack picker popup
- Gold input field (copper, Enter to set)
- Accept Trade / Cancel buttons
- Window close button triggers cancel trade
2026-03-11 00:44:07 -07:00
Kelsi
7c5d688c00 fix: show area name in SMSG_ZONE_UNDER_ATTACK system message
Replace the raw area ID in the zone-under-attack message with the
resolved area name from the zone manager, matching retail WoW behavior
('Hillsbrad Foothills is under attack!' instead of 'area 267').
2026-03-11 00:36:40 -07:00
Kelsi
eaf827668a fix: parse SMSG_QUESTUPDATE_ADD_PVP_KILL and update quest log kill counts
Previously only displayed a chat message without updating the quest
tracker. Now parses the full packet (guid+questId+count+reqCount),
stores progress under entry-key 0 in killCounts, and shows a progress
message matching the format used for creature kills.

Handles both WotLK (4-field) and Classic (3-field, no reqCount) variants
with fallback to the existing killCounts or killObjectives for reqCount.
2026-03-11 00:34:23 -07:00
Kelsi
77ce54833a feat: add quest kill objective indicator (⚔) to unit nameplates
Yellow crossed-swords icon appears to the right of the unit name when
the creature's entry is an incomplete kill objective in a tracked quest.
Updated icon is suppressed once the kill count is satisfied.

Uses unit->getEntry() (Unit subclass method) rather than the base
Entity pointer, matching how questKillEntries keys are stored.
2026-03-11 00:29:35 -07:00
Kelsi
72a16a2427 fix: clear gossip/quest POI markers on map change (SMSG_NEW_WORLD)
Quest POI markers are map-specific. Clearing gossipPois_ on world entry
prevents stale markers from previous maps being displayed on the new map.
Quest POIs will be re-fetched as the quest log re-queries on the new map.
2026-03-11 00:24:35 -07:00