- 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)."
- 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
- 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)
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.
- 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
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.
- 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
- 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
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.
- 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
- 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
- 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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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)
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.
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.
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.
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.
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
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.
- canReclaimCorpse() and getCorpseDistance() compared canonical movementInfo
(x=north=server_y, y=west=server_x) against raw server corpseX_/Y_ causing
the proximity check to always report wrong distance even when standing on corpse
- Fix: use corpseY_ for canonical north and corpseX_ for canonical west
- Also detect OBJECT_TYPE_CORPSE update blocks owned by the player to set
corpse coordinates at login-as-ghost (before SMSG_DEATH_RELEASE_LOC arrives)
TalentsInfoParser used a completely wrong byte layout (expected big-endian
counts, wrong field order), causing unspentTalentPoints to always be misread.
This made canLearn always false so clicking talents did nothing.
New format matches the actual WoW 3.3.5a wire format:
uint8 talentType, uint32 unspentTalents, uint8 groupCount, uint8 activeGroup,
per-group: uint8 talentCount, [uint32 id + uint8 rank]×N, uint8 glyphCount, [uint16]×M
Matches the proven parsing logic in handleInspectResults.
Implements SMSG_SET_FLAT_SPELL_MODIFIER and SMSG_SET_PCT_SPELL_MODIFIER
(previously consumed silently). Parses per-group (uint8 groupIndex, uint8
SpellModOp, int32 value) tuples sent by the server after login and talent
changes, and stores them in spellFlatMods_/spellPctMods_ maps keyed by
(SpellModOp, groupIndex).
Exposes getSpellFlatMod(op)/getSpellPctMod(op) accessors and a static
applySpellMod() helper. Clears both maps on character login alongside
spellCooldowns. Surfaces talent-modified mana cost and cast time in the
spellbook tooltip via SpellModOp::Cost and SpellModOp::CastingTime lookups.
Previously the arena path in handlePvpLogData consumed the packet and
returned early with no data. Now the two-team header is parsed (rating
change, new rating, team name), followed by the same player list and
winner fields as battlegrounds.
The BgScoreboardData struct gains ArenaTeamScore fields (teamName,
ratingChange, newRating) populated when isArena=true.
The BG scoreboard UI is updated to:
- Use "Arena Score" window title for arenas
- Show each team's name and rating delta at the top
- Identify the winner by team name instead of faction label
- Parse SMSG_SET_FACTION_ATWAR (uint32 repListId + uint8 set) to track
per-faction at-war flags in initialFactions_ flags byte
- Parse SMSG_SET_FACTION_VISIBLE (uint32 repListId + uint8 visible) to
track faction visibility changes from the server
- Add FACTION_FLAG_* constants (VISIBLE, AT_WAR, HIDDEN, etc.) to GameHandler
- Build repListId <-> factionId bidirectional maps when loading Faction.dbc
(ReputationListID field 1); used to correlate flag packets with standings
- Fix Faction.dbc field layout comment: field 1=ReputationListID, field 23=Name
(was incorrectly documented as field 22 with no ReputationListID field)
- Add isFactionAtWar(), isFactionVisible(), getFactionIdByRepListId(),
getRepListIdByFactionId() accessors on GameHandler
- Reputation panel now shows watched faction at top, highlights at-war
factions in red with "(At War)" label, and marks tracked faction in gold
When the player inspects another player on WotLK 3.3.5a, also send
CMSG_QUERY_INSPECT_ACHIEVEMENTS so the server responds with
SMSG_RESPOND_INSPECT_ACHIEVEMENTS. The new handler parses the
achievement-id/date sentinel-terminated block (same layout as
SMSG_ALL_ACHIEVEMENT_DATA but prefixed with a packed guid) and stores
the earned achievement IDs keyed by GUID in
inspectedPlayerAchievements_. The new public getter
getInspectedPlayerAchievements(guid) exposes this data for the inspect
UI. The cache is cleared on world entry to prevent stale data.
QueryInspectAchievementsPacket::build() handles the CMSG wire format
(uint64 guid + uint8 unk=0).
- Effect 10 (POWER_DRAIN): show PERIODIC_DAMAGE text on victim, ENERGIZE on caster;
handles Drain Mana, Viper Sting, Fel Drain, etc.
- Effect 11 (HEALTH_LEECH): show SPELL_DAMAGE on victim, HEAL on caster;
handles Drain Life, Death Coil, etc.
- Effect 24/114 (CREATE_ITEM/CREATE_ITEM2): existing profession crafting feedback
extended to also cover CREATE_ITEM2 (engineering/enchanting recipes using alt effect)
- Effect 26 (INTERRUPT_CAST): clear the interrupted unit's cast bar from unitCastStates_
so the cast bar dismisses immediately rather than waiting for the next update packet
- Effect 49 (FEED_PET): show "You feed your pet <item>." message for hunter pet feeding
All effects are expansion-aware: TBC/Classic use full uint64 GUIDs, WotLK uses packed GUIDs.
- Implement SMSG_SPELLLOGEXECUTE handler with expansion-aware caster GUID reading
(packed_guid for WotLK/Classic, full uint64 for TBC)
- Parse effect type 24 (SPELL_EFFECT_CREATE_ITEM): show "You create <item> using
<spell>." in chat when the player uses a profession or any create-item spell
- Look up item name via ensureItemInfo/getItemInfo and spell name via spellNameCache_
- Fall back to "You create: <item>." when the spell name is not cached
- Safely consume unknown effect types by stopping parse at first unrecognized effect
to avoid packet misalignment on variable-length sub-records
- Adds visible crafting feedback complementary to SMSG_ITEM_PUSH_RESULT (which shows
"Received:" for looted/obtained items) with a profession-specific "create" message
- Fix bug where SMSG_REDIRECT_CLIENT, SMSG_PVP_QUEUE_STATS, SMSG_PLAYER_SKINNED, etc.
were incorrectly falling through to handleQuestPoiQueryResponse instead of being
silently consumed; add separate setReadPos break for those opcodes
- Implement SMSG_SERVERTIME: sync gameTime_ from server's unix timestamp
- Implement SMSG_KICK_REASON: show player a chat message with reason for group removal
- Implement SMSG_GROUPACTION_THROTTLED: notify player of rate-limit with wait time
- Implement SMSG_GMRESPONSE_RECEIVED: display GM ticket response in chat and UI error
- Implement SMSG_GMRESPONSE_STATUS_UPDATE: show ticket status changes in chat
- Silence voice chat, dance, commentator, and debug/cheat opcodes with explicit consume
cases rather than falling to the unhandled-opcode warning log