Implement Lua tools

This commit is contained in:
Tamás Bálint Misius 2024-09-14 13:06:29 +02:00
parent ff4500620e
commit 00ec4e0754
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
12 changed files with 457 additions and 24 deletions

View File

@ -1729,3 +1729,11 @@ bool GameController::ThreadedRenderingAllowed()
{
return gameModel->GetThreadedRendering() && !commandInterface->HaveSimGraphicsEventHandlers();
}
void GameController::SetToolIndex(ByteString identifier, std::optional<int> index)
{
if (commandInterface)
{
commandInterface->SetToolIndex(identifier, index);
}
}

View File

@ -206,4 +206,6 @@ public:
void BeforeSimDraw();
void AfterSimDraw();
bool ThreadedRenderingAllowed();
void SetToolIndex(ByteString identifier, std::optional<int> index);
};

View File

@ -598,6 +598,14 @@ Brush *GameModel::GetBrushByID(int i)
return nullptr;
}
int GameModel::GetBrushIndex(const Brush &brush)
{
auto it = std::find_if(brushList.begin(), brushList.end(), [&brush](auto &ptr) {
return ptr.get() == &brush;
});
return int(it - brushList.begin());
}
int GameModel::GetBrushID()
{
return currentBrush;
@ -1647,6 +1655,7 @@ void GameModel::AllocTool(std::unique_ptr<Tool> tool)
index = int(tools.size());
tools.emplace_back();
}
GameController::Ref().SetToolIndex(tool->Identifier, *index);
tools[*index] = std::move(tool);
}
@ -1659,6 +1668,7 @@ void GameModel::FreeTool(Tool *tool)
}
auto &ptr = tools[*index];
DeselectTool(ptr->Identifier);
GameController::Ref().SetToolIndex(ptr->Identifier, std::nullopt);
ptr.reset();
}

View File

@ -213,6 +213,7 @@ public:
Brush &GetBrush();
Brush *GetBrushByID(int i);
int GetBrushID();
int GetBrushIndex(const Brush &brush);
int BrushListSize() const
{
return int(brushList.size());

View File

@ -5,6 +5,7 @@
#include "gui/game/GameControllerEvents.h"
#include "TPTSTypes.h"
#include <deque>
#include <optional>
class GameModel;
class GameController;
@ -55,5 +56,7 @@ public:
AnyType tptS_quit(std::deque<String> * words);
ValueType testType(String word);
void SetToolIndex(ByteString identifier, std::optional<int> index);
static CommandInterfacePtr Create(GameController *newGameController, GameModel *newGameModel);
};

View File

@ -155,6 +155,7 @@ LuaScriptInterface::LuaScriptInterface(GameController *newGameController, GameMo
LuaRenderer::Open(L);
LuaSimulation::Open(L);
LuaSocket::Open(L);
LuaTools::Open(L);
{
lua_getglobal(L, "os");
lua_pushcfunction(L, osExit);
@ -210,6 +211,12 @@ void CommandInterface::Init()
}
}
void CommandInterface::SetToolIndex(ByteString identifier, std::optional<int> index)
{
auto *lsi = static_cast<LuaScriptInterface *>(this);
LuaTools::SetToolIndex(lsi->L, identifier, index);
}
void LuaGetProperty(lua_State *L, StructProperty property, intptr_t propertyAddress)
{
switch (property.Type)

View File

@ -58,6 +58,17 @@ struct CustomElement
LuaSmartRef changeType;
};
struct CustomTool
{
LuaSmartRef perform;
LuaSmartRef click;
LuaSmartRef drag;
LuaSmartRef draw;
LuaSmartRef drawLine;
LuaSmartRef drawRect;
LuaSmartRef drawFill;
};
class LuaScriptInterface : public CommandInterface
{
LuaStatePtr luaState;
@ -86,6 +97,7 @@ public:
}
std::vector<CustomElement> customElements; // must come after luaState
std::vector<CustomTool> customTools;
EventTraits eventTraits = eventTraitNone;
@ -204,6 +216,12 @@ namespace LuaSocket
void OpenTCP(lua_State *L);
}
namespace LuaTools
{
void Open(lua_State *L);
void SetToolIndex(lua_State *L, ByteString identifier, std::optional<int> index);
}
inline LuaScriptInterface *GetLSI()
{
return static_cast<LuaScriptInterface *>(&CommandInterface::Ref());

View File

@ -2020,21 +2020,6 @@ void LuaSimulation::Open(lua_State *L)
LCONSTAS("NUM_WALLS", UI_WALLCOUNT);
}
{
lua_newtable(L);
auto &toolList = lsi->gameModel->GetTools();
for (int i = 0; i < int(toolList.size()); ++i)
{
tpt_lua_pushByteString(L, toolList[i]->Identifier);
lua_pushinteger(L, i);
lua_settable(L, -3);
lua_pushinteger(L, i);
tpt_lua_pushByteString(L, toolList[i]->Identifier);
lua_settable(L, -3);
}
lua_setfield(L, -2, "tools");
LCONSTAS("NUM_TOOLS", UI_WALLCOUNT);
}
#undef LCONSTAS
#undef LCONSTF
#undef LCONST

394
src/lua/LuaTools.cpp Normal file
View File

@ -0,0 +1,394 @@
#include "LuaScriptInterface.h"
#include "gui/game/GameModel.h"
#include "simulation/SimTool.h"
#include "simulation/Simulation.h"
#include <type_traits>
static int allocate(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TSTRING);
auto group = tpt_lua_toByteString(L, 1).ToUpper();
auto name = tpt_lua_toByteString(L, 2).ToUpper();
if (name.Contains("_"))
{
return luaL_error(L, "The tool name may not contain '_'.");
}
if (group.Contains("_"))
{
return luaL_error(L, "The group name may not contain '_'.");
}
if (group == "DEFAULT")
{
return luaL_error(L, "You cannot create tools in the 'DEFAULT' group.");
}
auto *lsi = GetLSI();
auto identifier = group + "_TOOL_" + name;
{
SimTool tool;
tool.Identifier = identifier;
lsi->gameModel->AllocTool(std::make_unique<SimTool>(tool));
}
lsi->gameModel->BuildMenus();
auto index = *lsi->gameModel->GetToolIndex(lsi->gameModel->GetToolFromIdentifier(identifier));
lsi->customTools.resize(std::max(int(lsi->customTools.size()), index + 1));
lua_pushinteger(L, index);
return 1;
}
static bool IsDefault(Tool *tool)
{
return tool->Identifier.BeginsWith("DEFAULT_");
}
static int ffree(lua_State *L)
{
int index = luaL_checkinteger(L, 1);
auto *lsi = GetLSI();
auto *tool = lsi->gameModel->GetToolByIndex(index);
if (!tool)
{
return luaL_error(L, "Invalid tool");
}
if (IsDefault(tool))
{
return luaL_error(L, "Cannot free default tools");
}
lsi->customTools[index] = {};
lsi->gameModel->FreeTool(tool);
lsi->gameModel->BuildMenus();
return 0;
}
static int luaPerformWrapper(SimTool *tool, Simulation *sim, Particle *cpart, int x, int y, int brushX, int brushY, float strength)
{
int ok = 0;
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].perform)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].perform);
if (cpart)
{
lua_pushinteger(L, cpart - &lsi->sim->parts[0]);
}
else
{
lua_pushnil(L);
}
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
lua_pushinteger(L, brushX);
lua_pushinteger(L, brushY);
if (tpt_lua_pcall(L, 9, 1, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In perform func: " + LuaGetError());
lua_pop(L, 1);
}
if (lua_isboolean(L, -1))
{
ok = lua_toboolean(L, -1);
}
lua_pop(L, 1);
}
return ok;
}
static void luaClickWrapper(SimTool *tool, Simulation *sim, const Brush &brush, ui::Point position)
{
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].click)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].click);
lua_pushinteger(L, lsi->gameModel->GetBrushIndex(brush));
lua_pushinteger(L, position.X);
lua_pushinteger(L, position.Y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
if (tpt_lua_pcall(L, 7, 0, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In click func: " + LuaGetError());
lua_pop(L, 1);
}
}
}
static void luaDragWrapper(SimTool *tool, Simulation *sim, const Brush &brush, ui::Point position1, ui::Point position2)
{
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].drag)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].drag);
lua_pushinteger(L, lsi->gameModel->GetBrushIndex(brush));
lua_pushinteger(L, position1.X);
lua_pushinteger(L, position1.Y);
lua_pushinteger(L, position2.X);
lua_pushinteger(L, position2.Y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
if (tpt_lua_pcall(L, 9, 0, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In drag func: " + LuaGetError());
lua_pop(L, 1);
}
}
}
static void luaDrawWrapper(SimTool *tool, Simulation *sim, const Brush &brush, ui::Point position)
{
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].draw)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].draw);
lua_pushinteger(L, lsi->gameModel->GetBrushIndex(brush));
lua_pushinteger(L, position.X);
lua_pushinteger(L, position.Y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
if (tpt_lua_pcall(L, 7, 0, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In draw func: " + LuaGetError());
lua_pop(L, 1);
}
}
}
static void luaDrawLineWrapper(SimTool *tool, Simulation *sim, const Brush &brush, ui::Point position1, ui::Point position2, bool dragging)
{
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].drawLine)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].drawLine);
lua_pushinteger(L, lsi->gameModel->GetBrushIndex(brush));
lua_pushinteger(L, position1.X);
lua_pushinteger(L, position1.Y);
lua_pushinteger(L, position2.X);
lua_pushinteger(L, position2.Y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
if (tpt_lua_pcall(L, 9, 0, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In drawLine func: " + LuaGetError());
lua_pop(L, 1);
}
}
}
static void luaDrawRectWrapper(SimTool *tool, Simulation *sim, const Brush &brush, ui::Point position1, ui::Point position2)
{
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].drawRect)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].drawRect);
lua_pushinteger(L, lsi->gameModel->GetBrushIndex(brush));
lua_pushinteger(L, position1.X);
lua_pushinteger(L, position1.Y);
lua_pushinteger(L, position2.X);
lua_pushinteger(L, position2.Y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
if (tpt_lua_pcall(L, 9, 0, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In drawRect func: " + LuaGetError());
lua_pop(L, 1);
}
}
}
static void luaDrawFillWrapper(SimTool *tool, Simulation *sim, const Brush &brush, ui::Point position)
{
auto *lsi = GetLSI();
auto L = lsi->L;
auto index = *lsi->gameModel->GetToolIndex(tool);
auto &customTools = lsi->customTools;
if (customTools[index].drawFill)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, customTools[index].drawFill);
lua_pushinteger(L, lsi->gameModel->GetBrushIndex(brush));
lua_pushinteger(L, position.X);
lua_pushinteger(L, position.Y);
lua_pushnumber(L, tool->Strength);
lua_pushboolean(L, tool->shiftBehaviour);
lua_pushboolean(L, tool->ctrlBehaviour);
lua_pushboolean(L, tool->altBehaviour);
if (tpt_lua_pcall(L, 7, 0, 0, eventTraitNone))
{
lsi->Log(CommandInterface::LogError, "In drawFill func: " + LuaGetError());
lua_pop(L, 1);
}
}
}
template <typename T>
struct DependentFalse : std::false_type
{
};
static int property(lua_State *L)
{
auto *lsi = GetLSI();
int index = luaL_checkinteger(L, 1);
auto *tool = lsi->gameModel->GetToolByIndex(index);
if (!tool)
{
return luaL_error(L, "Invalid tool");
}
ByteString propertyName = tpt_lua_checkByteString(L, 2);
auto handleCallback = [lsi, L, index, tool, &propertyName](
auto customToolMember,
auto simToolMember,
auto wrapper,
const char *luaPropertyName
) {
if (propertyName == luaPropertyName)
{
if (lua_gettop(L) > 2)
{
if (IsDefault(tool))
{
luaL_error(L, "Cannot change callbacks of default tools");
}
if (lua_type(L, 3) == LUA_TFUNCTION)
{
(lsi->customTools[index].*customToolMember).Assign(L, 3);
(static_cast<SimTool *>(tool)->*simToolMember) = wrapper;
}
else if (lua_type(L, 3) == LUA_TBOOLEAN && !lua_toboolean(L, 3))
{
(lsi->customTools[index].*customToolMember).Clear();
(static_cast<SimTool *>(tool)->*simToolMember) = SimTool().*simToolMember;
}
return true;
}
luaL_error(L, "Invalid tool property");
}
return false;
};
if (handleCallback(&CustomTool::perform , &SimTool::Perform , luaPerformWrapper , "Perform" ) ||
handleCallback(&CustomTool::click , &SimTool::PerformClick , luaClickWrapper , "Click" ) ||
handleCallback(&CustomTool::drag , &SimTool::PerformDrag , luaDragWrapper , "Drag" ) ||
handleCallback(&CustomTool::draw , &SimTool::PerformDraw , luaDrawWrapper , "Draw" ) ||
handleCallback(&CustomTool::drawLine, &SimTool::PerformDrawLine, luaDrawLineWrapper, "DrawLine") ||
handleCallback(&CustomTool::drawRect, &SimTool::PerformDrawRect, luaDrawRectWrapper, "DrawRect") ||
handleCallback(&CustomTool::drawFill, &SimTool::PerformDrawFill, luaDrawFillWrapper, "DrawFill"))
{
return 0;
}
int returnValueCount = 0;
auto handleProperty = [L, tool, &propertyName, &returnValueCount](auto simToolMember, const char *luaPropertyName) {
if (propertyName == luaPropertyName)
{
auto &thing = tool->*simToolMember;
using PropertyType = std::remove_reference_t<decltype(thing)>;
if (lua_gettop(L) > 2)
{
if constexpr (std::is_same_v<PropertyType, String >) thing = tpt_lua_checkString(L, 3);
else if constexpr (std::is_same_v<PropertyType, RGB<uint8_t>>) thing = RGB<uint8_t>::Unpack(luaL_checkinteger(L, 3));
else static_assert(DependentFalse<PropertyType>::value);
}
else
{
if constexpr (std::is_same_v<PropertyType, String >) tpt_lua_pushString(L, thing);
else if constexpr (std::is_same_v<PropertyType, RGB<uint8_t>>) lua_pushinteger(L, thing.Pack());
else static_assert(DependentFalse<PropertyType>::value);
returnValueCount = 1;
}
return true;
}
return false;
};
if (handleProperty(&SimTool::Name , "Name" ) ||
handleProperty(&SimTool::Description, "Description") ||
handleProperty(&SimTool::Colour , "Colour" ) ||
handleProperty(&SimTool::Colour , "Color" ))
{
return returnValueCount;
}
if (propertyName == "Identifier")
{
tpt_lua_pushByteString(L, tool->Identifier);
return 1;
}
return luaL_error(L, "Invalid tool property");
}
static int exists(lua_State *L)
{
int index = luaL_checkinteger(L, 1);
auto *lsi = GetLSI();
lua_pushboolean(L, bool(lsi->gameModel->GetToolByIndex(index)));
return 1;
}
void LuaTools::Open(lua_State *L)
{
auto *lsi = GetLSI();
static const luaL_Reg reg[] = {
#define LFUNC(v) { #v, v }
LFUNC(allocate),
LFUNC(property),
LFUNC(exists),
#undef LFUNC
{ "free", ffree },
{ NULL, NULL }
};
lua_newtable(L);
luaL_register(L, NULL, reg);
lua_setglobal(L, "tools");
auto &toolList = lsi->gameModel->GetTools();
for (int i = 0; i < int(toolList.size()); ++i)
{
if (!toolList[i])
{
continue;
}
SetToolIndex(L, toolList[i]->Identifier, i);
}
}
void LuaTools::SetToolIndex(lua_State *L, ByteString identifier, std::optional<int> index)
{
lua_getglobal(L, "tools");
tpt_lua_pushByteString(L, identifier);
if (index)
{
lua_pushinteger(L, *index);
}
else
{
lua_pushnil(L);
}
lua_settable(L, -3);
lua_pop(L, 1);
}

View File

@ -37,3 +37,7 @@ String CommandInterface::FormatCommand(String command)
{
return PlainFormatCommand(command);
}
void CommandInterface::SetToolIndex(ByteString identifier, std::optional<int> index)
{
}

View File

@ -55,15 +55,15 @@ tpt.setfpscap = tpt.fpsCap
ui.MOUSE_UP_BLUR = ui.MOUSEUP_BLUR
ui.MOUSE_UP_DRAW_END = ui.MOUSEUP_DRAWEND
ui.MOUSE_UP_NORMAL = ui.MOUSEUP_NORMAL
sim.TOOL_HEAT = sim.tools.DEFAULT_TOOL_HEAT
sim.TOOL_COOL = sim.tools.DEFAULT_TOOL_COOL
sim.TOOL_VAC = sim.tools.DEFAULT_TOOL_VAC
sim.TOOL_PGRV = sim.tools.DEFAULT_TOOL_PGRV
sim.TOOL_AIR = sim.tools.DEFAULT_TOOL_AIR
sim.TOOL_NGRV = sim.tools.DEFAULT_TOOL_NGRV
sim.TOOL_MIX = sim.tools.DEFAULT_TOOL_MIX
sim.TOOL_CYCL = sim.tools.DEFAULT_TOOL_CYCL
sim.TOOL_WIND = sim.tools.DEFAULT_TOOL_WIND
sim.TOOL_HEAT = tools.DEFAULT_TOOL_HEAT
sim.TOOL_COOL = tools.DEFAULT_TOOL_COOL
sim.TOOL_VAC = tools.DEFAULT_TOOL_VAC
sim.TOOL_PGRV = tools.DEFAULT_TOOL_PGRV
sim.TOOL_AIR = tools.DEFAULT_TOOL_AIR
sim.TOOL_NGRV = tools.DEFAULT_TOOL_NGRV
sim.TOOL_MIX = tools.DEFAULT_TOOL_MIX
sim.TOOL_CYCL = tools.DEFAULT_TOOL_CYCL
sim.TOOL_WIND = tools.DEFAULT_TOOL_WIND
if socket then
socket.gettime = socket.getTime
end

View File

@ -21,6 +21,7 @@ luaconsole_files = files(
'LuaSocket.cpp',
'LuaSmartRef.cpp',
'LuaTextbox.cpp',
'LuaTools.cpp',
'LuaWindow.cpp',
)
if lua_variant != 'luajit'