Reduce flare intensity when sun is near the horizon via smoothstep on
sunDir.z (0→0.25 range). Apply amber/orange color shift to flare elements
at sunrise/sunset for a warm golden glow. Prevents overly bright flares
at low sun angles while enhancing atmospheric mood.
SMSG_ENCHANTMENTLOG now resolves spell name and shows "You enchant with
[name]" or "[Caster] enchants your item with [name]" instead of silent
debug log. SMSG_LOG_XPGAIN now shows creature name: "Wolf dies, you gain
45 experience" instead of generic "You gain 45 experience" for kill XP.
Add AddonManager::reload() which saves all SavedVariables, shuts down the
Lua VM, re-initializes it, rescans .toc files, and reloads all addons.
Wire /reload, /reloadui, /rl slash commands that call reload() and fire
VARIABLES_LOADED + PLAYER_LOGIN + PLAYER_ENTERING_WORLD lifecycle events.
Essential for addon development and troubleshooting.
When the dungeon finder initiates a role check (SMSG_LFG_ROLE_CHECK_UPDATE
state=2), show a centered popup with Tank/Healer/DPS checkboxes and
Accept/Leave Queue buttons. Accept sends CMSG_LFG_SET_ROLES with the
selected role mask. Previously only showed passive "Role check in progress"
text with no way to respond.
Add Weather::Type::STORM enum value and wire it from SMSG_WEATHER type 3.
Storm particles are faster (70 units/s vs rain's 50), wind-angled at 15+
units lateral velocity with gusty turbulence, darker blue-grey tint, and
shorter lifetime. Previously storms rendered identically to rain.
Remove unused getPlayerUnit() helper in lua_engine.cpp (-Wunused-function).
Increase countStr buffer from 8 to 16 bytes in action bar item count
display to eliminate -Wformat-truncation warning for %d with int32_t.
Build is now warning-free.
Add zone name query functions using worldStateZoneId + getAreaName lookup.
GetRealZoneText is aliased to GetZoneText. These are heavily used by boss
mod addons (DBM) for zone detection and by quest tracking addons.
Add UNIT_SPELLCAST_CHANNEL_START (MSG_CHANNEL_START), UNIT_SPELLCAST_CHANNEL_STOP
(MSG_CHANNEL_UPDATE with 0ms remaining), UNIT_SPELLCAST_FAILED (SMSG_CAST_RESULT
with error), and UNIT_SPELLCAST_INTERRUPTED (SMSG_SPELL_FAILURE) events. These
enable addons to track channeled spells and cast interruptions for all units.
Show [Winning] (green) or [Outbid] (red) labels on the Bids tab based on
bidderGuid vs player GUID comparison. Show [Bid] (gold) indicator on the
seller's Auctions tab when someone has placed a bid on their listing.
Improves auction house usability by making bid status visible at a glance.
Add UnitAffectingCombat, GetNumRaidMembers, GetNumPartyMembers, UnitInParty,
UnitInRaid, UnitIsUnit, UnitIsFriend, UnitIsEnemy, and UnitCreatureType.
These are commonly used by raid/group addons for party composition checks,
combat state queries, and mob type identification. Total API count now 55.
NPC character models used wrong geoset groups: gloves were group 3 (300s)
instead of group 4 (400s), boots were group 4 (400s) instead of group 5
(500s), matching the character preview code. Also remove spurious "torso"
geoset from group 5 (conflicted with boots) — chest armor controls only
group 8 (sleeves), not a separate torso visibility group. Fixes NPC
equipment rendering with incorrect body part meshes.
Fire ZONE_CHANGED_NEW_AREA and ZONE_CHANGED when worldStateZoneId changes
in SMSG_INIT_WORLD_STATES. Add VARIABLES_LOADED and PLAYER_LOGIN events in
the addon loading sequence (before PLAYER_ENTERING_WORLD), and fire
PLAYER_ENTERING_WORLD on subsequent world entries (teleport, instance).
Enables zone-aware addons like DBM and quest trackers.
Add QuestPoi struct and setQuestPois() to WorldMap, render quest objective
markers as cyan circles with golden outlines and quest title labels. Wire
gossipPois_ (from SMSG_QUEST_POI_QUERY_RESPONSE) through GameScreen to the
world map so quest objectives are visible alongside party dots and taxi nodes.
IsMounted, IsFlying, IsSwimming, IsResting, IsFalling, and IsStealthed
now query actual GameHandler state (mount display ID, movement flags,
resting flag, aura list) instead of returning false. Add GetUnitSpeed
for player run speed. Fixes addon conditionals that depend on player
movement/mount/combat state.
handleGroupUninvite and leaveGroup cleared partyData but did not fire
GROUP_ROSTER_UPDATE/PARTY_MEMBERS_CHANGED events, so addon group tracking
would not update when kicked or leaving. Now both paths fire both events.
Fire COMBAT_LOG_EVENT_UNFILTERED from addCombatText with WoW-compatible
subevent names (SWING_DAMAGE, SPELL_DAMAGE, SPELL_HEAL, etc.), source/dest
GUIDs, names, spell info, and amount. Also fire SPELL_UPDATE_COOLDOWN and
ACTIONBAR_UPDATE_COOLDOWN when cooldowns start (handleSpellCooldown), not
just when they end. Enables damage meter and boss mod addons.
Add PLAYER_REGEN_DISABLED/ENABLED (combat enter/leave) via per-frame
edge detection, LEARNED_SPELL_IN_TAB/SPELLS_CHANGED on spell learn/remove,
SPELL_UPDATE_COOLDOWN/ACTIONBAR_UPDATE_COOLDOWN on cooldown finish, and
PLAYER_XP_UPDATE on XP field changes. Total addon events now at 34.
Extend resolveUnit() to support party1-4, raid1-40, and use resolveUnitGuid
for UnitGUID/UnitIsPlayer/UnitBuff/UnitDebuff (including unitAurasCache for
party member auras). Fire UNIT_HEALTH, UNIT_POWER, UNIT_AURA, UNIT_SPELLCAST_START,
UNIT_SPELLCAST_SUCCEEDED, GROUP_ROSTER_UPDATE, and PARTY_MEMBERS_CHANGED events
to Lua addons from the corresponding packet handlers.
- hooksecurefunc(tblOrName, name, hook) — hook any function to run
additional code after it executes without replacing the original.
Supports both global and table method forms.
- UIParent, WorldFrame — standard parent frames that many addons
reference as parents for their own frames.
- Noop stubs: SetDesaturation, SetPortraitTexture, PlaySound,
PlaySoundFile — prevent errors from addons that call these
visual/audio functions which don't have implementations yet.
Many WoW addons use DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b) to
output colored text to chat. Implemented as a Lua table with AddMessage
that converts RGB floats to WoW color codes and calls print().
Also aliased as ChatFrame1 for compatibility.
Example: DEFAULT_CHAT_FRAME:AddMessage("Hello!", 1, 0.5, 0)
- GetNumAddOns() — returns count of loaded addons
- GetAddOnInfo(indexOrName) — returns name, title, notes, loadable
Addon info is stored in the Lua registry from the .toc directives
and populated before addon files execute. Useful for addon managers
and compatibility checks between addons.
Total WoW API: 33 functions.
Fire ADDON_LOADED(addonName) after all of an addon's files have been
executed. This is the standard WoW pattern for addon initialization —
addons register for this event to set up defaults after SavedVariables
are loaded:
local f = CreateFrame("Frame")
f:RegisterEvent("ADDON_LOADED")
f:SetScript("OnEvent", function(self, event, name)
if name == "MyAddon" then
MyAddonDB = MyAddonDB or {defaults}
end
end)
Total addon events: 20.
Implement the most commonly used buff/debuff query functions:
- UnitBuff(unitId, index) — query the Nth buff on a unit
- UnitDebuff(unitId, index) — query the Nth debuff on a unit
Returns WoW-compatible 11-value tuple: name, rank, icon, count,
debuffType, duration, expirationTime, caster, isStealable,
shouldConsolidate, spellId.
Supports "player" and "target" unit IDs. Essential for buff tracking
addons (WeakAuras-style), healer addons, and combat analysis tools.
Total WoW API: 31 functions.
The Semgrep security scan was failing because vendored Lua 5.1.5 source
uses strcpy/strncpy which are flagged as insecure C functions. These are
false positives in frozen third-party code that we don't modify.
Added .semgrepignore to exclude all vendored extern/ directories
(lua-5.1.5, imgui, stb, vk-bootstrap, FidelityFX SDKs).
Addons can now persist data across sessions using the standard WoW
SavedVariables pattern:
1. Declare in .toc: ## SavedVariables: MyAddonDB
2. Use the global in Lua: MyAddonDB = MyAddonDB or {default = true}
3. Data is automatically saved on logout and restored on next login
Implementation:
- TocFile::getSavedVariables() parses comma-separated variable names
- LuaEngine::loadSavedVariables() executes saved .lua file to restore globals
- LuaEngine::saveSavedVariables() serializes Lua tables/values to valid Lua
- Serializer handles tables (nested), strings, numbers, booleans, nil
- Save triggered on PLAYER_LEAVING_WORLD and AddonManager::shutdown()
- Files stored as <AddonDir>/<AddonName>.lua.saved
Updated HelloWorld addon to track login count across sessions.
- UnitRace(unitId) — returns race name ("Human", "Orc", etc.)
- UnitPowerType(unitId) — returns power type ID and name ("MANA", "RAGE", etc.)
- GetNumGroupMembers() — party/raid member count
- UnitGUID(unitId) — returns hex GUID string (0x format)
- UnitIsPlayer(unitId) — true if target is a player (not NPC)
- InCombatLockdown() — true if player is in combat
Total WoW API surface: 29 functions.
Implement WoW's C_Timer API used by most modern addons:
- C_Timer.After(seconds, callback) — fire callback after delay
- C_Timer.NewTicker(seconds, callback, iterations) — repeating timer
with optional iteration limit and :Cancel() method
Implemented in pure Lua using a hidden OnUpdate frame that
auto-hides when no timers are pending (zero overhead when idle).
Example:
C_Timer.After(3, function() print("3 sec later!") end)
local ticker = C_Timer.NewTicker(1, function() print("tick") end, 5)
Frames can now set an OnUpdate script that fires every frame with
the elapsed time as an argument. This enables addon timers, polling,
and animations.
local f = CreateFrame("Frame")
f:SetScript("OnUpdate", function(self, elapsed)
-- called every frame with deltaTime
end)
OnUpdate only fires for visible frames (frame:Hide() pauses it).
Tracked in __WoweeOnUpdateFrames table, dispatched via
LuaEngine::dispatchOnUpdate() called from the Application main loop.
Fire more gameplay events to Lua addons:
- PLAYER_MONEY — when gold/silver/copper changes (both CREATE and VALUES paths)
- PLAYER_DEAD — on forced death (SMSG_FORCED_DEATH_UPDATE)
- PLAYER_ALIVE — when ghost flag clears (player resurrected)
Total addon events: 19 (2 world + 12 chat + 5 gameplay).
Add a generic AddonEventCallback to GameHandler for firing named events
with string arguments directly from game logic. Wire it to the addon
system in Application.
New events fired:
- PLAYER_TARGET_CHANGED — when target is set or cleared
- PLAYER_LEVEL_UP(newLevel) — on level up
The generic callback pattern makes it easy to add more events from
game_handler.cpp without touching Application/AddonManager code.
Total addon events: 16 (2 world + 12 chat + 2 gameplay).
Implement the core WoW frame system that nearly all addons use:
- CreateFrame(type, name, parent, template) — creates a frame table
with metatable methods, optionally registered as a global by name
- frame:RegisterEvent(event) — register frame for event dispatch
- frame:UnregisterEvent(event) — unregister
- frame:SetScript(type, handler) — set OnEvent/OnUpdate/etc handlers
- frame:GetScript(type) — retrieve handlers
- frame:Show()/Hide()/IsShown()/IsVisible() — visibility state
- frame:GetName() — return frame name
Event dispatch now fires both global RegisterEvent handlers AND
frame OnEvent scripts, matching WoW's dual dispatch model.
Updated HelloWorld to use standard WoW addon pattern:
local f = CreateFrame("Frame", "MyFrame")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:SetScript("OnEvent", function(self, event, ...) end)
Add 5 more essential WoW API functions for addon development:
- SendChatMessage(msg, type, lang, target) — send chat messages
(SAY, YELL, WHISPER, PARTY, GUILD, OFFICER, RAID, BG)
- CastSpellByName(name) — cast highest rank of named spell
- IsSpellKnown(spellId) — check if player knows a spell
- GetSpellCooldown(nameOrId) — get remaining cooldown
- HasTarget() — check if player has a target
Total WoW API surface: 18 functions across Unit, Game, and Action
categories. Addons can now query state, react to events, send
messages, and cast spells.
Wire chat messages to the addon event system via AddonChatCallback.
Every chat message now fires the corresponding WoW event:
- CHAT_MSG_SAY, CHAT_MSG_YELL, CHAT_MSG_WHISPER
- CHAT_MSG_PARTY, CHAT_MSG_GUILD, CHAT_MSG_OFFICER
- CHAT_MSG_RAID, CHAT_MSG_RAID_WARNING, CHAT_MSG_BATTLEGROUND
- CHAT_MSG_SYSTEM, CHAT_MSG_CHANNEL, CHAT_MSG_EMOTE
Event handlers receive (eventName, message, senderName) arguments.
Addons can now filter, react to, or log chat messages in real-time.
Implement the WoW-compatible event system that lets addons react to
gameplay events in real-time:
- RegisterEvent(eventName, handler) — register a Lua function for an event
- UnregisterEvent(eventName, handler) — remove a handler
- fireEvent() dispatches events to all registered handlers with args
Currently fired events:
- PLAYER_ENTERING_WORLD — after addons load and world entry completes
- PLAYER_LEAVING_WORLD — before logout/disconnect
Events are stored in a __WoweeEvents Lua table, dispatched via
LuaEngine::fireEvent() which is called from AddonManager::fireEvent().
Error handling logs Lua errors without crashing.
Updated HelloWorld addon to use RegisterEvent for world entry/exit.
Foundation for WoW-compatible addon support:
- Vendor Lua 5.1.5 source as a static library (extern/lua-5.1.5)
- TocParser: parses .toc files (## directives + file lists)
- LuaEngine: Lua 5.1 VM with sandboxed stdlib (no io/os/debug),
WoW-compatible print() that outputs to chat, GetTime() stub
- AddonManager: scans Data/interface/AddOns/ for .toc files,
loads .lua files on world entry, skips LoadOnDemand addons
- /run <code> slash command for inline Lua execution
- HelloWorld test addon that prints to chat on load
Integration: AddonManager initialized after asset manager, addons
loaded once on first world entry, reset on logout. XML frame
parsing is deferred to a future step.
When hovering a /use macro whose item's on-use spell isn't in the DBC
(no rich spell tooltip available), the tooltip fell back to showing raw
macro text. Now searches the item info cache and shows the full item
tooltip (stats, quality, binding, description) as a more useful
fallback for /use macros.
Macros with /use ItemName tried to find the item as a spell name for
icon resolution, which fails for items without a matching spell (e.g.
engineering trinkets, quest items). Now falls back to searching the
item info cache by name and showing the item's display icon when no
spell name matches.
setQuestTracked() modified trackedQuestIds_ but didn't call
saveCharacterConfig(), so tracked quests were only persisted if
another action (like editing a macro or rearranging the action bar)
happened to trigger a save before logout. Now saves immediately
when quests are tracked or untracked.
Quest tracking choices (right-click → Track on the quest objective
tracker) were lost on logout because trackedQuestIds_ was not saved
in the character config. Now saves tracked quest IDs as a comma-
separated list and restores them on login, so the quest tracker
shows the same quests the player chose to track in their previous
session.
Add [target=mouseover] and the @ shorthand syntax (@focus, @pet,
@mouseover, @player, @target) to the /macrohelp output. These are
commonly used for mouseover healing macros and were already supported
but not documented in the in-game help.
The /help text was missing several commonly-used commands: /castsequence,
/use, /threat, /combatlog, /mark, /raidinfo, /assist, /inspect,
/chathelp. Reorganized categories for clarity and added all missing
entries to match the expanded auto-complete list.
The spell visual failed-model cache was never cleared across world
changes, so models that failed to load during initial asset loading
(before MPQ/CASC data was fully indexed) would never retry. Now clears
spellVisualFailedModels_ in resetCombatVisualState() alongside the
active spell visual cleanup, giving failed models a fresh attempt on
each world entry.
Macros with /use ItemName (e.g. /use Healthstone, /use Engineering
trinket) had no icon, cooldown, or tooltip on the action bar because
only /cast and /castsequence were recognized. Now the spell resolution
also handles /use by looking up the item name in the item info cache
and finding its on-use spell ID. Added getItemInfoCache() accessor.
The macro primary spell cache stored 0 (no spell found) when a macro
referenced a spell the player hadn't learned yet. After learning the
spell from a trainer or leveling up, the cache was never refreshed,
so the macro button stayed broken. Now tracks the known spell count
and clears the cache when it changes, ensuring newly learned spells
are resolved on the next frame.