2026-03-20 11:12:07 -07:00
# include "addons/lua_engine.hpp"
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
# include "addons/lua_api_helpers.hpp"
# include "addons/lua_api_registrations.hpp"
2026-03-20 13:07:45 -07:00
# include "addons/toc_parser.hpp"
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
# include "core/window.hpp"
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
# include <imgui.h>
2026-03-20 12:22:50 -07:00
# include <fstream>
# include <filesystem>
2026-03-20 11:12:07 -07:00
extern " C " {
# include <lua.h>
# include <lauxlib.h>
# include <lualib.h>
}
namespace wowee : : addons {
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
static int lua_wow_print ( lua_State * L ) {
int nargs = lua_gettop ( L ) ;
std : : string result ;
for ( int i = 1 ; i < = nargs ; i + + ) {
if ( i > 1 ) result + = ' \t ' ;
// Lua 5.1: use lua_tostring (luaL_tolstring is 5.3+)
if ( lua_isstring ( L , i ) | | lua_isnumber ( L , i ) ) {
const char * s = lua_tostring ( L , i ) ;
if ( s ) result + = s ;
} else if ( lua_isboolean ( L , i ) ) {
result + = lua_toboolean ( L , i ) ? " true " : " false " ;
} else if ( lua_isnil ( L , i ) ) {
result + = " nil " ;
feat: add action bar, combo points, reaction, and connection Lua API functions
Implement 10 new WoW Lua API functions for addon compatibility:
- GetComboPoints, UnitReaction, UnitIsConnected for unit frames/raid addons
- HasAction, GetActionTexture, IsCurrentAction, IsUsableAction, GetActionCooldown
for action bar addons (Bartender, Dominos, etc.)
- UnitMana/UnitManaMax as Classic-era aliases for UnitPower/UnitPowerMax
2026-03-21 01:31:34 -07:00
} else {
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
result + = lua_typename ( L , lua_type ( L , i ) ) ;
2026-03-22 16:30:31 -07:00
}
}
auto * gh = getGameHandler ( L ) ;
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
if ( gh ) {
game : : MessageChatData msg ;
msg . type = game : : ChatType : : SYSTEM ;
msg . language = game : : ChatLanguage : : UNIVERSAL ;
msg . message = result ;
gh - > addLocalChatMessage ( msg ) ;
2026-03-22 16:30:31 -07:00
}
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
LOG_INFO ( " [Lua] " , result ) ;
2026-03-22 16:30:31 -07:00
return 0 ;
}
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
// WoW-compatible message() — same as print for now
static int lua_wow_message ( lua_State * L ) {
return lua_wow_print ( L ) ;
2026-03-22 16:30:31 -07:00
}
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
// Helper: resolve WoW unit IDs to GUID
// Read UNIT_FIELD_TARGET_LO/HI from an entity's update fields to get what it's targeting
2026-03-22 16:30:31 -07:00
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
// --- Frame system functions ---
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
static int lua_Frame_RegisterEvent ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ; // self
const char * eventName = luaL_checkstring ( L , 2 ) ;
// Get frame's registered events table (create if needed)
lua_getfield ( L , 1 , " __events " ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_newtable ( L ) ;
lua_pushvalue ( L , - 1 ) ;
lua_setfield ( L , 1 , " __events " ) ;
}
lua_pushboolean ( L , 1 ) ;
lua_setfield ( L , - 2 , eventName ) ;
lua_pop ( L , 1 ) ;
// Also register in global __WoweeFrameEvents for dispatch
lua_getglobal ( L , " __WoweeFrameEvents " ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_newtable ( L ) ;
lua_pushvalue ( L , - 1 ) ;
lua_setglobal ( L , " __WoweeFrameEvents " ) ;
}
lua_getfield ( L , - 1 , eventName ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_newtable ( L ) ;
lua_pushvalue ( L , - 1 ) ;
lua_setfield ( L , - 3 , eventName ) ;
}
// Append frame reference
int len = static_cast < int > ( lua_objlen ( L , - 1 ) ) ;
lua_pushvalue ( L , 1 ) ; // push frame
lua_rawseti ( L , - 2 , len + 1 ) ;
lua_pop ( L , 2 ) ; // pop list + __WoweeFrameEvents
return 0 ;
}
// Frame method: frame:UnregisterEvent("EVENT")
static int lua_Frame_UnregisterEvent ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
const char * eventName = luaL_checkstring ( L , 2 ) ;
// Remove from frame's own events
lua_getfield ( L , 1 , " __events " ) ;
if ( lua_istable ( L , - 1 ) ) {
lua_pushnil ( L ) ;
lua_setfield ( L , - 2 , eventName ) ;
}
lua_pop ( L , 1 ) ;
return 0 ;
}
// Frame method: frame:SetScript("handler", func)
static int lua_Frame_SetScript ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
const char * scriptType = luaL_checkstring ( L , 2 ) ;
// arg 3 can be function or nil
lua_getfield ( L , 1 , " __scripts " ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_newtable ( L ) ;
lua_pushvalue ( L , - 1 ) ;
lua_setfield ( L , 1 , " __scripts " ) ;
}
lua_pushvalue ( L , 3 ) ;
lua_setfield ( L , - 2 , scriptType ) ;
lua_pop ( L , 1 ) ;
2026-03-20 12:07:22 -07:00
// Track frames with OnUpdate in __WoweeOnUpdateFrames
if ( strcmp ( scriptType , " OnUpdate " ) = = 0 ) {
lua_getglobal ( L , " __WoweeOnUpdateFrames " ) ;
if ( ! lua_istable ( L , - 1 ) ) { lua_pop ( L , 1 ) ; return 0 ; }
if ( lua_isfunction ( L , 3 ) ) {
// Add frame to the list
int len = static_cast < int > ( lua_objlen ( L , - 1 ) ) ;
lua_pushvalue ( L , 1 ) ;
lua_rawseti ( L , - 2 , len + 1 ) ;
}
lua_pop ( L , 1 ) ;
}
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
return 0 ;
}
// Frame method: frame:GetScript("handler")
static int lua_Frame_GetScript ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
const char * scriptType = luaL_checkstring ( L , 2 ) ;
lua_getfield ( L , 1 , " __scripts " ) ;
if ( lua_istable ( L , - 1 ) ) {
lua_getfield ( L , - 1 , scriptType ) ;
} else {
lua_pushnil ( L ) ;
}
return 1 ;
}
// Frame method: frame:GetName()
static int lua_Frame_GetName ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __name " ) ;
return 1 ;
}
// Frame method: frame:Show() / frame:Hide() / frame:IsShown() / frame:IsVisible()
static int lua_Frame_Show ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_pushboolean ( L , 1 ) ;
lua_setfield ( L , 1 , " __visible " ) ;
return 0 ;
}
static int lua_Frame_Hide ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_pushboolean ( L , 0 ) ;
lua_setfield ( L , 1 , " __visible " ) ;
return 0 ;
}
static int lua_Frame_IsShown ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __visible " ) ;
lua_pushboolean ( L , lua_toboolean ( L , - 1 ) ) ;
return 1 ;
}
2026-03-21 01:52:59 -07:00
// Frame method: frame:CreateTexture(name, layer) → texture stub
static int lua_Frame_CreateTexture ( lua_State * L ) {
lua_newtable ( L ) ;
// Add noop methods for common texture operations
luaL_dostring ( L ,
" return function(t) "
" function t:SetTexture() end "
" function t:SetTexCoord() end "
" function t:SetVertexColor() end "
" function t:SetAllPoints() end "
" function t:SetPoint() end "
" function t:SetSize() end "
" function t:SetWidth() end "
" function t:SetHeight() end "
" function t:Show() end "
" function t:Hide() end "
" function t:SetAlpha() end "
" function t:GetTexture() return '' end "
" function t:SetDesaturated() end "
" function t:SetBlendMode() end "
" function t:SetDrawLayer() end "
" end " ) ;
lua_pushvalue ( L , - 2 ) ; // push the table
lua_call ( L , 1 , 0 ) ; // call the function with the table
return 1 ;
}
// Frame method: frame:CreateFontString(name, layer, template) → fontstring stub
static int lua_Frame_CreateFontString ( lua_State * L ) {
lua_newtable ( L ) ;
luaL_dostring ( L ,
" return function(fs) "
" fs._text = '' "
" function fs:SetText(t) self._text = t or '' end "
" function fs:GetText() return self._text end "
" function fs:SetFont() end "
" function fs:SetFontObject() end "
" function fs:SetTextColor() end "
" function fs:SetJustifyH() end "
" function fs:SetJustifyV() end "
" function fs:SetPoint() end "
" function fs:SetAllPoints() end "
" function fs:Show() end "
" function fs:Hide() end "
" function fs:SetAlpha() end "
" function fs:GetStringWidth() return 0 end "
" function fs:GetStringHeight() return 0 end "
" function fs:SetWordWrap() end "
" function fs:SetNonSpaceWrap() end "
" function fs:SetMaxLines() end "
" function fs:SetShadowOffset() end "
" function fs:SetShadowColor() end "
" function fs:SetWidth() end "
" function fs:SetHeight() end "
" end " ) ;
lua_pushvalue ( L , - 2 ) ;
lua_call ( L , 1 , 0 ) ;
return 1 ;
}
// GetFramerate() → fps
static int lua_GetFramerate ( lua_State * L ) {
lua_pushnumber ( L , static_cast < double > ( ImGui : : GetIO ( ) . Framerate ) ) ;
return 1 ;
}
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
// GetCursorPosition() → x, y (screen coordinates, origin top-left)
static int lua_GetCursorPosition ( lua_State * L ) {
const auto & io = ImGui : : GetIO ( ) ;
lua_pushnumber ( L , io . MousePos . x ) ;
lua_pushnumber ( L , io . MousePos . y ) ;
return 2 ;
}
// GetScreenWidth() → width
static int lua_GetScreenWidth ( lua_State * L ) {
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
auto * svc = getLuaServices ( L ) ;
auto * window = svc ? svc - > window : nullptr ;
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
lua_pushnumber ( L , window ? window - > getWidth ( ) : 1920 ) ;
return 1 ;
}
// GetScreenHeight() → height
static int lua_GetScreenHeight ( lua_State * L ) {
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
auto * svc = getLuaServices ( L ) ;
auto * window = svc ? svc - > window : nullptr ;
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
lua_pushnumber ( L , window ? window - > getHeight ( ) : 1080 ) ;
return 1 ;
}
2026-03-22 17:12:57 -07:00
// Modifier key state queries using ImGui IO
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
static int lua_Frame_SetPoint ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
const char * point = luaL_optstring ( L , 2 , " CENTER " ) ;
// Store point info in frame table
lua_pushstring ( L , point ) ;
lua_setfield ( L , 1 , " __point " ) ;
// Optional x/y offsets (args 4,5 if relativeTo is given, or 3,4 if not)
double xOfs = 0 , yOfs = 0 ;
if ( lua_isnumber ( L , 4 ) ) { xOfs = lua_tonumber ( L , 4 ) ; yOfs = lua_tonumber ( L , 5 ) ; }
else if ( lua_isnumber ( L , 3 ) ) { xOfs = lua_tonumber ( L , 3 ) ; yOfs = lua_tonumber ( L , 4 ) ; }
lua_pushnumber ( L , xOfs ) ;
lua_setfield ( L , 1 , " __xOfs " ) ;
lua_pushnumber ( L , yOfs ) ;
lua_setfield ( L , 1 , " __yOfs " ) ;
return 0 ;
}
static int lua_Frame_SetSize ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
double w = luaL_optnumber ( L , 2 , 0 ) ;
double h = luaL_optnumber ( L , 3 , 0 ) ;
lua_pushnumber ( L , w ) ;
lua_setfield ( L , 1 , " __width " ) ;
lua_pushnumber ( L , h ) ;
lua_setfield ( L , 1 , " __height " ) ;
return 0 ;
}
static int lua_Frame_SetWidth ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_pushnumber ( L , luaL_checknumber ( L , 2 ) ) ;
lua_setfield ( L , 1 , " __width " ) ;
return 0 ;
}
static int lua_Frame_SetHeight ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_pushnumber ( L , luaL_checknumber ( L , 2 ) ) ;
lua_setfield ( L , 1 , " __height " ) ;
return 0 ;
}
static int lua_Frame_GetWidth ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __width " ) ;
if ( lua_isnil ( L , - 1 ) ) { lua_pop ( L , 1 ) ; lua_pushnumber ( L , 0 ) ; }
return 1 ;
}
static int lua_Frame_GetHeight ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __height " ) ;
if ( lua_isnil ( L , - 1 ) ) { lua_pop ( L , 1 ) ; lua_pushnumber ( L , 0 ) ; }
return 1 ;
}
static int lua_Frame_GetCenter ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __xOfs " ) ;
double x = lua_isnumber ( L , - 1 ) ? lua_tonumber ( L , - 1 ) : 0 ;
lua_pop ( L , 1 ) ;
lua_getfield ( L , 1 , " __yOfs " ) ;
double y = lua_isnumber ( L , - 1 ) ? lua_tonumber ( L , - 1 ) : 0 ;
lua_pop ( L , 1 ) ;
lua_pushnumber ( L , x ) ;
lua_pushnumber ( L , y ) ;
return 2 ;
}
static int lua_Frame_SetAlpha ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_pushnumber ( L , luaL_checknumber ( L , 2 ) ) ;
lua_setfield ( L , 1 , " __alpha " ) ;
return 0 ;
}
static int lua_Frame_GetAlpha ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __alpha " ) ;
if ( lua_isnil ( L , - 1 ) ) { lua_pop ( L , 1 ) ; lua_pushnumber ( L , 1.0 ) ; }
return 1 ;
}
static int lua_Frame_SetParent ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
if ( lua_istable ( L , 2 ) | | lua_isnil ( L , 2 ) ) {
lua_pushvalue ( L , 2 ) ;
lua_setfield ( L , 1 , " __parent " ) ;
}
return 0 ;
}
static int lua_Frame_GetParent ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
lua_getfield ( L , 1 , " __parent " ) ;
return 1 ;
}
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
// CreateFrame(frameType, name, parent, template)
static int lua_CreateFrame ( lua_State * L ) {
const char * frameType = luaL_optstring ( L , 1 , " Frame " ) ;
const char * name = luaL_optstring ( L , 2 , nullptr ) ;
( void ) frameType ; // All frame types use the same table structure for now
// Create the frame table
lua_newtable ( L ) ;
// Set frame name
if ( name & & * name ) {
lua_pushstring ( L , name ) ;
lua_setfield ( L , - 2 , " __name " ) ;
// Also set as a global so other addons can find it by name
lua_pushvalue ( L , - 1 ) ;
lua_setglobal ( L , name ) ;
}
// Set initial visibility
lua_pushboolean ( L , 1 ) ;
lua_setfield ( L , - 2 , " __visible " ) ;
// Apply frame metatable with methods
lua_getglobal ( L , " __WoweeFrameMT " ) ;
lua_setmetatable ( L , - 2 ) ;
return 1 ;
}
2026-03-20 11:40:58 -07:00
// --- WoW Utility Functions ---
// strsplit(delimiter, str) — WoW's string split
2026-03-20 11:12:07 -07:00
LuaEngine : : LuaEngine ( ) = default ;
LuaEngine : : ~ LuaEngine ( ) {
shutdown ( ) ;
}
bool LuaEngine : : initialize ( ) {
if ( L_ ) return true ;
L_ = luaL_newstate ( ) ;
if ( ! L_ ) {
LOG_ERROR ( " LuaEngine: failed to create Lua state " ) ;
return false ;
}
// Open safe standard libraries (no io, os, debug, package)
luaopen_base ( L_ ) ;
luaopen_table ( L_ ) ;
luaopen_string ( L_ ) ;
luaopen_math ( L_ ) ;
// Remove unsafe globals from base library
const char * unsafeGlobals [ ] = {
" dofile " , " loadfile " , " load " , " collectgarbage " , " newproxy " , nullptr
} ;
for ( const char * * g = unsafeGlobals ; * g ; + + g ) {
lua_pushnil ( L_ ) ;
lua_setglobal ( L_ , * g ) ;
}
registerCoreAPI ( ) ;
2026-03-20 11:23:38 -07:00
registerEventAPI ( ) ;
2026-03-20 11:12:07 -07:00
LOG_INFO ( " LuaEngine: initialized (Lua 5.1) " ) ;
return true ;
}
void LuaEngine : : shutdown ( ) {
if ( L_ ) {
lua_close ( L_ ) ;
L_ = nullptr ;
LOG_INFO ( " LuaEngine: shut down " ) ;
}
}
void LuaEngine : : setGameHandler ( game : : GameHandler * handler ) {
gameHandler_ = handler ;
if ( L_ ) {
lua_pushlightuserdata ( L_ , handler ) ;
lua_setfield ( L_ , LUA_REGISTRYINDEX , " wowee_game_handler " ) ;
}
}
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
void LuaEngine : : setLuaServices ( const LuaServices & services ) {
luaServices_ = services ;
if ( L_ ) {
lua_pushlightuserdata ( L_ , & luaServices_ ) ;
lua_setfield ( L_ , LUA_REGISTRYINDEX , " wowee_lua_services " ) ;
}
}
2026-03-20 11:12:07 -07:00
void LuaEngine : : registerCoreAPI ( ) {
// Override print() to go to chat
lua_pushcfunction ( L_ , lua_wow_print ) ;
lua_setglobal ( L_ , " print " ) ;
// WoW API stubs
lua_pushcfunction ( L_ , lua_wow_message ) ;
lua_setglobal ( L_ , " message " ) ;
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
// --- Per-domain Lua API registration ---
registerUnitLuaAPI ( L_ ) ;
registerSpellLuaAPI ( L_ ) ;
registerInventoryLuaAPI ( L_ ) ;
registerQuestLuaAPI ( L_ ) ;
registerSocialLuaAPI ( L_ ) ;
registerSystemLuaAPI ( L_ ) ;
registerActionLuaAPI ( L_ ) ;
2026-03-20 11:40:58 -07:00
// WoW aliases
lua_getglobal ( L_ , " string " ) ;
lua_getfield ( L_ , - 1 , " format " ) ;
lua_setglobal ( L_ , " format " ) ;
lua_pop ( L_ , 1 ) ; // pop string table
// tinsert/tremove aliases
lua_getglobal ( L_ , " table " ) ;
lua_getfield ( L_ , - 1 , " insert " ) ;
lua_setglobal ( L_ , " tinsert " ) ;
lua_getfield ( L_ , - 1 , " remove " ) ;
lua_setglobal ( L_ , " tremove " ) ;
lua_pop ( L_ , 1 ) ; // pop table
// SlashCmdList table — addons register slash commands here
lua_newtable ( L_ ) ;
lua_setglobal ( L_ , " SlashCmdList " ) ;
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
// Frame metatable with methods
lua_newtable ( L_ ) ; // metatable
lua_pushvalue ( L_ , - 1 ) ;
lua_setfield ( L_ , - 2 , " __index " ) ; // metatable.__index = metatable
static const struct luaL_Reg frameMethods [ ] = {
{ " RegisterEvent " , lua_Frame_RegisterEvent } ,
{ " UnregisterEvent " , lua_Frame_UnregisterEvent } ,
{ " SetScript " , lua_Frame_SetScript } ,
{ " GetScript " , lua_Frame_GetScript } ,
{ " GetName " , lua_Frame_GetName } ,
{ " Show " , lua_Frame_Show } ,
{ " Hide " , lua_Frame_Hide } ,
{ " IsShown " , lua_Frame_IsShown } ,
{ " IsVisible " , lua_Frame_IsShown } , // alias
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
{ " SetPoint " , lua_Frame_SetPoint } ,
{ " SetSize " , lua_Frame_SetSize } ,
{ " SetWidth " , lua_Frame_SetWidth } ,
{ " SetHeight " , lua_Frame_SetHeight } ,
{ " GetWidth " , lua_Frame_GetWidth } ,
{ " GetHeight " , lua_Frame_GetHeight } ,
{ " GetCenter " , lua_Frame_GetCenter } ,
{ " SetAlpha " , lua_Frame_SetAlpha } ,
{ " GetAlpha " , lua_Frame_GetAlpha } ,
{ " SetParent " , lua_Frame_SetParent } ,
{ " GetParent " , lua_Frame_GetParent } ,
2026-03-21 01:52:59 -07:00
{ " CreateTexture " , lua_Frame_CreateTexture } ,
{ " CreateFontString " , lua_Frame_CreateFontString } ,
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
{ nullptr , nullptr }
} ;
for ( const luaL_Reg * r = frameMethods ; r - > name ; r + + ) {
lua_pushcfunction ( L_ , r - > func ) ;
lua_setfield ( L_ , - 2 , r - > name ) ;
}
lua_setglobal ( L_ , " __WoweeFrameMT " ) ;
feat: add 40+ frame metatable methods to prevent addon nil-reference errors
Add commonly called frame methods as no-ops or with basic state tracking
on the frame metatable, so any CreateFrame result supports them:
Layout: SetFrameLevel/Get, SetFrameStrata/Get, SetScale/Get/GetEffective,
ClearAllPoints, SetID/GetID, GetLeft/Right/Top/Bottom, GetNumPoints,
GetPoint, SetHitRectInsets
Behavior: EnableMouse, EnableMouseWheel, SetMovable, SetResizable,
RegisterForDrag, SetClampedToScreen, SetToplevel, Raise, Lower,
StartMoving, StopMovingOrSizing, RegisterForClicks, IsMouseOver
Visual: SetBackdrop, SetBackdropColor, SetBackdropBorderColor
Scripting: HookScript (chains with existing SetScript handlers),
SetAttribute/GetAttribute, GetObjectType
Sizing: SetMinResize, SetMaxResize
These prevent the most common addon errors when addons call standard
WoW frame methods on CreateFrame results.
2026-03-21 02:39:44 -07:00
// Add commonly called no-op frame methods to prevent addon errors
luaL_dostring ( L_ ,
" local mt = __WoweeFrameMT \n "
" function mt:SetFrameLevel(level) self.__frameLevel = level end \n "
" function mt:GetFrameLevel() return self.__frameLevel or 1 end \n "
" function mt:SetFrameStrata(strata) self.__strata = strata end \n "
" function mt:GetFrameStrata() return self.__strata or 'MEDIUM' end \n "
" function mt:EnableMouse(enable) end \n "
" function mt:EnableMouseWheel(enable) end \n "
" function mt:SetMovable(movable) end \n "
" function mt:SetResizable(resizable) end \n "
" function mt:RegisterForDrag(...) end \n "
" function mt:SetClampedToScreen(clamped) end \n "
" function mt:SetBackdrop(backdrop) end \n "
" function mt:SetBackdropColor(...) end \n "
" function mt:SetBackdropBorderColor(...) end \n "
" function mt:ClearAllPoints() end \n "
" function mt:SetID(id) self.__id = id end \n "
" function mt:GetID() return self.__id or 0 end \n "
" function mt:SetScale(scale) self.__scale = scale end \n "
" function mt:GetScale() return self.__scale or 1.0 end \n "
" function mt:GetEffectiveScale() return self.__scale or 1.0 end \n "
" function mt:SetToplevel(top) end \n "
" function mt:Raise() end \n "
" function mt:Lower() end \n "
" function mt:GetLeft() return 0 end \n "
" function mt:GetRight() return 0 end \n "
" function mt:GetTop() return 0 end \n "
" function mt:GetBottom() return 0 end \n "
" function mt:GetNumPoints() return 0 end \n "
" function mt:GetPoint(n) return 'CENTER', nil, 'CENTER', 0, 0 end \n "
" function mt:SetHitRectInsets(...) end \n "
" function mt:RegisterForClicks(...) end \n "
" function mt:SetAttribute(name, value) self['attr_'..name] = value end \n "
" function mt:GetAttribute(name) return self['attr_'..name] end \n "
" function mt:HookScript(scriptType, fn) \n "
" local orig = self.__scripts and self.__scripts[scriptType] \n "
" if orig then \n "
" self:SetScript(scriptType, function(...) orig(...); fn(...) end) \n "
" else \n "
" self:SetScript(scriptType, fn) \n "
" end \n "
" end \n "
" function mt:SetMinResize(...) end \n "
" function mt:SetMaxResize(...) end \n "
" function mt:StartMoving() end \n "
" function mt:StopMovingOrSizing() end \n "
" function mt:IsMouseOver() return false end \n "
" function mt:GetObjectType() return 'Frame' end \n "
) ;
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
// CreateFrame function
lua_pushcfunction ( L_ , lua_CreateFrame ) ;
lua_setglobal ( L_ , " CreateFrame " ) ;
2026-03-21 01:52:59 -07:00
// Cursor/screen/FPS functions
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
lua_pushcfunction ( L_ , lua_GetCursorPosition ) ;
lua_setglobal ( L_ , " GetCursorPosition " ) ;
lua_pushcfunction ( L_ , lua_GetScreenWidth ) ;
lua_setglobal ( L_ , " GetScreenWidth " ) ;
lua_pushcfunction ( L_ , lua_GetScreenHeight ) ;
lua_setglobal ( L_ , " GetScreenHeight " ) ;
2026-03-21 01:52:59 -07:00
lua_pushcfunction ( L_ , lua_GetFramerate ) ;
lua_setglobal ( L_ , " GetFramerate " ) ;
feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions
Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent
These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
// Frame event dispatch table
lua_newtable ( L_ ) ;
lua_setglobal ( L_ , " __WoweeFrameEvents " ) ;
2026-03-20 12:07:22 -07:00
// OnUpdate frame tracking table
lua_newtable ( L_ ) ;
lua_setglobal ( L_ , " __WoweeOnUpdateFrames " ) ;
2026-03-20 12:11:24 -07:00
// C_Timer implementation via Lua (uses OnUpdate internally)
luaL_dostring ( L_ ,
" C_Timer = {} \n "
" local timers = {} \n "
" local timerFrame = CreateFrame('Frame', '__WoweeTimerFrame') \n "
" timerFrame:SetScript('OnUpdate', function(self, elapsed) \n "
" local i = 1 \n "
" while i <= #timers do \n "
" timers[i].remaining = timers[i].remaining - elapsed \n "
" if timers[i].remaining <= 0 then \n "
" local cb = timers[i].callback \n "
" table.remove(timers, i) \n "
" cb() \n "
" else \n "
" i = i + 1 \n "
" end \n "
" end \n "
" if #timers == 0 then self:Hide() end \n "
" end) \n "
" timerFrame:Hide() \n "
" function C_Timer.After(seconds, callback) \n "
" tinsert(timers, {remaining = seconds, callback = callback}) \n "
" timerFrame:Show() \n "
" end \n "
" function C_Timer.NewTicker(seconds, callback, iterations) \n "
" local count = 0 \n "
" local maxIter = iterations or -1 \n "
" local ticker = {cancelled = false} \n "
" local function tick() \n "
" if ticker.cancelled then return end \n "
" count = count + 1 \n "
" callback(ticker) \n "
" if maxIter > 0 and count >= maxIter then return end \n "
" C_Timer.After(seconds, tick) \n "
" end \n "
" C_Timer.After(seconds, tick) \n "
" function ticker:Cancel() self.cancelled = true end \n "
" return ticker \n "
" end \n "
) ;
2026-03-20 13:18:16 -07:00
// DEFAULT_CHAT_FRAME with AddMessage method (used by many addons)
luaL_dostring ( L_ ,
" DEFAULT_CHAT_FRAME = {} \n "
" function DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b) \n "
" if r and g and b then \n "
" local hex = format('|cff%02x%02x%02x', "
" math.floor(r*255), math.floor(g*255), math.floor(b*255)) \n "
" print(hex .. tostring(text) .. '|r') \n "
" else \n "
" print(tostring(text)) \n "
" end \n "
" end \n "
" ChatFrame1 = DEFAULT_CHAT_FRAME \n "
) ;
2026-03-20 13:27:27 -07:00
// hooksecurefunc — hook a function to run additional code after it
luaL_dostring ( L_ ,
" function hooksecurefunc(tblOrName, nameOrFunc, funcOrNil) \n "
" local tbl, name, hook \n "
" if type(tblOrName) == 'table' then \n "
" tbl, name, hook = tblOrName, nameOrFunc, funcOrNil \n "
" else \n "
" tbl, name, hook = _G, tblOrName, nameOrFunc \n "
" end \n "
" local orig = tbl[name] \n "
" if type(orig) ~= 'function' then return end \n "
" tbl[name] = function(...) \n "
" local r = {orig(...)} \n "
" hook(...) \n "
" return unpack(r) \n "
" end \n "
" end \n "
) ;
2026-03-21 02:15:50 -07:00
// LibStub — universal library version management used by Ace3 and virtually all addon libs.
// This is the standard WoW LibStub implementation that addons embed/expect globally.
luaL_dostring ( L_ ,
" local LibStub = LibStub or {} \n "
" LibStub.libs = LibStub.libs or {} \n "
" LibStub.minors = LibStub.minors or {} \n "
" function LibStub:NewLibrary(major, minor) \n "
" assert(type(major) == 'string', 'LibStub:NewLibrary: bad argument #1 (string expected)') \n "
" minor = assert(tonumber(minor or (type(minor) == 'string' and minor:match('(%d+)'))), 'LibStub:NewLibrary: bad argument #2 (number expected)') \n "
" local oldMinor = self.minors[major] \n "
" if oldMinor and oldMinor >= minor then return nil end \n "
" local lib = self.libs[major] or {} \n "
" self.libs[major] = lib \n "
" self.minors[major] = minor \n "
" return lib, oldMinor \n "
" end \n "
" function LibStub:GetLibrary(major, silent) \n "
" if not self.libs[major] and not silent then \n "
" error('Cannot find a library instance of \" ' .. tostring(major) .. ' \" .') \n "
" end \n "
" return self.libs[major], self.minors[major] \n "
" end \n "
" function LibStub:IterateLibraries() return pairs(self.libs) end \n "
" setmetatable(LibStub, { __call = LibStub.GetLibrary }) \n "
" _G['LibStub'] = LibStub \n "
) ;
// CallbackHandler-1.0 — minimal implementation for Ace3-based addons
luaL_dostring ( L_ ,
" if LibStub then \n "
" local CBH = LibStub:NewLibrary('CallbackHandler-1.0', 7) \n "
" if CBH then \n "
" CBH.mixins = { 'RegisterCallback', 'UnregisterCallback', 'UnregisterAllCallbacks', 'Fire' } \n "
" function CBH:New(target, regName, unregName, unregAllName, onUsed) \n "
" local registry = setmetatable({}, { __index = CBH }) \n "
" registry.callbacks = {} \n "
" target = target or {} \n "
" target[regName or 'RegisterCallback'] = function(self, event, method, ...) \n "
" if not registry.callbacks[event] then registry.callbacks[event] = {} end \n "
" local handler = type(method) == 'function' and method or self[method] \n "
" registry.callbacks[event][self] = handler \n "
" end \n "
" target[unregName or 'UnregisterCallback'] = function(self, event) \n "
" if registry.callbacks[event] then registry.callbacks[event][self] = nil end \n "
" end \n "
" target[unregAllName or 'UnregisterAllCallbacks'] = function(self) \n "
" for event, handlers in pairs(registry.callbacks) do handlers[self] = nil end \n "
" end \n "
" registry.Fire = function(self, event, ...) \n "
" if not self.callbacks[event] then return end \n "
" for obj, handler in pairs(self.callbacks[event]) do \n "
" handler(obj, event, ...) \n "
" end \n "
" end \n "
" return registry \n "
" end \n "
" end \n "
" end \n "
) ;
2026-03-20 13:27:27 -07:00
// Noop stubs for commonly called functions that don't need implementation
luaL_dostring ( L_ ,
" function SetDesaturation() end \n "
" function SetPortraitTexture() end \n "
feat: add WoW compatibility stubs for broader addon support
Add error handling (geterrorhandler, seterrorhandler, debugstack, securecall,
issecurevariable), CVar system (GetCVar, GetCVarBool, SetCVar), screen/state
queries (GetScreenWidth/Height, GetFramerate, GetNetStats, IsLoggedIn,
IsMounted, IsFlying, etc.), UI stubs (StaticPopup_Show/Hide, StopSound),
and RAID_CLASS_COLORS table. Prevents common addon load errors.
2026-03-20 14:38:50 -07:00
" function StopSound() end \n "
2026-03-20 13:27:27 -07:00
" function UIParent_OnEvent() end \n "
" UIParent = CreateFrame('Frame', 'UIParent') \n "
2026-03-22 21:39:40 +03:00
" UIPanelWindows = {} \n "
2026-03-20 13:27:27 -07:00
" WorldFrame = CreateFrame('Frame', 'WorldFrame') \n "
2026-03-21 01:52:59 -07:00
// GameTooltip: global tooltip frame used by virtually all addons
" GameTooltip = CreateFrame('Frame', 'GameTooltip') \n "
" GameTooltip.__lines = {} \n "
" function GameTooltip:SetOwner(owner, anchor) self.__owner = owner; self.__anchor = anchor end \n "
" function GameTooltip:ClearLines() self.__lines = {} end \n "
" function GameTooltip:AddLine(text, r, g, b, wrap) table.insert(self.__lines, {text=text or '',r=r,g=g,b=b}) end \n "
" function GameTooltip:AddDoubleLine(l, r, lr, lg, lb, rr, rg, rb) table.insert(self.__lines, {text=(l or '')..' '..(r or '')}) end \n "
" function GameTooltip:SetText(text, r, g, b) self.__lines = {{text=text or '',r=r,g=g,b=b}} end \n "
2026-03-22 16:13:39 -07:00
" function GameTooltip:GetItem() \n "
" if self.__itemId and self.__itemId > 0 then \n "
" local name = GetItemInfo(self.__itemId) \n "
2026-03-22 17:08:31 -07:00
" local _, itemLink = GetItemInfo(self.__itemId) \n "
" return name, itemLink or ('|cffffffff|Hitem:'..self.__itemId..':0|h['..tostring(name)..']|h|r') \n "
2026-03-22 16:13:39 -07:00
" end \n "
" return nil \n "
" end \n "
" function GameTooltip:GetSpell() \n "
" if self.__spellId and self.__spellId > 0 then \n "
" local name = GetSpellInfo(self.__spellId) \n "
" return name, nil, self.__spellId \n "
" end \n "
" return nil \n "
" end \n "
2026-03-21 01:52:59 -07:00
" function GameTooltip:GetUnit() return nil end \n "
" function GameTooltip:NumLines() return #self.__lines end \n "
" function GameTooltip:GetText() return self.__lines[1] and self.__lines[1].text or '' end \n "
2026-03-22 16:13:39 -07:00
" function GameTooltip:SetUnitBuff(unit, index, filter) \n "
" self:ClearLines() \n "
" local name, rank, icon, count, debuffType, duration, expTime, caster, steal, consolidate, spellId = UnitBuff(unit, index, filter) \n "
" if name then \n "
" self:SetText(name, 1, 1, 1) \n "
" if duration and duration > 0 then \n "
" self:AddLine(string.format('%.0f sec remaining', expTime - GetTime()), 1, 1, 1) \n "
" end \n "
" self.__spellId = spellId \n "
" end \n "
" end \n "
" function GameTooltip:SetUnitDebuff(unit, index, filter) \n "
" self:ClearLines() \n "
" local name, rank, icon, count, debuffType, duration, expTime, caster, steal, consolidate, spellId = UnitDebuff(unit, index, filter) \n "
" if name then \n "
" self:SetText(name, 1, 0, 0) \n "
" if debuffType then self:AddLine(debuffType, 0.5, 0.5, 0.5) end \n "
" self.__spellId = spellId \n "
" end \n "
" end \n "
" function GameTooltip:SetHyperlink(link) \n "
" self:ClearLines() \n "
" if not link then return end \n "
" local id = link:match('item:(%d+)') \n "
" if id then \n "
fix: use rich tooltip display for SetAction items and SetHyperlink
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.
2026-03-22 18:22:56 -07:00
" _WoweePopulateItemTooltip(self, tonumber(id)) \n "
2026-03-22 16:13:39 -07:00
" return \n "
" end \n "
" id = link:match('spell:(%d+)') \n "
" if id then \n "
fix: use rich tooltip display for SetAction items and SetHyperlink
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.
2026-03-22 18:22:56 -07:00
" self:SetSpellByID(tonumber(id)) \n "
" return \n "
2026-03-22 16:13:39 -07:00
" end \n "
" end \n "
2026-03-22 18:02:46 -07:00
// Shared item tooltip builder using GetItemInfo return values
" function _WoweePopulateItemTooltip(self, itemId) \n "
" local name, itemLink, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, sellPrice = GetItemInfo(itemId) \n "
" if not name then return false end \n "
" local qColors = {[0]={0.62,0.62,0.62},[1]={1,1,1},[2]={0.12,1,0},[3]={0,0.44,0.87},[4]={0.64,0.21,0.93},[5]={1,0.5,0},[6]={0.9,0.8,0.5},[7]={0,0.8,1}} \n "
" local c = qColors[quality or 1] or {1,1,1} \n "
" self:SetText(name, c[1], c[2], c[3]) \n "
" -- Item level for equipment \n "
" if equipSlot and equipSlot ~= '' and iLevel and iLevel > 0 then \n "
" self:AddLine('Item Level '..iLevel, 1, 0.82, 0) \n "
" end \n "
" -- Equip slot and subclass on same line \n "
" if equipSlot and equipSlot ~= '' then \n "
" local slotNames = {INVTYPE_HEAD='Head',INVTYPE_NECK='Neck',INVTYPE_SHOULDER='Shoulder', \n "
" INVTYPE_CHEST='Chest',INVTYPE_WAIST='Waist',INVTYPE_LEGS='Legs',INVTYPE_FEET='Feet', \n "
" INVTYPE_WRIST='Wrist',INVTYPE_HAND='Hands',INVTYPE_FINGER='Finger', \n "
" INVTYPE_TRINKET='Trinket',INVTYPE_CLOAK='Back',INVTYPE_WEAPON='One-Hand', \n "
" INVTYPE_SHIELD='Off Hand',INVTYPE_2HWEAPON='Two-Hand',INVTYPE_RANGED='Ranged', \n "
" INVTYPE_WEAPONMAINHAND='Main Hand',INVTYPE_WEAPONOFFHAND='Off Hand', \n "
" INVTYPE_HOLDABLE='Held In Off-Hand',INVTYPE_TABARD='Tabard',INVTYPE_ROBE='Chest'} \n "
" local slotText = slotNames[equipSlot] or '' \n "
" local subText = (subclass and subclass ~= '') and subclass or '' \n "
" if slotText ~= '' or subText ~= '' then \n "
" self:AddDoubleLine(slotText, subText, 1,1,1, 1,1,1) \n "
" end \n "
" elseif class and class ~= '' then \n "
" self:AddLine(class, 1, 1, 1) \n "
" end \n "
feat: add full item stat display to tooltips (armor, damage, stats, bind)
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.
2026-03-22 18:07:58 -07:00
" -- Fetch detailed stats from C side \n "
" local data = _GetItemTooltipData(itemId) \n "
" if data then \n "
" -- Bind type \n "
2026-03-22 20:25:41 -07:00
" if data.isHeroic then self:AddLine('Heroic', 0, 1, 0) end \n "
2026-03-23 01:32:37 -07:00
" if data.isUnique then self:AddLine('Unique', 1, 1, 1) \n "
" elseif data.isUniqueEquipped then self:AddLine('Unique-Equipped', 1, 1, 1) end \n "
feat: add full item stat display to tooltips (armor, damage, stats, bind)
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.
2026-03-22 18:07:58 -07:00
" if data.bindType == 1 then self:AddLine('Binds when picked up', 1, 1, 1) \n "
" elseif data.bindType == 2 then self:AddLine('Binds when equipped', 1, 1, 1) \n "
" elseif data.bindType == 3 then self:AddLine('Binds when used', 1, 1, 1) end \n "
" -- Armor \n "
" if data.armor and data.armor > 0 then \n "
" self:AddLine(data.armor..' Armor', 1, 1, 1) \n "
" end \n "
" -- Weapon damage and speed \n "
" if data.damageMin and data.damageMax and data.damageMin > 0 then \n "
" local speed = (data.speed or 0) / 1000 \n "
" if speed > 0 then \n "
" self:AddDoubleLine(string.format('%.0f - %.0f Damage', data.damageMin, data.damageMax), string.format('Speed %.2f', speed), 1,1,1, 1,1,1) \n "
" local dps = (data.damageMin + data.damageMax) / 2 / speed \n "
" self:AddLine(string.format('(%.1f damage per second)', dps), 1, 1, 1) \n "
" end \n "
" end \n "
" -- Stats \n "
" if data.stamina then self:AddLine('+'..data.stamina..' Stamina', 0, 1, 0) end \n "
" if data.strength then self:AddLine('+'..data.strength..' Strength', 0, 1, 0) end \n "
" if data.agility then self:AddLine('+'..data.agility..' Agility', 0, 1, 0) end \n "
" if data.intellect then self:AddLine('+'..data.intellect..' Intellect', 0, 1, 0) end \n "
" if data.spirit then self:AddLine('+'..data.spirit..' Spirit', 0, 1, 0) end \n "
feat: add combat ratings, resistances to item tooltip display
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.).
2026-03-22 18:13:23 -07:00
" -- Extra stats (hit, crit, haste, AP, SP, etc.) \n "
" if data.extraStats then \n "
" local statNames = {[3]='Agility',[4]='Strength',[5]='Intellect',[6]='Spirit',[7]='Stamina', \n "
" [12]='Defense Rating',[13]='Dodge Rating',[14]='Parry Rating',[15]='Block Rating', \n "
" [16]='Melee Hit Rating',[17]='Ranged Hit Rating',[18]='Spell Hit Rating', \n "
" [19]='Melee Crit Rating',[20]='Ranged Crit Rating',[21]='Spell Crit Rating', \n "
" [28]='Melee Haste Rating',[29]='Ranged Haste Rating',[30]='Spell Haste Rating', \n "
" [31]='Hit Rating',[32]='Crit Rating',[36]='Haste Rating', \n "
" [33]='Resilience Rating',[34]='Attack Power',[35]='Spell Power', \n "
" [37]='Expertise Rating',[38]='Attack Power',[39]='Ranged Attack Power', \n "
" [43]='Mana per 5 sec.',[44]='Armor Penetration Rating', \n "
" [45]='Spell Power',[46]='Health per 5 sec.',[47]='Spell Penetration'} \n "
" for _, stat in ipairs(data.extraStats) do \n "
" local name = statNames[stat.type] \n "
" if name and stat.value ~= 0 then \n "
" local prefix = stat.value > 0 and '+' or '' \n "
" self:AddLine(prefix..stat.value..' '..name, 0, 1, 0) \n "
" end \n "
" end \n "
" end \n "
" -- Resistances \n "
" if data.fireRes and data.fireRes ~= 0 then self:AddLine('+'..data.fireRes..' Fire Resistance', 0, 1, 0) end \n "
" if data.natureRes and data.natureRes ~= 0 then self:AddLine('+'..data.natureRes..' Nature Resistance', 0, 1, 0) end \n "
" if data.frostRes and data.frostRes ~= 0 then self:AddLine('+'..data.frostRes..' Frost Resistance', 0, 1, 0) end \n "
" if data.shadowRes and data.shadowRes ~= 0 then self:AddLine('+'..data.shadowRes..' Shadow Resistance', 0, 1, 0) end \n "
" if data.arcaneRes and data.arcaneRes ~= 0 then self:AddLine('+'..data.arcaneRes..' Arcane Resistance', 0, 1, 0) end \n "
feat: show item spell effects in tooltips (Use/Equip/Chance on Hit)
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.
2026-03-22 20:17:50 -07:00
" -- Item spell effects (Use: / Equip: / Chance on Hit:) \n "
" if data.itemSpells then \n "
" local triggerLabels = {[0]='Use: ',[1]='Equip: ',[2]='Chance on hit: ',[5]=''} \n "
" for _, sp in ipairs(data.itemSpells) do \n "
" local label = triggerLabels[sp.trigger] or '' \n "
" local text = sp.description or sp.name or '' \n "
" if text ~= '' then \n "
" self:AddLine(label .. text, 0, 1, 0) \n "
" end \n "
" end \n "
" end \n "
2026-03-22 19:59:52 -07:00
" -- Gem sockets \n "
" if data.sockets then \n "
" local socketNames = {[1]='Meta',[2]='Red',[4]='Yellow',[8]='Blue'} \n "
" for _, sock in ipairs(data.sockets) do \n "
" local colorName = socketNames[sock.color] or 'Prismatic' \n "
" self:AddLine('[' .. colorName .. ' Socket]', 0.5, 0.5, 0.5) \n "
" end \n "
" end \n "
feat: add full item stat display to tooltips (armor, damage, stats, bind)
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.
2026-03-22 18:07:58 -07:00
" -- Required level \n "
" if data.requiredLevel and data.requiredLevel > 1 then \n "
" self:AddLine('Requires Level '..data.requiredLevel, 1, 1, 1) \n "
" end \n "
" -- Flavor text \n "
" if data.description then self:AddLine(' \" '..data.description..' \" ', 1, 0.82, 0) end \n "
2026-03-23 01:28:28 -07:00
" if data.startsQuest then self:AddLine('This Item Begins a Quest', 1, 0.82, 0) end \n "
feat: add full item stat display to tooltips (armor, damage, stats, bind)
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.
2026-03-22 18:07:58 -07:00
" end \n "
2026-03-22 18:17:21 -07:00
" -- Sell price from GetItemInfo \n "
" if sellPrice and sellPrice > 0 then \n "
" local gold = math.floor(sellPrice / 10000) \n "
" local silver = math.floor((sellPrice % 10000) / 100) \n "
" local copper = sellPrice % 100 \n "
" local parts = {} \n "
" if gold > 0 then table.insert(parts, gold..'g') end \n "
" if silver > 0 then table.insert(parts, silver..'s') end \n "
" if copper > 0 then table.insert(parts, copper..'c') end \n "
" if #parts > 0 then self:AddLine('Sell Price: '..table.concat(parts, ' '), 1, 1, 1) end \n "
" end \n "
2026-03-22 18:02:46 -07:00
" self.__itemId = itemId \n "
" return true \n "
" end \n "
2026-03-22 16:13:39 -07:00
" function GameTooltip:SetInventoryItem(unit, slot) \n "
" self:ClearLines() \n "
" if unit ~= 'player' then return false, false, 0 end \n "
" local link = GetInventoryItemLink(unit, slot) \n "
" if not link then return false, false, 0 end \n "
" local id = link:match('item:(%d+)') \n "
" if not id then return false, false, 0 end \n "
2026-03-22 18:02:46 -07:00
" local ok = _WoweePopulateItemTooltip(self, tonumber(id)) \n "
" return ok or false, false, 0 \n "
2026-03-22 16:13:39 -07:00
" end \n "
" function GameTooltip:SetBagItem(bag, slot) \n "
" self:ClearLines() \n "
" local tex, count, locked, quality, readable, lootable, link = GetContainerItemInfo(bag, slot) \n "
" if not link then return end \n "
" local id = link:match('item:(%d+)') \n "
" if not id then return end \n "
2026-03-22 18:02:46 -07:00
" _WoweePopulateItemTooltip(self, tonumber(id)) \n "
" if count and count > 1 then self:AddLine('Count: '..count, 0.5, 0.5, 0.5) end \n "
2026-03-22 16:13:39 -07:00
" end \n "
" function GameTooltip:SetSpellByID(spellId) \n "
" self:ClearLines() \n "
" if not spellId or spellId == 0 then return end \n "
2026-03-22 18:19:51 -07:00
" local name, rank, icon, castTime, minRange, maxRange = GetSpellInfo(spellId) \n "
2026-03-22 16:13:39 -07:00
" if name then \n "
" self:SetText(name, 1, 1, 1) \n "
" if rank and rank ~= '' then self:AddLine(rank, 0.5, 0.5, 0.5) end \n "
2026-03-22 18:19:51 -07:00
" -- Mana cost \n "
" local cost, costType = GetSpellPowerCost(spellId) \n "
" if cost and cost > 0 then \n "
" local powerNames = {[0]='Mana',[1]='Rage',[2]='Focus',[3]='Energy',[6]='Runic Power'} \n "
" self:AddLine(cost..' '..(powerNames[costType] or 'Mana'), 1, 1, 1) \n "
" end \n "
" -- Range \n "
" if maxRange and maxRange > 0 then \n "
" self:AddDoubleLine(string.format('%.0f yd range', maxRange), '', 1,1,1, 1,1,1) \n "
" end \n "
" -- Cast time \n "
" if castTime and castTime > 0 then \n "
" self:AddDoubleLine(string.format('%.1f sec cast', castTime / 1000), '', 1,1,1, 1,1,1) \n "
" else \n "
" self:AddDoubleLine('Instant', '', 1,1,1, 1,1,1) \n "
" end \n "
2026-03-22 20:03:18 -07:00
" -- Description \n "
" local desc = GetSpellDescription(spellId) \n "
" if desc and desc ~= '' then \n "
" self:AddLine(desc, 1, 0.82, 0) \n "
" end \n "
2026-03-22 18:19:51 -07:00
" -- Cooldown \n "
" local start, dur = GetSpellCooldown(spellId) \n "
" if dur and dur > 0 then \n "
" local rem = start + dur - GetTime() \n "
" if rem > 0.1 then self:AddLine(string.format('%.0f sec cooldown', rem), 1, 0, 0) end \n "
" end \n "
2026-03-22 16:13:39 -07:00
" self.__spellId = spellId \n "
" end \n "
" end \n "
" function GameTooltip:SetAction(slot) \n "
" self:ClearLines() \n "
" if not slot then return end \n "
" local actionType, id = GetActionInfo(slot) \n "
" if actionType == 'spell' and id and id > 0 then \n "
" self:SetSpellByID(id) \n "
" elseif actionType == 'item' and id and id > 0 then \n "
fix: use rich tooltip display for SetAction items and SetHyperlink
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.
2026-03-22 18:22:56 -07:00
" _WoweePopulateItemTooltip(self, id) \n "
2026-03-22 16:13:39 -07:00
" end \n "
" end \n "
2026-03-21 01:52:59 -07:00
" function GameTooltip:FadeOut() end \n "
" function GameTooltip:SetFrameStrata(...) end \n "
" function GameTooltip:SetClampedToScreen(...) end \n "
" function GameTooltip:IsOwned(f) return self.__owner == f end \n "
// ShoppingTooltip: used by comparison tooltips
" ShoppingTooltip1 = CreateFrame('Frame', 'ShoppingTooltip1') \n "
" ShoppingTooltip2 = CreateFrame('Frame', 'ShoppingTooltip2') \n "
feat: add WoW compatibility stubs for broader addon support
Add error handling (geterrorhandler, seterrorhandler, debugstack, securecall,
issecurevariable), CVar system (GetCVar, GetCVarBool, SetCVar), screen/state
queries (GetScreenWidth/Height, GetFramerate, GetNetStats, IsLoggedIn,
IsMounted, IsFlying, etc.), UI stubs (StaticPopup_Show/Hide, StopSound),
and RAID_CLASS_COLORS table. Prevents common addon load errors.
2026-03-20 14:38:50 -07:00
// Error handling stubs (used by many addons)
" local _errorHandler = function(err) return err end \n "
" function geterrorhandler() return _errorHandler end \n "
" function seterrorhandler(fn) if type(fn)=='function' then _errorHandler=fn end end \n "
" function debugstack(start, count1, count2) return '' end \n "
" function securecall(fn, ...) if type(fn)=='function' then return fn(...) end end \n "
" function issecurevariable(...) return false end \n "
" function issecure() return false end \n "
2026-03-22 16:13:39 -07:00
// GetCVarBool wraps C-side GetCVar (registered in table) for boolean queries
" function GetCVarBool(name) return GetCVar(name) == '1' end \n "
feat: add WoW compatibility stubs for broader addon support
Add error handling (geterrorhandler, seterrorhandler, debugstack, securecall,
issecurevariable), CVar system (GetCVar, GetCVarBool, SetCVar), screen/state
queries (GetScreenWidth/Height, GetFramerate, GetNetStats, IsLoggedIn,
IsMounted, IsFlying, etc.), UI stubs (StaticPopup_Show/Hide, StopSound),
and RAID_CLASS_COLORS table. Prevents common addon load errors.
2026-03-20 14:38:50 -07:00
// Misc compatibility stubs
2026-03-21 01:52:59 -07:00
// GetScreenWidth, GetScreenHeight, GetNumLootItems are now C functions
// GetFramerate is now a C function
feat: add WoW compatibility stubs for broader addon support
Add error handling (geterrorhandler, seterrorhandler, debugstack, securecall,
issecurevariable), CVar system (GetCVar, GetCVarBool, SetCVar), screen/state
queries (GetScreenWidth/Height, GetFramerate, GetNetStats, IsLoggedIn,
IsMounted, IsFlying, etc.), UI stubs (StaticPopup_Show/Hide, StopSound),
and RAID_CLASS_COLORS table. Prevents common addon load errors.
2026-03-20 14:38:50 -07:00
" function GetNetStats() return 0, 0, 0, 0 end \n "
" function IsLoggedIn() return true end \n "
" function StaticPopup_Show() end \n "
" function StaticPopup_Hide() end \n "
2026-03-22 16:33:57 -07:00
// UI Panel management — Show/Hide standard WoW panels
" UIPanelWindows = {} \n "
" function ShowUIPanel(frame, force) \n "
" if frame and frame.Show then frame:Show() end \n "
" end \n "
" function HideUIPanel(frame) \n "
" if frame and frame.Hide then frame:Hide() end \n "
" end \n "
" function ToggleFrame(frame) \n "
" if frame then \n "
" if frame:IsShown() then frame:Hide() else frame:Show() end \n "
" end \n "
" end \n "
" function GetUIPanel(which) return nil end \n "
" function CloseWindows(ignoreCenter) return false end \n "
// TEXT localization stub — returns input string unchanged
" function TEXT(text) return text end \n "
// Faux scroll frame helpers (used by many list UIs)
" function FauxScrollFrame_GetOffset(frame) \n "
" return frame and frame.offset or 0 \n "
" end \n "
" function FauxScrollFrame_Update(frame, numItems, numVisible, valueStep, button, smallWidth, bigWidth, highlightFrame, smallHighlightWidth, bigHighlightWidth) \n "
" if not frame then return false end \n "
" frame.offset = frame.offset or 0 \n "
" local showScrollBar = numItems > numVisible \n "
" return showScrollBar \n "
" end \n "
" function FauxScrollFrame_SetOffset(frame, offset) \n "
" if frame then frame.offset = offset or 0 end \n "
" end \n "
" function FauxScrollFrame_OnVerticalScroll(frame, value, itemHeight, updateFunction) \n "
" if not frame then return end \n "
" frame.offset = math.floor(value / (itemHeight or 1) + 0.5) \n "
" if updateFunction then updateFunction() end \n "
" end \n "
// SecureCmdOptionParse — parses conditional macros like [target=focus]
" function SecureCmdOptionParse(options) \n "
" if not options then return nil end \n "
" -- Simple: return the unconditional fallback (text after last semicolon or the whole string) \n "
" local result = options:match(';%s*(.-)$') or options:match('^%[.*%]%s*(.-)$') or options \n "
" return result \n "
" end \n "
// ChatFrame message group stubs
" function ChatFrame_AddMessageGroup(frame, group) end \n "
" function ChatFrame_RemoveMessageGroup(frame, group) end \n "
" function ChatFrame_AddChannel(frame, channel) end \n "
" function ChatFrame_RemoveChannel(frame, channel) end \n "
2026-03-21 01:52:59 -07:00
// CreateTexture/CreateFontString are now C frame methods in the metatable
2026-03-21 10:02:34 -07:00
" do \n "
" local function cc(r,g,b) \n "
" local t = {r=r, g=g, b=b} \n "
" t.colorStr = string.format('%02x%02x%02x', math.floor(r*255), math.floor(g*255), math.floor(b*255)) \n "
" function t:GenerateHexColor() return '|cff' .. self.colorStr end \n "
" function t:GenerateHexColorMarkup() return '|cff' .. self.colorStr end \n "
" return t \n "
" end \n "
" RAID_CLASS_COLORS = { \n "
" WARRIOR=cc(0.78,0.61,0.43), PALADIN=cc(0.96,0.55,0.73), \n "
" HUNTER=cc(0.67,0.83,0.45), ROGUE=cc(1.0,0.96,0.41), \n "
" PRIEST=cc(1.0,1.0,1.0), DEATHKNIGHT=cc(0.77,0.12,0.23), \n "
" SHAMAN=cc(0.0,0.44,0.87), MAGE=cc(0.41,0.80,0.94), \n "
" WARLOCK=cc(0.58,0.51,0.79), DRUID=cc(1.0,0.49,0.04), \n "
" } \n "
" end \n "
feat: add GetClassColor and QuestDifficultyColors for UI coloring
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.
2026-03-22 21:05:33 -07:00
// GetClassColor(className) — returns r, g, b, colorString
" function GetClassColor(className) \n "
" local c = RAID_CLASS_COLORS[className] \n "
" if c then return c.r, c.g, c.b, c.colorStr end \n "
" return 1, 1, 1, 'ffffffff' \n "
" end \n "
// QuestDifficultyColors table for quest level coloring
" QuestDifficultyColors = { \n "
" impossible = {r=1.0,g=0.1,b=0.1,font='QuestDifficulty_Impossible'}, \n "
" verydifficult = {r=1.0,g=0.5,b=0.25,font='QuestDifficulty_VeryDifficult'}, \n "
" difficult = {r=1.0,g=1.0,b=0.0,font='QuestDifficulty_Difficult'}, \n "
" standard = {r=0.25,g=0.75,b=0.25,font='QuestDifficulty_Standard'}, \n "
" trivial = {r=0.5,g=0.5,b=0.5,font='QuestDifficulty_Trivial'}, \n "
" header = {r=1.0,g=0.82,b=0.0,font='QuestDifficulty_Header'}, \n "
" } \n "
2026-03-20 20:44:59 -07:00
// Money formatting utility
" function GetCoinTextureString(copper) \n "
" if not copper or copper == 0 then return '0c' end \n "
" copper = math.floor(copper) \n "
" local g = math.floor(copper / 10000) \n "
" local s = math.floor(math.fmod(copper, 10000) / 100) \n "
" local c = math.fmod(copper, 100) \n "
" local r = '' \n "
" if g > 0 then r = r .. g .. 'g ' end \n "
" if s > 0 then r = r .. s .. 's ' end \n "
" if c > 0 or r == '' then r = r .. c .. 'c' end \n "
" return r \n "
" end \n "
" GetCoinText = GetCoinTextureString \n "
2026-03-20 13:27:27 -07:00
) ;
feat: add UIDropDownMenu framework, font objects, and UI global stubs
Add the UIDropDownMenu compatibility framework used by virtually every
addon with settings or selection menus: UIDropDownMenu_Initialize,
CreateInfo, AddButton, SetWidth, SetText, GetText, SetSelectedID, etc.
Add global font object stubs (GameFontNormal, GameFontHighlight, etc.)
referenced by CreateFontString template arguments.
Add UISpecialFrames table, InterfaceOptionsFrame for addon panels,
InterfaceOptions_AddCategory, and common font color constants
(GRAY_FONT_COLOR, NORMAL_FONT_COLOR, etc.).
These globals prevent nil-reference errors in most popular addons.
2026-03-21 02:36:06 -07:00
// UIDropDownMenu framework — minimal compat for addons using dropdown menus
luaL_dostring ( L_ ,
" UIDROPDOWNMENU_MENU_LEVEL = 1 \n "
" UIDROPDOWNMENU_MENU_VALUE = nil \n "
" UIDROPDOWNMENU_OPEN_MENU = nil \n "
" local _ddMenuList = {} \n "
" function UIDropDownMenu_Initialize(frame, initFunc, displayMode, level, menuList) \n "
" if frame then frame.__initFunc = initFunc end \n "
" end \n "
" function UIDropDownMenu_CreateInfo() return {} end \n "
" function UIDropDownMenu_AddButton(info, level) table.insert(_ddMenuList, info) end \n "
" function UIDropDownMenu_SetWidth(frame, width) end \n "
" function UIDropDownMenu_SetButtonWidth(frame, width) end \n "
" function UIDropDownMenu_SetText(frame, text) \n "
" if frame then frame.__text = text end \n "
" end \n "
" function UIDropDownMenu_GetText(frame) \n "
" return frame and frame.__text or '' \n "
" end \n "
" function UIDropDownMenu_SetSelectedID(frame, id) end \n "
" function UIDropDownMenu_SetSelectedValue(frame, value) end \n "
" function UIDropDownMenu_GetSelectedID(frame) return 1 end \n "
" function UIDropDownMenu_GetSelectedValue(frame) return nil end \n "
" function UIDropDownMenu_JustifyText(frame, justify) end \n "
" function UIDropDownMenu_EnableDropDown(frame) end \n "
" function UIDropDownMenu_DisableDropDown(frame) end \n "
" function CloseDropDownMenus() end \n "
" function ToggleDropDownMenu(level, value, frame, anchor, xOfs, yOfs) end \n "
) ;
// UISpecialFrames: frames in this list close on Escape key
luaL_dostring ( L_ ,
" UISpecialFrames = {} \n "
// Font object stubs — addons reference these for CreateFontString templates
" GameFontNormal = {} \n "
" GameFontNormalSmall = {} \n "
" GameFontNormalLarge = {} \n "
" GameFontHighlight = {} \n "
" GameFontHighlightSmall = {} \n "
" GameFontHighlightLarge = {} \n "
" GameFontDisable = {} \n "
" GameFontDisableSmall = {} \n "
" GameFontWhite = {} \n "
" GameFontRed = {} \n "
" GameFontGreen = {} \n "
" NumberFontNormal = {} \n "
" ChatFontNormal = {} \n "
" SystemFont = {} \n "
// InterfaceOptionsFrame: addons register settings panels here
" InterfaceOptionsFrame = CreateFrame('Frame', 'InterfaceOptionsFrame') \n "
" InterfaceOptionsFramePanelContainer = CreateFrame('Frame', 'InterfaceOptionsFramePanelContainer') \n "
" function InterfaceOptions_AddCategory(panel) end \n "
" function InterfaceOptionsFrame_OpenToCategory(panel) end \n "
// Commonly expected global tables
" SLASH_RELOAD1 = '/reload' \n "
" SLASH_RELOADUI1 = '/reloadui' \n "
" GRAY_FONT_COLOR = {r=0.5,g=0.5,b=0.5} \n "
" NORMAL_FONT_COLOR = {r=1.0,g=0.82,b=0.0} \n "
" HIGHLIGHT_FONT_COLOR = {r=1.0,g=1.0,b=1.0} \n "
" GREEN_FONT_COLOR = {r=0.1,g=1.0,b=0.1} \n "
" RED_FONT_COLOR = {r=1.0,g=0.1,b=0.1} \n "
feat: add SendAddonMessage and RegisterAddonMessagePrefix for addon comms
Implement the addon messaging API used by virtually every multiplayer
addon (DBM, BigWigs, EPGP, RC Loot Council, WeakAuras, etc.):
- SendAddonMessage(prefix, text, chatType, target) sends an addon
message encoded as "prefix\ttext" via the appropriate chat channel
- RegisterAddonMessagePrefix(prefix) registers a prefix for filtering
incoming addon messages
- IsAddonMessagePrefixRegistered(prefix) checks registration status
- C_ChatInfo table with aliases for the above functions (newer API compat)
Without these functions, all inter-addon communication between players
fails, breaking boss mods, loot distribution, and group coordination.
2026-03-21 03:31:54 -07:00
// C_ChatInfo — addon message prefix API used by some addons
" C_ChatInfo = C_ChatInfo or {} \n "
" C_ChatInfo.RegisterAddonMessagePrefix = RegisterAddonMessagePrefix \n "
" C_ChatInfo.IsAddonMessagePrefixRegistered = IsAddonMessagePrefixRegistered \n "
" C_ChatInfo.SendAddonMessage = SendAddonMessage \n "
feat: add UIDropDownMenu framework, font objects, and UI global stubs
Add the UIDropDownMenu compatibility framework used by virtually every
addon with settings or selection menus: UIDropDownMenu_Initialize,
CreateInfo, AddButton, SetWidth, SetText, GetText, SetSelectedID, etc.
Add global font object stubs (GameFontNormal, GameFontHighlight, etc.)
referenced by CreateFontString template arguments.
Add UISpecialFrames table, InterfaceOptionsFrame for addon panels,
InterfaceOptions_AddCategory, and common font color constants
(GRAY_FONT_COLOR, NORMAL_FONT_COLOR, etc.).
These globals prevent nil-reference errors in most popular addons.
2026-03-21 02:36:06 -07:00
) ;
feat: add WoW table/string/math/bit utility functions for addon compat
Add commonly used WoW global utility functions that many addons depend on:
Table: tContains, tInvert, CopyTable, tDeleteItem
String: strupper, strlower, strfind, strsub, strlen, strrep, strbyte,
strchar, strrev, gsub, gmatch, strjoin
Math: Clamp, Round
Bit ops: bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift
(pure Lua implementation for Lua 5.1 which lacks native bit ops)
These prevent nil-reference errors and missing-function crashes in
addons that use standard WoW utility globals.
2026-03-21 02:37:56 -07:00
feat: add action bar constants and functions for Bartender/Dominos compat
Add essential WoW action bar globals and functions that action bar
addons (Bartender4, Dominos, CT_BarMod) require on initialization:
Constants: NUM_ACTIONBAR_BUTTONS, NUM_ACTIONBAR_PAGES, NUM_PET_ACTION_SLOTS
Functions: GetActionBarPage, ChangeActionBarPage, GetBonusBarOffset,
GetActionText, GetActionCount
Binding: GetBindingKey, GetBindingAction, SetBinding, SaveBindings
Macro: GetNumMacros, GetMacroInfo, GetMacroBody, GetMacroIndexByName
Stance: GetNumShapeshiftForms, GetShapeshiftFormInfo
Pet: GetPetActionInfo, GetPetActionsUsable
These prevent nil-reference errors during addon initialization and
enable basic action bar addon functionality.
2026-03-21 04:48:06 -07:00
// Action bar constants and functions used by action bar addons
luaL_dostring ( L_ ,
" NUM_ACTIONBAR_BUTTONS = 12 \n "
" NUM_ACTIONBAR_PAGES = 6 \n "
" ACTION_BUTTON_SHOW_GRID_REASON_CVAR = 1 \n "
" ACTION_BUTTON_SHOW_GRID_REASON_EVENT = 2 \n "
// Action bar page tracking
" local _actionBarPage = 1 \n "
" function GetActionBarPage() return _actionBarPage end \n "
" function ChangeActionBarPage(page) _actionBarPage = page end \n "
" function GetBonusBarOffset() return 0 end \n "
// Action type query
" function GetActionText(slot) return nil end \n "
" function GetActionCount(slot) return 0 end \n "
// Binding functions
" function GetBindingKey(action) return nil end \n "
" function GetBindingAction(key) return nil end \n "
" function SetBinding(key, action) end \n "
" function SaveBindings(which) end \n "
" function GetCurrentBindingSet() return 1 end \n "
// Macro functions
" function GetNumMacros() return 0, 0 end \n "
" function GetMacroInfo(id) return nil end \n "
" function GetMacroBody(id) return nil end \n "
" function GetMacroIndexByName(name) return 0 end \n "
// Stance bar
" function GetNumShapeshiftForms() return 0 end \n "
" function GetShapeshiftFormInfo(index) return nil, nil, nil, nil end \n "
// Pet action bar
" NUM_PET_ACTION_SLOTS = 10 \n "
2026-03-21 07:58:38 -07:00
// Common WoW constants used by many addons
" MAX_TALENT_TABS = 3 \n "
" MAX_NUM_TALENTS = 100 \n "
" BOOKTYPE_SPELL = 0 \n "
" BOOKTYPE_PET = 1 \n "
" MAX_PARTY_MEMBERS = 4 \n "
" MAX_RAID_MEMBERS = 40 \n "
" MAX_ARENA_TEAMS = 3 \n "
" INVSLOT_FIRST_EQUIPPED = 1 \n "
" INVSLOT_LAST_EQUIPPED = 19 \n "
" NUM_BAG_SLOTS = 4 \n "
" NUM_BANKBAGSLOTS = 7 \n "
" CONTAINER_BAG_OFFSET = 0 \n "
" MAX_SKILLLINE_TABS = 8 \n "
" TRADE_ENCHANT_SLOT = 7 \n "
feat: add action bar constants and functions for Bartender/Dominos compat
Add essential WoW action bar globals and functions that action bar
addons (Bartender4, Dominos, CT_BarMod) require on initialization:
Constants: NUM_ACTIONBAR_BUTTONS, NUM_ACTIONBAR_PAGES, NUM_PET_ACTION_SLOTS
Functions: GetActionBarPage, ChangeActionBarPage, GetBonusBarOffset,
GetActionText, GetActionCount
Binding: GetBindingKey, GetBindingAction, SetBinding, SaveBindings
Macro: GetNumMacros, GetMacroInfo, GetMacroBody, GetMacroIndexByName
Stance: GetNumShapeshiftForms, GetShapeshiftFormInfo
Pet: GetPetActionInfo, GetPetActionsUsable
These prevent nil-reference errors during addon initialization and
enable basic action bar addon functionality.
2026-03-21 04:48:06 -07:00
" function GetPetActionInfo(slot) return nil end \n "
" function GetPetActionsUsable() return false end \n "
) ;
feat: add WoW table/string/math/bit utility functions for addon compat
Add commonly used WoW global utility functions that many addons depend on:
Table: tContains, tInvert, CopyTable, tDeleteItem
String: strupper, strlower, strfind, strsub, strlen, strrep, strbyte,
strchar, strrev, gsub, gmatch, strjoin
Math: Clamp, Round
Bit ops: bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift
(pure Lua implementation for Lua 5.1 which lacks native bit ops)
These prevent nil-reference errors and missing-function crashes in
addons that use standard WoW utility globals.
2026-03-21 02:37:56 -07:00
// WoW table/string utility functions used by many addons
luaL_dostring ( L_ ,
// Table utilities
" function tContains(tbl, item) \n "
" for _, v in pairs(tbl) do if v == item then return true end end \n "
" return false \n "
" end \n "
" function tInvert(tbl) \n "
" local inv = {} \n "
" for k, v in pairs(tbl) do inv[v] = k end \n "
" return inv \n "
" end \n "
" function CopyTable(src) \n "
" if type(src) ~= 'table' then return src end \n "
" local copy = {} \n "
" for k, v in pairs(src) do copy[k] = CopyTable(v) end \n "
" return setmetatable(copy, getmetatable(src)) \n "
" end \n "
" function tDeleteItem(tbl, item) \n "
" for i = #tbl, 1, -1 do if tbl[i] == item then table.remove(tbl, i) end end \n "
" end \n "
2026-03-22 21:23:00 -07:00
// Mixin pattern — used by modern addons for OOP-style object creation
" function Mixin(obj, ...) \n "
" for i = 1, select('#', ...) do \n "
" local mixin = select(i, ...) \n "
" for k, v in pairs(mixin) do obj[k] = v end \n "
" end \n "
" return obj \n "
" end \n "
" function CreateFromMixins(...) \n "
" return Mixin({}, ...) \n "
" end \n "
" function CreateAndInitFromMixin(mixin, ...) \n "
" local obj = CreateFromMixins(mixin) \n "
" if obj.Init then obj:Init(...) end \n "
" return obj \n "
" end \n "
" function MergeTable(dest, src) \n "
" for k, v in pairs(src) do dest[k] = v end \n "
" return dest \n "
" end \n "
feat: add WoW table/string/math/bit utility functions for addon compat
Add commonly used WoW global utility functions that many addons depend on:
Table: tContains, tInvert, CopyTable, tDeleteItem
String: strupper, strlower, strfind, strsub, strlen, strrep, strbyte,
strchar, strrev, gsub, gmatch, strjoin
Math: Clamp, Round
Bit ops: bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift
(pure Lua implementation for Lua 5.1 which lacks native bit ops)
These prevent nil-reference errors and missing-function crashes in
addons that use standard WoW utility globals.
2026-03-21 02:37:56 -07:00
// String utilities (WoW globals that alias Lua string functions)
" strupper = string.upper \n "
" strlower = string.lower \n "
" strfind = string.find \n "
" strsub = string.sub \n "
" strlen = string.len \n "
" strrep = string.rep \n "
" strbyte = string.byte \n "
" strchar = string.char \n "
2026-03-22 21:17:59 -07:00
" strgfind = string.gmatch \n "
" function tostringall(...) \n "
" local n = select('#', ...) \n "
" if n == 0 then return end \n "
" local r = {} \n "
" for i = 1, n do r[i] = tostring(select(i, ...)) end \n "
" return unpack(r, 1, n) \n "
" end \n "
feat: add WoW table/string/math/bit utility functions for addon compat
Add commonly used WoW global utility functions that many addons depend on:
Table: tContains, tInvert, CopyTable, tDeleteItem
String: strupper, strlower, strfind, strsub, strlen, strrep, strbyte,
strchar, strrev, gsub, gmatch, strjoin
Math: Clamp, Round
Bit ops: bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift
(pure Lua implementation for Lua 5.1 which lacks native bit ops)
These prevent nil-reference errors and missing-function crashes in
addons that use standard WoW utility globals.
2026-03-21 02:37:56 -07:00
" strrev = string.reverse \n "
" gsub = string.gsub \n "
" gmatch = string.gmatch \n "
" strjoin = function(delim, ...) \n "
" return table.concat({...}, delim) \n "
" end \n "
// Math utilities
" function Clamp(val, lo, hi) return math.min(math.max(val, lo), hi) end \n "
" function Round(val) return math.floor(val + 0.5) end \n "
// Bit operations (WoW provides these; Lua 5.1 doesn't have native bit ops)
" bit = bit or {} \n "
" bit.band = bit.band or function(a, b) local r,m=0,1 for i=0,31 do if a%2==1 and b%2==1 then r=r+m end a=math.floor(a/2) b=math.floor(b/2) m=m*2 end return r end \n "
" bit.bor = bit.bor or function(a, b) local r,m=0,1 for i=0,31 do if a%2==1 or b%2==1 then r=r+m end a=math.floor(a/2) b=math.floor(b/2) m=m*2 end return r end \n "
" bit.bxor = bit.bxor or function(a, b) local r,m=0,1 for i=0,31 do if (a%2==1)~=(b%2==1) then r=r+m end a=math.floor(a/2) b=math.floor(b/2) m=m*2 end return r end \n "
" bit.bnot = bit.bnot or function(a) return 4294967295 - a end \n "
" bit.lshift = bit.lshift or function(a, n) return a * (2^n) end \n "
" bit.rshift = bit.rshift or function(a, n) return math.floor(a / (2^n)) end \n "
) ;
2026-03-20 11:12:07 -07:00
}
2026-03-20 11:23:38 -07:00
// ---- Event System ----
// Lua-side: WoweeEvents table holds { ["EVENT_NAME"] = { handler1, handler2, ... } }
// RegisterEvent("EVENT", handler) adds a handler function
// UnregisterEvent("EVENT", handler) removes it
`chore(lua): refactor addon Lua engine API + progress docs`
- Refactor Lua addon integration:
- Update CMakeLists.txt for addon build paths
- Enhance addons API headers and Lua engine interface
- Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
- Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
2026-03-20 11:23:38 -07:00
static int lua_RegisterEvent ( lua_State * L ) {
const char * eventName = luaL_checkstring ( L , 1 ) ;
luaL_checktype ( L , 2 , LUA_TFUNCTION ) ;
// Get or create the WoweeEvents table
lua_getglobal ( L , " __WoweeEvents " ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_newtable ( L ) ;
lua_pushvalue ( L , - 1 ) ;
lua_setglobal ( L , " __WoweeEvents " ) ;
}
// Get or create the handler list for this event
lua_getfield ( L , - 1 , eventName ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_newtable ( L ) ;
lua_pushvalue ( L , - 1 ) ;
lua_setfield ( L , - 3 , eventName ) ;
}
// Append the handler function to the list
int len = static_cast < int > ( lua_objlen ( L , - 1 ) ) ;
lua_pushvalue ( L , 2 ) ; // push the handler function
lua_rawseti ( L , - 2 , len + 1 ) ;
lua_pop ( L , 2 ) ; // pop handler list + WoweeEvents
return 0 ;
}
static int lua_UnregisterEvent ( lua_State * L ) {
const char * eventName = luaL_checkstring ( L , 1 ) ;
luaL_checktype ( L , 2 , LUA_TFUNCTION ) ;
lua_getglobal ( L , " __WoweeEvents " ) ;
if ( lua_isnil ( L , - 1 ) ) { lua_pop ( L , 1 ) ; return 0 ; }
lua_getfield ( L , - 1 , eventName ) ;
if ( lua_isnil ( L , - 1 ) ) { lua_pop ( L , 2 ) ; return 0 ; }
// Remove matching handler from the list
int len = static_cast < int > ( lua_objlen ( L , - 1 ) ) ;
for ( int i = 1 ; i < = len ; i + + ) {
lua_rawgeti ( L , - 1 , i ) ;
if ( lua_rawequal ( L , - 1 , 2 ) ) {
lua_pop ( L , 1 ) ;
// Shift remaining elements down
for ( int j = i ; j < len ; j + + ) {
lua_rawgeti ( L , - 1 , j + 1 ) ;
lua_rawseti ( L , - 2 , j ) ;
}
lua_pushnil ( L ) ;
lua_rawseti ( L , - 2 , len ) ;
break ;
}
lua_pop ( L , 1 ) ;
}
lua_pop ( L , 2 ) ;
return 0 ;
}
void LuaEngine : : registerEventAPI ( ) {
lua_pushcfunction ( L_ , lua_RegisterEvent ) ;
lua_setglobal ( L_ , " RegisterEvent " ) ;
lua_pushcfunction ( L_ , lua_UnregisterEvent ) ;
lua_setglobal ( L_ , " UnregisterEvent " ) ;
// Create the events table
lua_newtable ( L_ ) ;
lua_setglobal ( L_ , " __WoweeEvents " ) ;
}
void LuaEngine : : fireEvent ( const std : : string & eventName ,
const std : : vector < std : : string > & args ) {
if ( ! L_ ) return ;
lua_getglobal ( L_ , " __WoweeEvents " ) ;
if ( lua_isnil ( L_ , - 1 ) ) { lua_pop ( L_ , 1 ) ; return ; }
lua_getfield ( L_ , - 1 , eventName . c_str ( ) ) ;
if ( lua_isnil ( L_ , - 1 ) ) { lua_pop ( L_ , 2 ) ; return ; }
int handlerCount = static_cast < int > ( lua_objlen ( L_ , - 1 ) ) ;
for ( int i = 1 ; i < = handlerCount ; i + + ) {
lua_rawgeti ( L_ , - 1 , i ) ;
if ( ! lua_isfunction ( L_ , - 1 ) ) { lua_pop ( L_ , 1 ) ; continue ; }
// Push arguments: event name first, then extra args
lua_pushstring ( L_ , eventName . c_str ( ) ) ;
for ( const auto & arg : args ) {
lua_pushstring ( L_ , arg . c_str ( ) ) ;
}
int nargs = 1 + static_cast < int > ( args . size ( ) ) ;
if ( lua_pcall ( L_ , nargs , 0 , 0 ) ! = 0 ) {
const char * err = lua_tostring ( L_ , - 1 ) ;
2026-03-21 06:00:06 -07:00
std : : string errStr = err ? err : " (unknown) " ;
LOG_ERROR ( " LuaEngine: event ' " , eventName , " ' handler error: " , errStr ) ;
if ( luaErrorCallback_ ) luaErrorCallback_ ( errStr ) ;
2026-03-20 11:23:38 -07:00
lua_pop ( L_ , 1 ) ;
}
}
lua_pop ( L_ , 2 ) ; // pop handler list + WoweeEvents
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
// Also dispatch to frames that registered for this event via frame:RegisterEvent()
lua_getglobal ( L_ , " __WoweeFrameEvents " ) ;
if ( lua_istable ( L_ , - 1 ) ) {
lua_getfield ( L_ , - 1 , eventName . c_str ( ) ) ;
if ( lua_istable ( L_ , - 1 ) ) {
int frameCount = static_cast < int > ( lua_objlen ( L_ , - 1 ) ) ;
for ( int i = 1 ; i < = frameCount ; i + + ) {
lua_rawgeti ( L_ , - 1 , i ) ;
if ( ! lua_istable ( L_ , - 1 ) ) { lua_pop ( L_ , 1 ) ; continue ; }
// Get the frame's OnEvent script
lua_getfield ( L_ , - 1 , " __scripts " ) ;
if ( lua_istable ( L_ , - 1 ) ) {
lua_getfield ( L_ , - 1 , " OnEvent " ) ;
if ( lua_isfunction ( L_ , - 1 ) ) {
lua_pushvalue ( L_ , - 3 ) ; // self (frame)
lua_pushstring ( L_ , eventName . c_str ( ) ) ;
for ( const auto & arg : args ) lua_pushstring ( L_ , arg . c_str ( ) ) ;
int nargs = 2 + static_cast < int > ( args . size ( ) ) ;
if ( lua_pcall ( L_ , nargs , 0 , 0 ) ! = 0 ) {
2026-03-21 06:00:06 -07:00
const char * ferr = lua_tostring ( L_ , - 1 ) ;
std : : string ferrStr = ferr ? ferr : " (unknown) " ;
LOG_ERROR ( " LuaEngine: frame OnEvent error: " , ferrStr ) ;
if ( luaErrorCallback_ ) luaErrorCallback_ ( ferrStr ) ;
feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
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)
2026-03-20 11:46:04 -07:00
lua_pop ( L_ , 1 ) ;
}
} else {
lua_pop ( L_ , 1 ) ; // pop non-function
}
}
lua_pop ( L_ , 2 ) ; // pop __scripts + frame
}
}
lua_pop ( L_ , 1 ) ; // pop event frame list
}
lua_pop ( L_ , 1 ) ; // pop __WoweeFrameEvents
2026-03-20 11:23:38 -07:00
}
2026-03-20 12:07:22 -07:00
void LuaEngine : : dispatchOnUpdate ( float elapsed ) {
if ( ! L_ ) return ;
lua_getglobal ( L_ , " __WoweeOnUpdateFrames " ) ;
if ( ! lua_istable ( L_ , - 1 ) ) { lua_pop ( L_ , 1 ) ; return ; }
int count = static_cast < int > ( lua_objlen ( L_ , - 1 ) ) ;
for ( int i = 1 ; i < = count ; i + + ) {
lua_rawgeti ( L_ , - 1 , i ) ;
if ( ! lua_istable ( L_ , - 1 ) ) { lua_pop ( L_ , 1 ) ; continue ; }
// Check if frame is visible
lua_getfield ( L_ , - 1 , " __visible " ) ;
bool visible = lua_toboolean ( L_ , - 1 ) ;
lua_pop ( L_ , 1 ) ;
if ( ! visible ) { lua_pop ( L_ , 1 ) ; continue ; }
// Get OnUpdate script
lua_getfield ( L_ , - 1 , " __scripts " ) ;
if ( lua_istable ( L_ , - 1 ) ) {
lua_getfield ( L_ , - 1 , " OnUpdate " ) ;
if ( lua_isfunction ( L_ , - 1 ) ) {
lua_pushvalue ( L_ , - 3 ) ; // self (frame)
lua_pushnumber ( L_ , static_cast < double > ( elapsed ) ) ;
if ( lua_pcall ( L_ , 2 , 0 , 0 ) ! = 0 ) {
2026-03-21 06:08:17 -07:00
const char * uerr = lua_tostring ( L_ , - 1 ) ;
std : : string uerrStr = uerr ? uerr : " (unknown) " ;
LOG_ERROR ( " LuaEngine: OnUpdate error: " , uerrStr ) ;
if ( luaErrorCallback_ ) luaErrorCallback_ ( uerrStr ) ;
2026-03-20 12:07:22 -07:00
lua_pop ( L_ , 1 ) ;
}
} else {
lua_pop ( L_ , 1 ) ;
}
}
lua_pop ( L_ , 2 ) ; // pop __scripts + frame
}
lua_pop ( L_ , 1 ) ; // pop __WoweeOnUpdateFrames
}
2026-03-20 11:40:58 -07:00
bool LuaEngine : : dispatchSlashCommand ( const std : : string & command , const std : : string & args ) {
if ( ! L_ ) return false ;
// Check each SlashCmdList entry: for key NAME, check SLASH_NAME1, SLASH_NAME2, etc.
lua_getglobal ( L_ , " SlashCmdList " ) ;
if ( ! lua_istable ( L_ , - 1 ) ) { lua_pop ( L_ , 1 ) ; return false ; }
std : : string cmdLower = command ;
2026-03-25 11:40:49 -07:00
toLowerInPlace ( cmdLower ) ;
2026-03-20 11:40:58 -07:00
lua_pushnil ( L_ ) ;
while ( lua_next ( L_ , - 2 ) ! = 0 ) {
// Stack: SlashCmdList, key, handler
if ( ! lua_isfunction ( L_ , - 1 ) | | ! lua_isstring ( L_ , - 2 ) ) {
lua_pop ( L_ , 1 ) ;
continue ;
}
const char * name = lua_tostring ( L_ , - 2 ) ;
// Check SLASH_<NAME>1 through SLASH_<NAME>9
for ( int i = 1 ; i < = 9 ; i + + ) {
std : : string globalName = " SLASH_ " + std : : string ( name ) + std : : to_string ( i ) ;
lua_getglobal ( L_ , globalName . c_str ( ) ) ;
if ( lua_isstring ( L_ , - 1 ) ) {
std : : string slashStr = lua_tostring ( L_ , - 1 ) ;
2026-03-25 11:40:49 -07:00
toLowerInPlace ( slashStr ) ;
2026-03-20 11:40:58 -07:00
if ( slashStr = = cmdLower ) {
lua_pop ( L_ , 1 ) ; // pop global
// Call the handler with args
lua_pushvalue ( L_ , - 1 ) ; // copy handler
lua_pushstring ( L_ , args . c_str ( ) ) ;
if ( lua_pcall ( L_ , 1 , 0 , 0 ) ! = 0 ) {
LOG_ERROR ( " LuaEngine: SlashCmdList[' " , name , " '] error: " ,
lua_tostring ( L_ , - 1 ) ) ;
lua_pop ( L_ , 1 ) ;
}
lua_pop ( L_ , 3 ) ; // pop handler, key, SlashCmdList
return true ;
}
}
lua_pop ( L_ , 1 ) ; // pop global
}
lua_pop ( L_ , 1 ) ; // pop handler, keep key for next iteration
}
lua_pop ( L_ , 1 ) ; // pop SlashCmdList
return false ;
}
2026-03-20 12:22:50 -07:00
// ---- SavedVariables serialization ----
static void serializeLuaValue ( lua_State * L , int idx , std : : string & out , int indent ) ;
static void serializeLuaTable ( lua_State * L , int idx , std : : string & out , int indent ) {
out + = " { \n " ;
std : : string pad ( indent + 2 , ' ' ) ;
lua_pushnil ( L ) ;
while ( lua_next ( L , idx ) ! = 0 ) {
out + = pad ;
// Key
if ( lua_type ( L , - 2 ) = = LUA_TSTRING ) {
const char * k = lua_tostring ( L , - 2 ) ;
out + = " [ \" " ;
for ( const char * p = k ; * p ; + + p ) {
if ( * p = = ' " ' | | * p = = ' \\ ' ) out + = ' \\ ' ;
out + = * p ;
}
out + = " \" ] = " ;
} else if ( lua_type ( L , - 2 ) = = LUA_TNUMBER ) {
out + = " [ " + std : : to_string ( static_cast < long long > ( lua_tonumber ( L , - 2 ) ) ) + " ] = " ;
} else {
lua_pop ( L , 1 ) ;
continue ;
}
// Value
serializeLuaValue ( L , lua_gettop ( L ) , out , indent + 2 ) ;
out + = " , \n " ;
lua_pop ( L , 1 ) ;
}
out + = std : : string ( indent , ' ' ) + " } " ;
}
static void serializeLuaValue ( lua_State * L , int idx , std : : string & out , int indent ) {
switch ( lua_type ( L , idx ) ) {
case LUA_TNIL : out + = " nil " ; break ;
case LUA_TBOOLEAN : out + = lua_toboolean ( L , idx ) ? " true " : " false " ; break ;
case LUA_TNUMBER : {
double v = lua_tonumber ( L , idx ) ;
char buf [ 64 ] ;
snprintf ( buf , sizeof ( buf ) , " %.17g " , v ) ;
out + = buf ;
break ;
}
case LUA_TSTRING : {
const char * s = lua_tostring ( L , idx ) ;
out + = " \" " ;
for ( const char * p = s ; * p ; + + p ) {
if ( * p = = ' " ' | | * p = = ' \\ ' ) out + = ' \\ ' ;
else if ( * p = = ' \n ' ) { out + = " \\ n " ; continue ; }
else if ( * p = = ' \r ' ) continue ;
out + = * p ;
}
out + = " \" " ;
break ;
}
case LUA_TTABLE :
serializeLuaTable ( L , idx , out , indent ) ;
break ;
default :
out + = " nil " ; // Functions, userdata, etc. can't be serialized
break ;
}
}
2026-03-20 13:07:45 -07:00
void LuaEngine : : setAddonList ( const std : : vector < TocFile > & addons ) {
if ( ! L_ ) return ;
lua_pushnumber ( L_ , static_cast < double > ( addons . size ( ) ) ) ;
lua_setfield ( L_ , LUA_REGISTRYINDEX , " wowee_addon_count " ) ;
lua_newtable ( L_ ) ;
for ( size_t i = 0 ; i < addons . size ( ) ; i + + ) {
lua_newtable ( L_ ) ;
lua_pushstring ( L_ , addons [ i ] . addonName . c_str ( ) ) ;
lua_setfield ( L_ , - 2 , " name " ) ;
lua_pushstring ( L_ , addons [ i ] . getTitle ( ) . c_str ( ) ) ;
lua_setfield ( L_ , - 2 , " title " ) ;
auto notesIt = addons [ i ] . directives . find ( " Notes " ) ;
lua_pushstring ( L_ , notesIt ! = addons [ i ] . directives . end ( ) ? notesIt - > second . c_str ( ) : " " ) ;
lua_setfield ( L_ , - 2 , " notes " ) ;
2026-03-21 05:13:28 -07:00
// Store all TOC directives for GetAddOnMetadata
lua_newtable ( L_ ) ;
for ( const auto & [ key , val ] : addons [ i ] . directives ) {
lua_pushstring ( L_ , val . c_str ( ) ) ;
lua_setfield ( L_ , - 2 , key . c_str ( ) ) ;
}
lua_setfield ( L_ , - 2 , " metadata " ) ;
2026-03-20 13:07:45 -07:00
lua_rawseti ( L_ , - 2 , static_cast < int > ( i + 1 ) ) ;
}
lua_setfield ( L_ , LUA_REGISTRYINDEX , " wowee_addon_info " ) ;
}
2026-03-20 12:22:50 -07:00
bool LuaEngine : : loadSavedVariables ( const std : : string & path ) {
if ( ! L_ ) return false ;
std : : ifstream f ( path ) ;
if ( ! f . is_open ( ) ) return false ; // No saved data yet — not an error
std : : string content ( ( std : : istreambuf_iterator < char > ( f ) ) , std : : istreambuf_iterator < char > ( ) ) ;
if ( content . empty ( ) ) return true ;
int err = luaL_dostring ( L_ , content . c_str ( ) ) ;
if ( err ! = 0 ) {
LOG_WARNING ( " LuaEngine: error loading saved variables from ' " , path , " ': " ,
lua_tostring ( L_ , - 1 ) ) ;
lua_pop ( L_ , 1 ) ;
return false ;
}
return true ;
}
bool LuaEngine : : saveSavedVariables ( const std : : string & path , const std : : vector < std : : string > & varNames ) {
if ( ! L_ | | varNames . empty ( ) ) return false ;
std : : string output ;
for ( const auto & name : varNames ) {
lua_getglobal ( L_ , name . c_str ( ) ) ;
if ( ! lua_isnil ( L_ , - 1 ) ) {
output + = name + " = " ;
serializeLuaValue ( L_ , lua_gettop ( L_ ) , output , 0 ) ;
output + = " \n " ;
}
lua_pop ( L_ , 1 ) ;
}
if ( output . empty ( ) ) return true ;
// Ensure directory exists
size_t lastSlash = path . find_last_of ( " / \\ " ) ;
if ( lastSlash ! = std : : string : : npos ) {
std : : error_code ec ;
std : : filesystem : : create_directories ( path . substr ( 0 , lastSlash ) , ec ) ;
}
std : : ofstream f ( path ) ;
if ( ! f . is_open ( ) ) {
LOG_WARNING ( " LuaEngine: cannot write saved variables to ' " , path , " ' " ) ;
return false ;
}
f < < output ;
LOG_INFO ( " LuaEngine: saved variables to ' " , path , " ' ( " , output . size ( ) , " bytes) " ) ;
return true ;
}
2026-03-20 11:12:07 -07:00
bool LuaEngine : : executeFile ( const std : : string & path ) {
if ( ! L_ ) return false ;
int err = luaL_dofile ( L_ , path . c_str ( ) ) ;
if ( err ! = 0 ) {
const char * errMsg = lua_tostring ( L_ , - 1 ) ;
std : : string msg = errMsg ? errMsg : " (unknown error) " ;
LOG_ERROR ( " LuaEngine: error loading ' " , path , " ': " , msg ) ;
2026-03-21 06:08:17 -07:00
if ( luaErrorCallback_ ) luaErrorCallback_ ( msg ) ;
2026-03-20 11:12:07 -07:00
if ( gameHandler_ ) {
game : : MessageChatData errChat ;
errChat . type = game : : ChatType : : SYSTEM ;
errChat . language = game : : ChatLanguage : : UNIVERSAL ;
errChat . message = " |cffff4040[Lua Error] " + msg + " |r " ;
gameHandler_ - > addLocalChatMessage ( errChat ) ;
}
lua_pop ( L_ , 1 ) ;
return false ;
}
return true ;
}
bool LuaEngine : : executeString ( const std : : string & code ) {
if ( ! L_ ) return false ;
int err = luaL_dostring ( L_ , code . c_str ( ) ) ;
if ( err ! = 0 ) {
const char * errMsg = lua_tostring ( L_ , - 1 ) ;
std : : string msg = errMsg ? errMsg : " (unknown error) " ;
LOG_ERROR ( " LuaEngine: script error: " , msg ) ;
2026-03-21 06:08:17 -07:00
if ( luaErrorCallback_ ) luaErrorCallback_ ( msg ) ;
2026-03-20 11:12:07 -07:00
if ( gameHandler_ ) {
game : : MessageChatData errChat ;
errChat . type = game : : ChatType : : SYSTEM ;
errChat . language = game : : ChatLanguage : : UNIVERSAL ;
errChat . message = " |cffff4040[Lua Error] " + msg + " |r " ;
gameHandler_ - > addLocalChatMessage ( errChat ) ;
}
lua_pop ( L_ , 1 ) ;
return false ;
}
return true ;
}
} // namespace wowee::addons