Lava M2 models used independent static-local start times in pass 1
and pass 2 for UV scroll animation. Since static locals initialize
on first call, the two timers started at slightly different times
(microseconds to frames apart), causing a permanent UV offset mismatch
between passes — visible as texture flicker/jumping on lava surfaces.
Replace both function-scoped statics with a single file-scoped
kLavaAnimStart constant, ensuring both passes compute identical UV
offsets from the same epoch.
Implement the core map functions needed by WorldMapFrame.lua:
- SetMapToCurrentZone — sets map view from player's current mapId/zone
- GetCurrentMapContinent — returns continent (1=Kalimdor, 2=EK, etc.)
- GetCurrentMapZone — returns current zone ID
- SetMapZoom(continent, zone) — navigate map view
- GetMapContinents — returns continent name list
- GetMapZones(continent) — returns zone names per continent
- GetNumMapLandmarks — stub (returns 0)
Maps game mapId (0=EK, 1=Kalimdor, 530=Outland, 571=Northrend) to
WoW's continent numbering. Internal state tracks which continent/zone
the map UI is currently viewing.
Replace empty stub GameTooltip methods with working implementations:
- SetInventoryItem(unit, slot): populates tooltip with equipped item
name and quality-colored text via GetInventoryItemLink + GetItemInfo
- SetBagItem(bag, slot): populates from GetContainerItemInfo + GetItemInfo
- SetSpellByID(spellId): populates with spell name/rank from GetSpellInfo
- SetAction(slot): delegates to SetSpellByID or item lookup via GetActionInfo
- SetUnitBuff/SetUnitDebuff: populates from UnitBuff/UnitDebuff data
- SetHyperlink: parses item: and spell: links to populate name
- GetItem/GetSpell: now return real item/spell data when tooltip is populated
Also fix GetCVar/SetCVar conflict — remove Lua-side overrides that
were shadowing the C-side implementations (which return real screen
dimensions and sensible defaults for common CVars).
In the CREATE update block, the ufNpcFlags check at the end of the
else-if chain was unreachable dead code — it was already handled
earlier in the same chain. Remove the duplicate.
In the VALUES update block, mount display changes via field updates
fired mountCallback_ but not the UNIT_MODEL_CHANGED addon event,
unlike the CREATE path which fired both. Add the missing event so
Lua addons are notified when the player mounts/dismounts via VALUES
updates (the common case for aura-based mounting).
Also fire UNIT_MODEL_CHANGED for target/focus/pet display ID changes
in the VALUES displayIdChanged path, matching the CREATE path behavior.
Implement GetNumQuestLeaderBoards and GetQuestLogLeaderBoard — the core
functions WatchFrame.lua and QuestLogFrame.lua use to display objective
progress like "Kobold Vermin slain: 3/8" or "Linen Cloth: 2/6".
GetNumQuestLeaderBoards counts kill + item objectives from the parsed
SMSG_QUEST_QUERY_RESPONSE data. GetQuestLogLeaderBoard returns the
formatted progress text, type ("monster"/"item"/"object"), and
completion status for each objective.
Also adds ExpandQuestHeader/CollapseQuestHeader (no-ops for flat quest
list) and GetQuestLogSpecialItemInfo stub.
GetPlayerFacing() returns player orientation in radians, needed by
minimap addons for arrow rotation and facing-dependent mechanics.
GetCVar(name) returns sensible defaults for commonly queried CVars
(uiScale, screen dimensions, nameplate visibility, sound toggles,
autoLoot). SetCVar is a no-op stub for addon compatibility.
PLAYER_ENTERING_WORLD is the single most important WoW addon event —
virtually every addon registers for it to initialize UI, state, and
data structures. It was never fired, causing widespread addon init
failures on login and after teleports.
Now fired from:
- handleLoginVerifyWorld (initial login + same-map teleports)
- handleNewWorld (cross-map teleports, instance transitions)
Also fires:
- PLAYER_LOGIN on initial world entry only
- ZONE_CHANGED_NEW_AREA on all world entries
- UPDATE_WORLD_STATES on initial entry
- SPELLS_CHANGED + LEARNED_SPELL_IN_TAB after SMSG_INITIAL_SPELLS
(so spell book addons can initialize on login)
Implement GetNumSpellTabs, GetSpellTabInfo, GetSpellBookItemInfo, and
GetSpellBookItemName — the core functions SpellBookFrame.lua needs to
organize known spells into class skill line tabs.
Tabs are built lazily from knownSpells grouped by SkillLineAbility.dbc
mappings (category 7 = class). A "General" tab collects spells not in
any class skill line. Tabs auto-rebuild when the spell count changes.
Also adds SpellBookTab struct and getSpellBookTabs() to GameHandler.
Implement the quest tracking functions needed by WatchFrame.lua:
- SelectQuestLogEntry/GetQuestLogSelection — quest log selection state
- GetNumQuestWatches — count of tracked quests
- GetQuestIndexForWatch(watchIdx) — map Nth watched quest to log index
- AddQuestWatch/RemoveQuestWatch — toggle quest tracking by log index
- IsQuestWatched — check if a quest log entry is tracked
- GetQuestLink — generate colored quest link string
Backed by existing trackedQuestIds_ set and questLog_ vector.
Adds selectedQuestLogIndex_ state to GameHandler for quest selection.
Maps WoW equipment slot names (e.g. "HeadSlot", "MainHandSlot") to
inventory slot IDs, empty-slot textures, and relic check flags.
Supports case-insensitive matching with optional "Slot" suffix stripping.
Unblocks PaperDollFrame.lua and BankFrame.lua which call this function
to resolve slot button IDs during UI initialization.
PR #19 (572bb4ef) swapped CharSections.dbc field indices, placing
Texture1-3 at fields 4-6 and VariationIndex/ColorIndex at 8-9. Binary
analysis of the actual DBC files (Classic, TBC, Turtle — all identical
layout, no WotLK-specific override) confirms the correct order is:
Field 4 = VariationIndex
Field 5 = ColorIndex
Field 6 = Texture1 (string)
Field 7 = Texture2 (string)
Field 8 = Texture3 (string)
Field 9 = Flags
With the wrong indices, VariationIndex/ColorIndex reads returned string
offsets (garbage values that never matched), so all CharSections lookups
failed silently — producing white untextured character models at the
login screen and in-world.
Fixes all 4 expansion JSON layouts, hardcoded fallbacks in
character_preview.cpp, application.cpp, and character_create_screen.cpp.
Also handles the single-layer edge case (body skin only, no face/underwear)
by loading the texture directly instead of skipping compositing.
IsActionInRange(slot) checks if the spell on an action bar slot is within
range of the current target, using DBC spell range data and entity positions.
Returns 1/0/nil matching the WoW API contract.
GetActionInfo(slot) returns action type ("spell"/"item"/"macro"), id, and
subType for action bar interrogation by bar addons.
GetActionCount(slot) returns item stack count across backpack and bags for
consumable tracking on action bars.
Expose cast/channel state to Lua addons via UnitCastingInfo(unit) and
UnitChannelInfo(unit), matching the WoW API signature (name, text, texture,
startTime, endTime, isTradeSkill, castID, notInterruptible). Works for
player, target, focus, and pet units using existing UnitCastState tracking.
Also fix handleCastFailed (SMSG_CAST_FAILED, Classic/TBC path) to fire
UNIT_SPELLCAST_FAILED and UNIT_SPELLCAST_STOP events — previously only
the WotLK SMSG_CAST_RESULT path fired these, leaving Classic/TBC addons
unaware of cast failures.
Adds isChannel field to UnitCastState and getCastTimeTotal() accessor.
When logging in while already dead (reconnect/crash recovery), send
MSG_CORPSE_QUERY to get the server-authoritative corpse location.
Without this, the minimap corpse marker would be missing or point to
the wrong position after reconnecting as a ghost.
Parse MSG_CORPSE_QUERY server response to get the exact corpse location
and map ID. Also send the query after releasing spirit so the minimap
corpse marker points to the correct position even when the player died
in an instance and releases to an outdoor graveyard.
Previously the corpse position was only set from the entity death
location, which could be wrong for cross-map ghost runs.
Change the bare shin (no boots) default from geoset 502 to 503 across
all four code paths (character creation, character preview, equipment
update, NPC rendering).
Geoset 503 has Y width ~0.44 which better matches the thigh mesh
width (~0.42) than 502's width (~0.39), reducing the visible gap at
the knee joint where lower and upper leg meshes meet.
Returns squared distance between the player and a unit, plus a boolean
indicating whether the calculation was possible. Squared distance
avoids sqrt for efficient range comparisons.
Used by DBM, BigWigs, and proximity warning addons for raid encounter
range checks (e.g., "spread 10 yards" mechanics).
Returns true if the player is within interaction distance of a unit:
- Index 1: Inspect range (28 yards)
- Index 2: Trade range (11 yards)
- Index 3: Duel range (10 yards)
- Index 4: Follow range (28 yards)
Used by trade addons, inspect addons, and proximity detection addons.
Returns 1 if the spell can reach the target, 0 if out of range, nil
if range can't be determined. Compares player-to-target distance against
the spell's maxRange from Spell.dbc via SpellDataResolver.
Used by healing addons (Healbot, VuhDo, Clique) to check if heals can
reach party members, and by action bar addons for range coloring.
Returns true when the unit's entity exists in the entity manager
(within UPDATE_OBJECT range). Unlike UnitExists which falls back to
party member data, UnitIsVisible only returns true for entities that
can actually be rendered on screen.
Used by nameplate addons and proximity addons to check if a unit is
within visual range.
Session 14 commit #100: 95 new API functions, 113 new events.
Fire AUTOFOLLOW_BEGIN when the player starts following another unit
via /follow. Fire AUTOFOLLOW_END when following is cancelled. Used by
movement addons and AFK detection addons.
Fire PLAYER_LOGOUT from SMSG_LOGOUT_RESPONSE when the server confirms
logout. Addons use this to save state and perform cleanup before the
player leaves the world.
Fire QUEST_TURNED_IN with questId from SMSG_QUESTGIVER_QUEST_COMPLETE
when a quest is successfully completed and removed from the quest log.
Used by quest tracking addons (Questie, QuestHelper) and achievement
tracking addons.
Fire CHAT_MSG_COMBAT_XP_GAIN from SMSG_EXPLORATION_EXPERIENCE when the
player discovers a new area and gains exploration XP. Used by XP
tracking addons to count all XP sources including discovery.
Returns "TANK", "HEALER", "DAMAGER", or "NONE" based on the WotLK LFG
roles bitmask from SMSG_GROUP_LIST. Used by raid frame addons (Grid,
VuhDo, Healbot) to display role icons next to player names.
Fire CHAT_MSG_COMBAT_FACTION_CHANGE with the reputation change message
alongside UPDATE_FACTION when faction standings change. Used by
reputation tracking addons (FactionFriend, RepHelper) that parse
reputation gain messages.
Fire TRAINER_UPDATE from SMSG_TRAINER_BUY_SUCCEEDED so trainer UI
addons refresh the spell list (marking learned spells as unavailable).
Also fire SPELLS_CHANGED so spellbook and action bar addons detect
the newly learned spell.
UnitCanAttack(unit, otherUnit): returns true if otherUnit is hostile
(attackable). UnitCanCooperate(unit, otherUnit): returns true if
otherUnit is friendly (can receive beneficial spells).
Used by nameplate addons for coloring and by targeting addons for
filtering hostile/friendly units.
Returns the creature family name (Wolf, Cat, Bear, etc.) for NPC units.
Data from CreatureInfo cache (creature_template family field). Used by
hunter pet management addons and tooltips that show pet family info.
Enhance RAID_CLASS_COLORS entries with colorStr hex string field and
GenerateHexColor()/GenerateHexColorMarkup() methods. Many addons
(Prat, Details, oUF) use colorStr to build colored chat text and
GenerateHexColor for inline color markup.
Fire UNIT_MODEL_CHANGED for the player when mount display ID changes
(mounting or dismounting). Mount addons and portrait addons now get
notified when the player's visual model switches between ground and
mounted form.
Returns a table of power cost entries: {{ type=powerType, cost=amount,
name=powerName }}. Data from SpellDataResolver (Spell.dbc ManaCost and
PowerType fields). Used by spell tooltip addons and action bar addons
that display mana/rage/energy costs.
Returns true when the player is on a taxi/flight path. Used by action
bar addons to disable abilities during flight and by map addons to
track taxi state.
Previously UnitIsDead returned false for out-of-range party members
(entity not in entity manager). Now checks curHealth==0 from
SMSG_PARTY_MEMBER_STATS data, so raid frame addons correctly show
dead members in other zones as dead.
Previously UnitPowerType returned 0 (MANA) for party members who are
out of entity range. Now falls back to SMSG_PARTY_MEMBER_STATS power
type data, so raid frame addons correctly color rage/energy/runic
power bars for distant party members.
Fire UPDATE_WORLD_STATES from SMSG_UPDATE_WORLD_STATE when BG scores,
zone capture progress, or other world state variables change. Used by
BG score addons and world PvP objective tracking addons.
Fire UNIT_THREAT_LIST_UPDATE from SMSG_THREAT_UPDATE,
SMSG_HIGHEST_THREAT_UPDATE, and SMSG_THREAT_CLEAR. Threat data is
already parsed and stored in threatLists_ — this event notifies
addon systems when the data changes.
Used by Omen, ThreatPlates, and other threat meter addons to refresh
their displays when threat values update.
Implement UnitDetailedThreatSituation(unit, mobUnit) returning:
- isTanking (boolean)
- status (0-3, same as UnitThreatSituation)
- threatPct (100 if tanking, 0 otherwise)
- rawThreatPct (same)
- threatValue (0 — no server threat data available)
Used by Omen and other threat meter addons that query detailed threat
info per mob-target pair.
Implement UnitThreatSituation(unit, mobUnit) returning 0-3 threat level:
- 0: not on threat table
- 1: in combat but not tanking (mob targeting someone else)
- 3: securely tanking (mob is targeting this unit)
Approximated from mob's UNIT_FIELD_TARGET to determine who the mob is
attacking. Used by threat meter addons (Omen, ThreatPlates) and tank
UI addons to display threat state.
Add three tapped-state query functions for addons:
- UnitIsTapped(unit): true if any player has tagged the mob
- UnitIsTappedByPlayer(unit): true if local player can loot (tapped+lootable)
- UnitIsTappedByAllThreatList(unit): true if shared-tag mob
Used by nameplate addons (Plater, TidyPlates) and unit frame addons
to determine and display tap ownership state.
Extend tapped-by-other detection to the focus frame, matching the
target frame and nameplate treatment. All three UI elements (nameplate,
target frame, focus frame) now consistently show grey for tapped mobs.
Extend the tapped-by-other-player check to the target frame. Mobs
tagged by another player now show a grey name color on the target
frame, matching the grey nameplate treatment and WoW's behavior.
Players can now see at a glance on both nameplates AND target frame
whether a mob is tagged.
Check UNIT_DYNFLAG_TAPPED_BY_PLAYER (0x0004) on hostile NPC nameplates.
Mobs tagged by another player now show grey health bars instead of red,
matching WoW's visual indication that the mob won't yield loot/XP.
Mobs with TAPPED_BY_ALL_THREAT_LIST (0x0008) still show red since
those are shared-tag mobs that give loot to everyone.
Fire UNIT_FLAGS for player/target/focus when UNIT_FIELD_FLAGS updates.
Covers PvP flag, combat state, silenced, disarmed, and other flag
changes. Used by nameplate addons for PvP indicators and by unit frame
addons tracking CC/silence state.