From 493a32a1b28f15cf02bb69c3735b9a411448c523 Mon Sep 17 00:00:00 2001 From: Simon Robertshaw Date: Fri, 31 Aug 2012 19:39:11 +0100 Subject: [PATCH] Some Lua interface API stuff --- src/cat/LuaButton.cpp | 134 +++++++++++++++++++++++++++++++++ src/cat/LuaButton.h | 33 ++++++++ src/cat/LuaLuna.h | 128 +++++++++++++++++++++++++++++++ src/cat/LuaScriptInterface.cpp | 65 +++++++++++++++- src/cat/LuaScriptInterface.h | 14 ++++ src/cat/LuaWindow.cpp | 101 +++++++++++++++++++++++++ src/cat/LuaWindow.h | 30 ++++++++ src/game/GameController.cpp | 10 ++- src/game/GameController.h | 1 + src/interface/Button.h | 1 + 10 files changed, 513 insertions(+), 4 deletions(-) create mode 100644 src/cat/LuaButton.cpp create mode 100644 src/cat/LuaButton.h create mode 100644 src/cat/LuaLuna.h create mode 100644 src/cat/LuaWindow.cpp create mode 100644 src/cat/LuaWindow.h diff --git a/src/cat/LuaButton.cpp b/src/cat/LuaButton.cpp new file mode 100644 index 000000000..8a3c92da8 --- /dev/null +++ b/src/cat/LuaButton.cpp @@ -0,0 +1,134 @@ +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} + +#include +#include "LuaButton.h" +#include "interface/Button.h" + +const char LuaButton::className[] = "Button"; + +#define method(class, name) {#name, &class::name} +Luna::RegType LuaButton::methods[] = { + method(LuaButton, action), + method(LuaButton, text), + method(LuaButton, position), + method(LuaButton, size), + {0, 0} +}; + +LuaButton::LuaButton(lua_State * l) : + actionFunction(0) +{ + this->l = l; + int posX = luaL_optinteger(l, 1, 0); + int posY = luaL_optinteger(l, 2, 0); + int sizeX = luaL_optinteger(l, 3, 10); + int sizeY = luaL_optinteger(l, 4, 10); + std::string text = luaL_optstring(l, 5, ""); + std::string toolTip = luaL_optstring(l, 6, "");; + + button = new ui::Button(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text, toolTip); + class ClickAction : public ui::ButtonAction + { + LuaButton * luaButton; + public: + ClickAction(LuaButton * luaButton) : luaButton(luaButton) {} + void ActionCallback(ui::Button * sender) + { + luaButton->triggerAction(); + } + }; + button->SetActionCallback(new ClickAction(this)); +} + +int LuaButton::action(lua_State * l) +{ + if(lua_type(l, 1) != LUA_TNIL) + { + luaL_checktype(l, 1, LUA_TFUNCTION); + lua_pushvalue(l, 1); + actionFunction = luaL_ref(l, LUA_REGISTRYINDEX); + } + else + { + actionFunction = 0; + } +} + +int LuaButton::text(lua_State * l) +{ + int args = lua_gettop(l); + if(args) + { + luaL_checktype(l, 1, LUA_TSTRING); + button->SetText(lua_tostring(l, 1)); + return 0; + } + else + { + lua_pushstring(l, button->GetText().c_str()); + return 1; + } +} + +int LuaButton::position(lua_State * l) +{ + int args = lua_gettop(l); + if(args) + { + luaL_checktype(l, 1, LUA_TNUMBER); + luaL_checktype(l, 2, LUA_TNUMBER); + button->Position = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2)); + return 0; + } + else + { + lua_pushinteger(l, button->Position.X); + lua_pushinteger(l, button->Position.Y); + return 2; + } +} + +int LuaButton::size(lua_State * l) +{ + int args = lua_gettop(l); + if(args) + { + luaL_checktype(l, 1, LUA_TNUMBER); + luaL_checktype(l, 2, LUA_TNUMBER); + button->Size = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2)); + button->Invalidate(); + return 0; + } + else + { + lua_pushinteger(l, button->Size.X); + lua_pushinteger(l, button->Size.Y); + return 2; + } +} + +void LuaButton::triggerAction() +{ + if(actionFunction) + { + std::cout << actionFunction << std::endl; + lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction); + lua_pushinteger(l, 1); + if (lua_pcall(l, 1, 1, 0)) + { + //Log error somewhere + } + } +} + +LuaButton::~LuaButton() +{ + if(button->GetParentWindow()) + button->GetParentWindow()->RemoveComponent(button); + delete button; +} \ No newline at end of file diff --git a/src/cat/LuaButton.h b/src/cat/LuaButton.h new file mode 100644 index 000000000..ef5bb54f7 --- /dev/null +++ b/src/cat/LuaButton.h @@ -0,0 +1,33 @@ +#pragma once + +extern "C" { + #include "lua.h" + #include "lauxlib.h" + #include "lualib.h" +} + +#include "LuaLuna.h" + +namespace ui +{ + class Button; +} + +class LuaButton +{ + ui::Button * button; + int actionFunction; + lua_State * l; + void triggerAction(); + int action(lua_State * l); + int text(lua_State * l); + int position(lua_State * l); + int size(lua_State * l); +public: + static const char className[]; + static Luna::RegType methods[]; + + ui::Button * GetComponent() { return button; } + LuaButton(lua_State * l); + ~LuaButton(); +}; \ No newline at end of file diff --git a/src/cat/LuaLuna.h b/src/cat/LuaLuna.h new file mode 100644 index 000000000..d15a7c6b1 --- /dev/null +++ b/src/cat/LuaLuna.h @@ -0,0 +1,128 @@ +#pragma once +//http://lua-users.org/wiki/SimplerCppBinding + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +} + +template class Luna +{ + typedef struct { T *pT; } userdataType; +public: + typedef int (T::*mfp)(lua_State *L); + typedef struct { const char *name; mfp mfunc; } RegType; + + static void Register(lua_State *L) + { + lua_newtable(L); + int methods = lua_gettop(L); + + luaL_newmetatable(L, T::className); + int metatable = lua_gettop(L); + + // store method table in globals so that + // scripts can add functions written in Lua. + lua_pushstring(L, T::className); + lua_pushvalue(L, methods); + lua_settable(L, LUA_GLOBALSINDEX); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methods); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methods); + lua_settable(L, metatable); + + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, tostring_T); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_T); + lua_settable(L, metatable); + + lua_newtable(L); // mt for method table + int mt = lua_gettop(L); + lua_pushliteral(L, "__call"); + lua_pushcfunction(L, new_T); + lua_pushliteral(L, "new"); + lua_pushvalue(L, -2); // dup new_T function + lua_settable(L, methods); // add new_T to method table + lua_settable(L, mt); // mt.__call = new_T + lua_setmetatable(L, methods); + + // fill method table with methods from class T + for (RegType *l = T::methods; l->name; l++) + { + /* edited by Snaily: shouldn't it be const RegType *l ... ? */ + lua_pushstring(L, l->name); + lua_pushlightuserdata(L, (void*)l); + lua_pushcclosure(L, thunk, 1); + lua_settable(L, methods); + } + + lua_pop(L, 2); // drop metatable and method table + } + + // get userdata from Lua stack and return pointer to T object + static T * check(lua_State * L, int narg) + { + userdataType *ud = static_cast(luaL_checkudata(L, narg, T::className)); + if(!ud) + luaL_typerror(L, narg, T::className); + return ud->pT; // pointer to T object + } + + static inline T * get(void * userData) + { + return ((userdataType*)userData)->pT; + } + +private: + Luna(); // hide default constructor + + // member function dispatcher + static int thunk(lua_State * L) + { + // stack has userdata, followed by method args + T *obj = check(L, 1); // get 'self', or if you prefer, 'this' + lua_remove(L, 1); // remove self so member function args start at index 1 + // get member function from upvalue + RegType *l = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + return (obj->*(l->mfunc))(L); // call member function + } + + // create a new T object and + // push onto the Lua stack a userdata containing a pointer to T object + static int new_T(lua_State * L) + { + lua_remove(L, 1); // use classname:new(), instead of classname.new() + T *obj = new T(L); // call constructor for T objects + userdataType *ud = static_cast(lua_newuserdata(L, sizeof(userdataType))); + ud->pT = obj; // store pointer to object in userdata + luaL_getmetatable(L, T::className); // lookup metatable in Lua registry + lua_setmetatable(L, -2); + return 1; // userdata containing pointer to T object + } + + // garbage collection metamethod + static int gc_T(lua_State *L) + { + userdataType *ud = static_cast(lua_touserdata(L, 1)); + T *obj = ud->pT; + delete obj; // call destructor for T objects + return 0; + } + + static int tostring_T (lua_State * L) + { + char buff[32]; + userdataType *ud = static_cast(lua_touserdata(L, 1)); + T *obj = ud->pT; + sprintf(buff, "%p", obj); + lua_pushfstring(L, "%s (%s)", T::className, buff); + return 1; + } +}; \ No newline at end of file diff --git a/src/cat/LuaScriptInterface.cpp b/src/cat/LuaScriptInterface.cpp index 84b4b9ee3..fe098a042 100644 --- a/src/cat/LuaScriptInterface.cpp +++ b/src/cat/LuaScriptInterface.cpp @@ -25,6 +25,10 @@ #include "LuaBit.h" +#include "LuaLuna.h" +#include "LuaWindow.h" +#include "LuaButton.h" + #ifdef WIN #include #else @@ -48,6 +52,7 @@ LuaScriptInterface::LuaScriptInterface(GameModel * m): luaL_openlibs(l); luaopen_bit(l); + initInterfaceAPI(); initRendererAPI(); initElementsAPI(); @@ -251,10 +256,66 @@ tpt.partsdata = nil"); lua_el_mode[i] = 0; } - //Autorun - luacon_eval("dofile(\"autorun.lua\")"); //Autorun lua script } +void LuaScriptInterface::Init() +{ + if(luacon_eval("dofile(\"autorun.lua\")")) + { + luacon_ci->Log(CommandInterface::LogError, luacon_geterror()); + } +} + +void LuaScriptInterface::SetWindow(ui::Window * window) +{ + Window = window; +} + +//// Begin Interface API + +void LuaScriptInterface::initInterfaceAPI() +{ + struct luaL_reg interfaceAPIMethods [] = { + {"showWindow", interface_showWindow}, + {"closeWindow", interface_closeWindow}, + {"addComponent", interface_addComponent}, + {NULL, NULL} + }; + luaL_register(l, "interface", interfaceAPIMethods); + Luna::Register(l); + Luna::Register(l); +} + +int LuaScriptInterface::interface_addComponent(lua_State * l) +{ + void * luaComponent = NULL; + ui::Component * component = NULL; + if(luaComponent = luaL_checkudata(l, 1, "Button")) + component = Luna::get(luaComponent)->GetComponent(); + else + luaL_typerror(l, 1, "Component"); + if(luacon_ci->Window && component) + luacon_ci->Window->AddComponent(component); + return 0; +} + +int LuaScriptInterface::interface_showWindow(lua_State * l) +{ + LuaWindow * window = Luna::check(l, 1); + if(window && ui::Engine::Ref().GetWindow()!=window->GetWindow()) + ui::Engine::Ref().ShowWindow(window->GetWindow()); + return 0; +} + +int LuaScriptInterface::interface_closeWindow(lua_State * l) +{ + LuaWindow * window = Luna::check(l, 1); + if(window && ui::Engine::Ref().GetWindow()==window->GetWindow()) + ui::Engine::Ref().CloseWindow(); + return 0; +} + + //// Begin Renderer API void LuaScriptInterface::initRendererAPI() diff --git a/src/cat/LuaScriptInterface.h b/src/cat/LuaScriptInterface.h index 96fd66ba7..37adc6514 100644 --- a/src/cat/LuaScriptInterface.h +++ b/src/cat/LuaScriptInterface.h @@ -18,6 +18,11 @@ extern "C" #include "CommandInterface.h" #include "simulation/Simulation.h" +namespace ui +{ + class Window; +} + //Because lua only has bindings for C, we're going to have to go outside "outside" the LuaScriptInterface, this means we can only have one instance :( #define LOCAL_LUA_DIR "Lua" @@ -53,7 +58,14 @@ class LuaScriptInterface: public CommandInterface { static int elements_property(lua_State * l); static int elements_loadDefault(lua_State * l); static int elements_free(lua_State * l); + + //Interface + void initInterfaceAPI(); + static int interface_showWindow(lua_State * l); + static int interface_closeWindow(lua_State * l); + static int interface_addComponent(lua_State * l); public: + ui::Window * Window; lua_State *l; LuaScriptInterface(GameModel * m); virtual bool OnBrushChanged(int brushType, int rx, int ry); @@ -64,6 +76,8 @@ public: virtual bool OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); virtual bool OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); virtual void OnTick(); + virtual void Init(); + virtual void SetWindow(ui::Window * window); virtual int Command(std::string command); virtual std::string FormatCommand(std::string command); virtual ~LuaScriptInterface(); diff --git a/src/cat/LuaWindow.cpp b/src/cat/LuaWindow.cpp new file mode 100644 index 000000000..cedfcdcec --- /dev/null +++ b/src/cat/LuaWindow.cpp @@ -0,0 +1,101 @@ +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} + +#include +#include "LuaWindow.h" +#include "LuaButton.h" +#include "interface/Button.h" +#include "interface/Window.h" + +const char LuaWindow::className[] = "Window"; + +#define method(class, name) {#name, &class::name} +Luna::RegType LuaWindow::methods[] = { + method(LuaWindow, position), + method(LuaWindow, size), + method(LuaWindow, addComponent), + {0, 0} +}; + +LuaWindow::LuaWindow(lua_State * l) +{ + this->l = l; + int posX = luaL_optinteger(l, 1, 0); + int posY = luaL_optinteger(l, 2, 0); + int sizeX = luaL_optinteger(l, 3, 10); + int sizeY = luaL_optinteger(l, 4, 10); + + class DrawnWindow : public ui::Window + { + public: + DrawnWindow(ui::Point position, ui::Point size) : ui::Window(position, size) {} + virtual void OnDraw() + { + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + } + }; + + window = new DrawnWindow(ui::Point(posX, posY), ui::Point(sizeX, sizeY)); +} + +int LuaWindow::addComponent(lua_State * l) +{ + void * luaComponent = NULL; + ui::Component * component = NULL; + if(luaComponent = luaL_checkudata(l, 1, "Button")) + component = Luna::get(luaComponent)->GetComponent(); + else + luaL_typerror(l, 1, "Component"); + if(component) + window->AddComponent(component); + return 0; +} + +int LuaWindow::position(lua_State * l) +{ + int args = lua_gettop(l); + if(args) + { + luaL_checktype(l, 1, LUA_TNUMBER); + luaL_checktype(l, 2, LUA_TNUMBER); + window->Position = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2)); + return 0; + } + else + { + lua_pushinteger(l, window->Position.X); + lua_pushinteger(l, window->Position.Y); + return 2; + } +} + +int LuaWindow::size(lua_State * l) +{ + int args = lua_gettop(l); + if(args) + { + luaL_checktype(l, 1, LUA_TNUMBER); + luaL_checktype(l, 2, LUA_TNUMBER); + window->Size = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2)); + return 0; + } + else + { + lua_pushinteger(l, window->Size.X); + lua_pushinteger(l, window->Size.Y); + return 2; + } +} + +LuaWindow::~LuaWindow() +{ + if(ui::Engine::Ref().GetWindow() == window) + ui::Engine::Ref().CloseWindow(); + delete window; +} \ No newline at end of file diff --git a/src/cat/LuaWindow.h b/src/cat/LuaWindow.h new file mode 100644 index 000000000..ea7b98f5c --- /dev/null +++ b/src/cat/LuaWindow.h @@ -0,0 +1,30 @@ +#pragma once + +extern "C" { + #include "lua.h" + #include "lauxlib.h" + #include "lualib.h" +} + +#include "LuaLuna.h" + +namespace ui +{ + class Window; +} + +class LuaWindow +{ + ui::Window * window; + lua_State * l; + int position(lua_State * l); + int size(lua_State * l); + int addComponent(lua_State * l); +public: + static const char className[]; + static Luna::RegType methods[]; + + ui::Window * GetWindow() { return window; } + LuaWindow(lua_State * l); + ~LuaWindow(); +}; \ No newline at end of file diff --git a/src/game/GameController.cpp b/src/game/GameController.cpp index 225823946..ffa763fe2 100644 --- a/src/game/GameController.cpp +++ b/src/game/GameController.cpp @@ -140,7 +140,8 @@ GameController::GameController(): options(NULL), activePreview(NULL), localBrowser(NULL), - HasDone(false) + HasDone(false), + firstTick(true) { gameView = new GameView(); gameModel = new GameModel(); @@ -149,7 +150,7 @@ GameController::GameController(): gameModel->AddObserver(gameView); commandInterface = new LuaScriptInterface(gameModel);//new TPTScriptInterface(); - //commandInterface->AttachGameModel(gameModel); + ((LuaScriptInterface*)commandInterface)->SetWindow(gameView); //sim = new Simulation(); Client::Ref().AddListener(this); @@ -658,6 +659,11 @@ bool GameController::KeyRelease(int key, Uint16 character, bool shift, bool ctrl void GameController::Tick() { + if(firstTick) + { + ((LuaScriptInterface*)commandInterface)->Init(); + firstTick = false; + } commandInterface->OnTick(); } diff --git a/src/game/GameController.h b/src/game/GameController.h index ea24bb207..af3f1bff0 100644 --- a/src/game/GameController.h +++ b/src/game/GameController.h @@ -31,6 +31,7 @@ class GameController: public ClientListener { private: //Simulation * sim; + bool firstTick; int screenshotIndex; PreviewController * activePreview; GameView * gameView; diff --git a/src/interface/Button.h b/src/interface/Button.h index 6b7fb9636..2244a91a3 100644 --- a/src/interface/Button.h +++ b/src/interface/Button.h @@ -53,6 +53,7 @@ public: ButtonAction * GetActionCallback() { return actionCallback; } void SetText(std::string buttonText); void SetIcon(Icon icon); + inline std::string GetText() { return ButtonText; }; protected: std::string toolTip;