Add UNIT_FIELD_BYTES_1 to all expansion update field tables (Classic=133,
TBC/WotLK=137). Byte 3 of this field contains the shapeshift form ID
(Bear=1, Cat=3, Travel=4, Moonkin=31, Tree=36, Battle Stance=17, etc.).
Track form changes in the VALUES update handler and fire
UPDATE_SHAPESHIFT_FORM + UPDATE_SHAPESHIFT_FORMS events when the
form changes. This enables stance bar addons and druid form tracking.
New Lua functions:
- GetShapeshiftForm() — returns current form ID (0 = no form)
- GetNumShapeshiftForms() — returns form count by class (Warrior=3,
Druid=6, DK=3, Rogue=1, Priest=1, Paladin=3)
GetEnchantInfo(enchantId) looks up the enchantment name from
SpellItemEnchantment.dbc (field 14). Returns the display name
like "Crusader", "+22 Intellect", or "Mongoose" for a given
enchant ID.
Used by equipment comparison addons and tooltip addons to display
enchantment names on equipped gear. The enchant ID comes from the
item's ITEM_FIELD_ENCHANTMENT update field.
Also adds getEnchantName() to GameHandler for C++ access.
Spell descriptions from DBC contain raw template variables like
\$s1, \$d, \$o1 that refer to effect values resolved at runtime.
Without DBC effect data loaded, these showed as literal "\$s1" in
tooltips, making descriptions hard to read.
Now strips template variables and replaces with readable placeholders:
- \$s1/\$s2/\$s3 → "X" (effect base points)
- \$d → "X sec" (duration)
- \$o1 → "X" (periodic total)
- \$a1 → "X" (radius)
- \$\$ → "$" (literal dollar sign)
- \${...} blocks → stripped
Result: "Hurls a fiery ball that causes X Fire Damage" instead of
"Hurls a fiery ball that causes \$s1 Fire Damage". Not as informative
as real values, but significantly more readable.
Implement the WoW Mixin pattern used by modern addons:
- Mixin(obj, ...) — copies fields from mixin tables into obj
- CreateFromMixins(...) — creates a new table from mixin templates
- CreateAndInitFromMixin(mixin, ...) — creates and calls Init()
- MergeTable(dest, src) — shallow-merge src into dest
These enable OOP-style addon architecture used by LibSharedMedia,
WeakAuras, and many Ace3-based addons for class/object creation.
strgfind = string.gmatch alias (deprecated WoW function used by
older addons that haven't migrated to string.gmatch).
tostringall(...) converts all arguments to strings and returns
them. Used by chat formatting and debug addons that need to safely
stringify mixed-type argument lists.
SpellStopCasting() cancels the current cast via cancelCast(). Used by
macro addons and cast-cancel logic (e.g., /stopcasting macro command).
UnitFullName/GetUnitName aliases for UnitName — some addons use these
variant names.
SpellIsTargeting() returns false (no AoE targeting reticle in this
client). SpellStopTargeting() is a no-op stub. Both prevent errors
in addons that check targeting state.
GetClassColor(className) returns r, g, b, colorString from the
RAID_CLASS_COLORS table. Used by unit frame addons, chat addons,
and party/raid frames to color player names by class.
QuestDifficultyColors table provides standard quest difficulty
color mappings (impossible=red, verydifficult=orange, difficult=yellow,
standard=green, trivial=gray, header=gold). Used by quest log and
quest tracker addons for level-appropriate coloring.
GetMaxPlayerLevel() returns the level cap for the active expansion:
60 (Classic/Turtle), 70 (TBC), 80 (WotLK). Used by XP bar addons
and leveling trackers.
GetAccountExpansionLevel() returns the expansion tier: 1 (Classic),
2 (TBC), 3 (WotLK). Used by addons that adapt features based on
which expansion is active.
Both read from the ExpansionRegistry's active profile at runtime.
CURRENT_SPELL_CAST_CHANGED fires when the player starts a new cast
via handleSpellStart. Some addons register for this as a catch-all
signal that the current spell state changed, complementing the more
specific UNIT_SPELLCAST_START/STOP/FAILED events.
PLAYER_COMBO_POINTS now fires from the existing SMSG_UPDATE_COMBO_POINTS
handler — the handler already updated comboPoints_ but never notified
Lua addons. Rogue/druid combo point displays and DPS rotation addons
register for this event.
LOOT_READY fires alongside LOOT_OPENED when a loot window opens. Some
addons register for this WoW 5.x+ event name instead of LOOT_OPENED.
The TBC item query parser left subclassName empty, so TBC items showed
no weapon/armor type in tooltips or the character sheet (e.g., "Sword",
"Plate", "Shield" were all blank). The Classic and WotLK parsers
correctly map subClass IDs to names.
Fix: call getItemSubclassName() in the TBC parser, same as WotLK.
Expose getItemSubclassName() in the header (was static, now shared
across parser files).
Items with maxCount=1 now show "Unique" in white text below the name.
Items with the Heroic flag (0x8) show "Heroic" in green text. Both
display before the bind type line, matching WoW's tooltip order.
Heroic items (from heroic dungeon/raid drops) are visually
distinguished from their normal-mode counterparts. Unique items
(trinkets, quest items, etc.) show the carry limit clearly.
Item tooltips now display spell effects in green text:
- "Use: Restores 2200 health over 30 sec" (trigger 0)
- "Equip: Increases attack power by 120" (trigger 1)
- "Chance on hit: Strikes the enemy for 95 Nature damage" (trigger 2)
Passes up to 5 item spell entries through _GetItemTooltipData with
spellId, trigger type, spell name, and spell description from DBC.
The tooltip builder maps trigger IDs to "Use: ", "Equip: ", or
"Chance on hit: " prefixes.
This completes the item tooltip with all major WoW tooltip sections:
quality name, bind type, equip slot/type, armor, damage/DPS/speed,
primary stats, combat ratings, resistances, spell effects, gem sockets,
required level, flavor text, and sell price.
Spell tooltips now show the spell description text (e.g., "Hurls a
fiery ball that causes 565 to 655 Fire Damage") in gold/yellow text
between the cast info and cooldown display.
New GetSpellDescription(spellId) C function exposes the description
field from SpellNameEntry (loaded from Spell.dbc via the spell name
cache). Descriptions contain the raw DBC text which may include
template variables ($s1, $d, etc.) — these show as-is until template
substitution is implemented.
Add gem socket display to item tooltips — shows [Meta Socket],
[Red Socket], [Yellow Socket], [Blue Socket], or [Prismatic Socket]
based on socketColor mask from ItemQueryResponseData.
Also pass itemSetId through _GetItemTooltipData for addons that
track set bonuses.
Register power-type-specific aliases (UnitRage, UnitEnergy, UnitFocus,
UnitRunicPower) that map to the existing lua_UnitPower function. Some
Classic/TBC addons call these directly instead of the generic UnitPower.
All return the unit's current power value regardless of type — the
underlying function reads from the entity's power field.
Replace PlaySound no-op stub with a real implementation that maps
WoW sound IDs and names to the UiSoundManager methods:
By ID: 856/1115→button click, 840→quest activate, 841→quest complete,
862→bag open, 863→bag close, 888→level up
By name: IGMAINMENUOPTION→click, IGQUESTLISTOPEN→quest activate,
IGQUESTLISTCOMPLETE→quest complete, IGBACKPACKOPEN/CLOSE→bags,
LEVELUPSOUND→level up, TALENTSCREEN→character sheet
This gives addons audio feedback when they call PlaySound() — button
clicks, quest sounds, and other UI sounds now actually play instead
of being silently swallowed.
DISPLAY_SIZE_CHANGED fires when the window is resized via
SDL_WINDOWEVENT_RESIZED, allowing UI addons to adapt their layout
to the new screen dimensions (5 FrameXML registrations).
UNIT_QUEST_LOG_CHANGED("player") fires alongside QUEST_LOG_UPDATE
at all 6 quest log modification points, for addons that register
for this variant instead (4 FrameXML registrations).
The tab-based addon message detection was too aggressive — any chat
message containing a tab character was treated as an addon message
and silently dropped from regular chat display. This could suppress
legitimate player messages containing tabs (from copy-paste).
Now only matches as addon message when:
- Chat type is PARTY/RAID/GUILD/WHISPER/etc. (not SAY/YELL/EMOTE)
- Prefix before tab is <=16 chars (WoW addon prefix limit)
- Prefix contains no spaces (addon prefixes are identifiers)
This prevents false positives while still correctly detecting addon
messages formatted as "DBM4\ttimer:start:10".
UNIT_QUEST_LOG_CHANGED("player") now fires at all 6 locations where
QUEST_LOG_UPDATE fires — quest accept, complete, objective update,
abandon, and server-driven quest log changes. Some addons register
for this event instead of QUEST_LOG_UPDATE (4 registrations in
FrameXML). Both events are semantically equivalent for the player.
Detect addon messages in the SMSG_MESSAGECHAT handler by looking for
the 'prefix\ttext' format (tab delimiter). When detected, fire
CHAT_MSG_ADDON with args (prefix, message, channel, sender) instead
of the regular CHAT_MSG_* event, and suppress the raw message from
appearing in chat.
This enables inter-addon communication used by:
- Boss mods (DBM, BigWigs) for timer/alert synchronization
- Raid tools (oRA3) for ready checks and cooldown tracking
- Group coordination addons for pull countdowns and assignments
Works with the existing SendAddonMessage/RegisterAddonMessagePrefix
functions that format outgoing messages as 'prefix\ttext'.
Some addons register for SPELL_UPDATE_USABLE instead of
ACTIONBAR_UPDATE_USABLE to detect when spell usability changes
due to power fluctuations. Fire both events together when the
player's mana/rage/energy changes.
When the player's mana/rage/energy changes, action bar addons need
ACTIONBAR_UPDATE_USABLE to update button dimming (grey out abilities
the player can't afford). Now fires from both the SMSG_POWER_UPDATE
handler and the SMSG_UPDATE_OBJECT VALUES power field change path.
Without this event, action bar buttons showed as usable even when the
player ran out of mana — the usability state only refreshed on spell
cast attempts, not on power changes.
Implement core vendor query functions from ListInventoryData:
- GetMerchantNumItems() — count of items for sale
- GetMerchantItemInfo(index) — name, texture, price, stackCount,
numAvailable, isUsable for each vendor item
- GetMerchantItemLink(index) — quality-colored item link
- CanMerchantRepair() — whether vendor offers repair service
Enables auto-sell addons (AutoVendor, Scrap) to read vendor inventory
and check repair capability. Data sourced from SMSG_LIST_INVENTORY
via currentVendorItems + itemInfoCache for names/icons.
When a unit's power type changes (e.g., druid shifting from mana to
rage in bear form, or warrior switching stances), fire UNIT_DISPLAYPOWER
so unit frame addons can switch the power bar display (mana blue bar →
rage red bar). Detected via UNIT_FIELD_BYTES_0 byte 3 changes in the
VALUES update path.
RAID_ROSTER_UPDATE now fires alongside GROUP_ROSTER_UPDATE when the
group type is raid, matching the event that raid frame addons register
for (6 registrations in FrameXML). Fires from group list updates and
group uninvite handlers.
UNIT_LEVEL fires when any tracked unit (player, target, focus, pet)
changes level via VALUES update fields. Used by unit frame addons to
update level display (5 registrations in FrameXML).
SetAction's item branch and SetHyperlink's item/spell branches
showed only the item name, ignoring the full tooltip system we built.
SetAction item path now uses _WoweePopulateItemTooltip (shows armor,
stats, damage, bind type, sell price etc.).
SetHyperlink item path now uses _WoweePopulateItemTooltip; spell
path now uses SetSpellByID (shows cost, range, cast time, cooldown).
This means shift-clicking an item link in chat, hovering an item on
the action bar, or viewing any hyperlink tooltip now shows the full
stat breakdown instead of just the name.
SetSpellByID now shows comprehensive spell information:
- Mana/Rage/Energy/Runic Power cost
- Range in yards (or omitted for self-cast)
- Cast time ("1.5 sec cast" or "Instant")
- Active cooldown remaining in red
Uses existing GetSpellInfo (castTime, range from DBC), GetSpellPowerCost
(mana cost from DBC), and GetSpellCooldown (remaining CD) to populate
the tooltip with real spell data.
Item tooltips now display the vendor sell price at the bottom, formatted
as "Sell Price: 12g 50s 30c". Uses the vendorPrice field from GetItemInfo
(field 11, sourced from SMSG_ITEM_QUERY_SINGLE_RESPONSE sellPrice).
Helps players quickly assess item value when looting or sorting bags.
Extend item tooltips with secondary stats and resistances:
- Extra stats from ItemQueryResponseData.extraStats: Hit Rating,
Crit Rating, Haste Rating, Resilience, Attack Power, Spell Power,
Defense Rating, Dodge/Parry/Block Rating, Expertise, Armor Pen,
Mana/Health per 5 sec, Spell Penetration — all in green text
- Elemental resistances: Fire/Nature/Frost/Shadow/Arcane Resistance
Also passes extraStats as an array of {type, value} pairs and
resistance fields through _GetItemTooltipData for Lua consumption.
Stat type IDs follow the WoW ItemMod enum (3=Agi, 7=Sta, 31=Hit,
32=Crit, 36=Haste, 45=SpellPower, etc.).
Enhance _WoweePopulateItemTooltip to show complete item information:
- Bind type (Binds when picked up / equipped / used)
- Armor value for armor items
- Weapon damage range, speed, and DPS for weapons
- Primary stats (+Stamina, +Strength, +Agility, +Intellect, +Spirit)
in green text
- Required level
- Flavor/lore description text in gold
Backed by new _GetItemTooltipData(itemId) C function that returns a
Lua table with armor, bindType, damageMin/Max, speed, primary stats,
requiredLevel, and description from ItemQueryResponseData.
Replace minimal name-only item tooltips with proper WoW-style display:
- Quality-colored item name header
- Item level line for equipment (gold text)
- Equip slot and weapon/armor type on a double line
(e.g., "Head" / "Plate" or "One-Hand" / "Sword")
- Item class for non-equipment items
Shared _WoweePopulateItemTooltip() helper used by both
SetInventoryItem and SetBagItem for consistent tooltip formatting.
Maps INVTYPE_* strings to display names (Head, Chest, Two-Hand, etc.).
GetItemInfo returned empty strings for item class (field 6), subclass
(field 7), and equip slot (field 9). Now returns:
- Class: mapped from itemClass enum (Consumable, Weapon, Armor, etc.)
- Subclass: from parsed subclassName (Sword, Mace, Shield, etc.)
- EquipSlot: mapped from inventoryType to INVTYPE_ strings
(INVTYPE_HEAD, INVTYPE_CHEST, INVTYPE_WEAPON, etc.)
These fields are used by equipment comparison addons, character sheet
displays, and bag sorting addons to categorize and filter items.
Four independent static local steady_clock start times were used as
time origins in GetTime(), GetSpellCooldown(), UnitBuff expiration,
and UnitCastingInfo — each initializing on first call at slightly
different times. This created systematic timestamp mismatches.
When addons compute (start + duration - GetTime()), the start value
from GetSpellCooldown and the GetTime() return used different epochs,
causing cooldown sweeps and buff timers to appear offset.
Replace all four independent statics with a single file-scope
kLuaTimeEpoch constant and luaGetTimeNow() helper, ensuring all
time-returning Lua functions share exactly the same origin.
Implement GetBindingKey(command) and GetBindingAction(key) with
default action button mappings (ACTIONBUTTON1-12 → "1"-"9","0","-","=").
Action bar addons display bound keys on button tooltips via
GetBindingKey("ACTIONBUTTON"..slot).
Also add stubs for GetNumBindings, GetBinding, SetBinding, SaveBindings,
SetOverrideBindingClick, and ClearOverrideBindings to prevent nil-call
errors in FrameXML keybinding UI code (37 call sites).
setActionBarSlot (called from PickupAction/PlaceAction drag-drop and
from server-driven action button updates) updated the slot data and
notified the server, but never fired the Lua addon event. Action bar
addons (Bartender4, Dominos) register for ACTIONBAR_SLOT_CHANGED to
refresh button textures, tooltips, and cooldown state when slots change.
Also fires ACTIONBAR_UPDATE_STATE for general action bar refresh.
PLAYER_DEAD only fired from SMSG_FORCED_DEATH_UPDATE (GM kill) — the
normal death path (health dropping to 0 via VALUES update) never fired
it. Death-related addons and the default release spirit dialog depend
on this event.
Also add PLAYER_ALIVE (fires when resurrected without having been a
ghost) and PLAYER_UNGHOST (fires when player rezzes from ghost form)
at the health-restored-from-zero VALUES path. These events control
the transition from ghost form back to alive, letting addons restore
normal UI state after death.
handleTextEmote pushed emote messages directly to chatHistory
instead of using addLocalChatMessage, so Lua chat addons never
received CHAT_MSG_TEXT_EMOTE events for /wave, /dance, /bow, etc.
from other players. Use addLocalChatMessage which fires the event
and also notifies the C++ display callback.
GetSpellCooldown only returned per-spell cooldowns from the
spellCooldowns map, ignoring the Global Cooldown. Addons like OmniCC
and action bar addons rely on GetSpellCooldown returning GCD timing
when no individual spell cooldown is active — this is what drives the
cooldown sweep animation on action bar buttons after casting.
Now falls back to GCD timing (from getGCDRemaining/getGCDTotal) when
the spell has no individual cooldown but the GCD is active. Returns
proper (start, duration) values so addons can compute elapsed/remaining.
Same fix applied to GetActionCooldown for spell-type action bar slots.
Implement keyboard modifier queries using ImGui IO:
- IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
- IsModifiedClick(action) — CHATLINK=Shift, DRESSUP=Ctrl, SELFCAST=Alt
- GetModifiedClick/SetModifiedClick for keybind configuration
Fire UPDATE_EXHAUSTION and PLAYER_UPDATE_RESTING events when rest state
changes (entering/leaving inns and cities) and when rested XP updates.
XP bar addons use UPDATE_EXHAUSTION to show the rested bonus indicator.
Implement keyboard modifier state queries using ImGui IO state:
- IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown — direct key queries
- IsModifiedClick(action) — checks if the modifier matching a named
action is held (CHATLINK/SPLITSTACK=Shift, DRESSUP=Ctrl, SELFCAST=Alt)
- GetModifiedClick(action) — returns the assigned key name for an action
- SetModifiedClick — no-op stub for compatibility
These are fundamental input functions used by virtually all interactive
addons: shift-click to link items in chat, ctrl-click for dressup,
alt-click for self-cast, shift-click to split stacks, etc.
GetItemInfo returned item links with hardcoded white color (|cFFFFFFFF)
regardless of quality. Now uses quality-appropriate colors: gray for
Poor, white for Common, green for Uncommon, blue for Rare, purple for
Epic, orange for Legendary, gold for Artifact, cyan for Heirloom.
Also fix GameTooltip:GetItem() to use the quality-colored link from
GetItemInfo instead of hardcoded white.
GetRaidRosterInfo(index) returns name, rank, subgroup, level, class,
fileName, zone, online, isDead, role, isML — the core function raid
frame addons (Grid, Healbot, VuhDo) use to populate unit frame data.
Resolves class from UNIT_FIELD_BYTES_0 when entity is available.
GetThreatStatusColor(status) returns RGB for threat indicator coloring
(gray/yellow/orange/red). Used by unit frames and threat meters.
GetReadyCheckStatus(unit) stub returns nil (no check in progress).
RegisterUnitWatch/UnregisterUnitWatch stubs for secure frame compat.
GetItemQualityColor(quality) returns r, g, b, hexString for item
quality coloring (Poor=gray through Heirloom=cyan). Used by bag
addons, tooltips, and item frames to color item names/borders.
GetItemCount(itemId) counts total stacks across backpack + bags.
Used by addons to check material availability, quest item counts,
and consumable tracking.
UseContainerItem(bag, slot) uses/equips an item from a container
slot, delegating to useItemById for the actual equip/use action.
Fire UI_ERROR_MESSAGE from addUIError() so Lua addons can react to
error messages like "Not enough mana" or "Target is too far away".
Previously only the C++ overlay callback was notified. Also add
addUIInfoMessage() helper for informational system messages.
Fire QUEST_REMOVED and QUEST_LOG_UPDATE when quests are removed from
the quest log — both via server-driven removal (SMSG_QUEST_UPDATE_FAILED
etc.) and player-initiated abandon (CMSG_QUESTLOG_REMOVE_QUEST). Quest
tracking addons like Questie register for these events to update their
map markers and objective displays.
Classic and Turtle WoW don't use SMSG_AURA_UPDATE packets — they
pack aura data into UNIT_FIELD_AURAS update fields. The code correctly
rebuilds playerAuras from these fields in both CREATE_OBJECT and VALUES
update paths, but never fired the UNIT_AURA("player") addon event.
Buff frame addons (Buffalo, ElkBuffBars, etc.) register for UNIT_AURA
to refresh their display. Without this event, buff frames on Classic
and Turtle never update when buffs are gained or lost.
addLocalChatMessage only pushed to chatHistory and called the C++
display callback — it never fired Lua addon events. This meant the
player's own sent messages (local echoes from sendChatMessage) and
system messages (loot, XP gains, errors) were invisible to Lua chat
frame addons.
Now fires CHAT_MSG_{type} with the full 12-arg WoW signature from
addLocalChatMessage, matching the incoming message path. Uses the
active character name as sender for player-originated messages.
The SMSG_MESSAGECHAT handler stored messages in chatHistory and
triggered chat bubbles, but never fired Lua addon events. Chat frame
addons (Prat, Chatter, WIM) and the default ChatFrame all register for
CHAT_MSG_SAY, CHAT_MSG_WHISPER, CHAT_MSG_PARTY, CHAT_MSG_GUILD, etc.
to display incoming messages.
Now fires CHAT_MSG_{type} for every incoming message with the full WoW
event signature: message, senderName, language, channelName, displayName,
specialFlags, zoneChannelID, channelIndex, channelBaseName, unused,
lineID, and senderGUID. Covers all chat types: SAY, YELL, WHISPER,
PARTY, RAID, GUILD, OFFICER, CHANNEL, EMOTE, SYSTEM, MONSTER_SAY, etc.