mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00: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)
This commit is contained in:
parent
c1820fd07d
commit
c284a971c2
2 changed files with 233 additions and 20 deletions
|
|
@ -1,24 +1,28 @@
|
|||
-- HelloWorld addon — test the WoWee addon system
|
||||
print("|cff00ff00[HelloWorld]|r Addon loaded! Lua 5.1 is working.")
|
||||
-- HelloWorld addon — demonstrates the WoWee addon system
|
||||
|
||||
-- Register for game events
|
||||
RegisterEvent("PLAYER_ENTERING_WORLD", function(event)
|
||||
local name = UnitName("player")
|
||||
local level = UnitLevel("player")
|
||||
local health = UnitHealth("player")
|
||||
local maxHealth = UnitHealthMax("player")
|
||||
local _, _, classId = UnitClass("player")
|
||||
local gold = math.floor(GetMoney() / 10000)
|
||||
-- Create a frame and register for events (standard WoW addon pattern)
|
||||
local f = CreateFrame("Frame", "HelloWorldFrame")
|
||||
f:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
f:RegisterEvent("CHAT_MSG_SAY")
|
||||
|
||||
print("|cff00ff00[HelloWorld]|r Welcome, " .. name .. "! (Level " .. level .. ")")
|
||||
if maxHealth > 0 then
|
||||
print("|cff00ff00[HelloWorld]|r Health: " .. health .. "/" .. maxHealth)
|
||||
end
|
||||
if gold > 0 then
|
||||
print("|cff00ff00[HelloWorld]|r Gold: " .. gold .. "g")
|
||||
f:SetScript("OnEvent", function(self, event, ...)
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
local name = UnitName("player")
|
||||
local level = UnitLevel("player")
|
||||
print("|cff00ff00[HelloWorld]|r Welcome, " .. name .. "! (Level " .. level .. ")")
|
||||
elseif event == "CHAT_MSG_SAY" then
|
||||
local msg, sender = ...
|
||||
if msg and sender then
|
||||
print("|cff00ff00[HelloWorld]|r " .. sender .. " said: " .. msg)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterEvent("PLAYER_LEAVING_WORLD", function(event)
|
||||
print("|cff00ff00[HelloWorld]|r Goodbye!")
|
||||
end)
|
||||
-- Register a custom slash command
|
||||
SLASH_HELLOWORLD1 = "/hello"
|
||||
SLASH_HELLOWORLD2 = "/hw"
|
||||
SlashCmdList["HELLOWORLD"] = function(args)
|
||||
print("|cff00ff00[HelloWorld]|r Hello! " .. (args ~= "" and args or "Type /hello <message>"))
|
||||
end
|
||||
|
||||
print("|cff00ff00[HelloWorld]|r Addon loaded. Type /hello to test slash commands.")
|
||||
|
|
|
|||
|
|
@ -301,6 +301,152 @@ static int lua_HasTarget(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// --- Frame System ---
|
||||
// Minimal WoW-compatible frame objects with RegisterEvent/SetScript/GetScript.
|
||||
// Frames are Lua tables with a metatable that provides methods.
|
||||
|
||||
// Frame method: frame:RegisterEvent("EVENT")
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// --- WoW Utility Functions ---
|
||||
|
||||
// strsplit(delimiter, str) — WoW's string split
|
||||
|
|
@ -497,7 +643,36 @@ void LuaEngine::registerCoreAPI() {
|
|||
lua_newtable(L_);
|
||||
lua_setglobal(L_, "SlashCmdList");
|
||||
|
||||
// SLASH_* globals will be set by addons, dispatched by the /run command handler
|
||||
// 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
|
||||
{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");
|
||||
|
||||
// CreateFrame function
|
||||
lua_pushcfunction(L_, lua_CreateFrame);
|
||||
lua_setglobal(L_, "CreateFrame");
|
||||
|
||||
// Frame event dispatch table
|
||||
lua_newtable(L_);
|
||||
lua_setglobal(L_, "__WoweeFrameEvents");
|
||||
}
|
||||
|
||||
// ---- Event System ----
|
||||
|
|
@ -609,6 +784,40 @@ void LuaEngine::fireEvent(const std::string& eventName,
|
|||
}
|
||||
}
|
||||
lua_pop(L_, 2); // pop handler list + WoweeEvents
|
||||
|
||||
// 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) {
|
||||
LOG_ERROR("LuaEngine: frame OnEvent error: ", lua_tostring(L_, -1));
|
||||
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
|
||||
}
|
||||
|
||||
bool LuaEngine::dispatchSlashCommand(const std::string& command, const std::string& args) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue