This repository has been archived on 2025-03-20. You can view files and clone it, but cannot push or open issues or pull requests.
The-Powder-Toy/src/lua/LegacyLuaAPI.cpp
Tamás Bálint Misius c725894abd
Emscripten: Add "vsync" FPS limit mode
Would be really useful for native versions too, but it's more complicated to pull off. For now, vsync on native is the same as tpt.setfpscap(2).
2023-08-22 00:26:32 +02:00

1336 lines
32 KiB
C++

#include "Format.h"
#include "LuaScriptHelper.h"
#include "LuaScriptInterface.h"
#include "LuaSmartRef.h"
#include "PowderToySDL.h"
#include "prefs/GlobalPrefs.h"
#include "common/platform/Platform.h"
#include "graphics/Graphics.h"
#include "graphics/Renderer.h"
#include "simulation/ElementCommon.h"
#include "simulation/gravity/Gravity.h"
#include "simulation/Simulation.h"
#include "simulation/SimulationData.h"
#include "gui/game/GameController.h"
#include "gui/game/GameModel.h"
#include "gui/interface/Engine.h"
#include <iomanip>
#include <vector>
#include <algorithm>
#include <locale>
std::map<ByteString, StructProperty> legacyPropNames;
std::map<ByteString, StructProperty> legacyTransitionNames;
void initLegacyProps()
{
std::vector<StructProperty> properties = Element::GetProperties();
for (auto prop : properties)
{
if (prop.Name == "MenuVisible")
legacyPropNames.insert(std::pair<ByteString, StructProperty>("menu", prop));
else if (prop.Name == "PhotonReflectWavelengths")
continue;
else if (prop.Name == "CarriesTypeIn")
continue;
else if (prop.Name == "Temperature")
legacyPropNames.insert(std::pair<ByteString, StructProperty>("heat", prop));
else if (prop.Name == "HeatConduct")
legacyPropNames.insert(std::pair<ByteString, StructProperty>("hconduct", prop));
// Put all transition stuff into separate map
else if (prop.Name == "LowPressure")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("presLowValue", prop));
else if (prop.Name == "LowPressureTransition")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("presLowType", prop));
else if (prop.Name == "HighPressure")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("presHighValue", prop));
else if (prop.Name == "HighPressureTransition")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("presHighType", prop));
else if (prop.Name == "LowTemperature")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("tempLowValue", prop));
else if (prop.Name == "LowTemperatureTransition")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("tempLowType", prop));
else if (prop.Name == "HighTemperature")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("tempHighValue", prop));
else if (prop.Name == "HighTemperatureTransition")
legacyTransitionNames.insert(std::pair<ByteString, StructProperty>("tempHighType", prop));
else
{
legacyPropNames.insert(std::pair<ByteString, StructProperty>(prop.Name.ToLower(), prop));
}
}
}
#ifndef FFI
int luacon_partread(lua_State* l)
{
int i = cIndex;
if (i < 0 || i >= NPART)
return luaL_error(l, "Out of range");
if (!luacon_sim->parts[i].type)
return luaL_error(l, "Dead particle");
auto &properties = Particle::GetProperties();
auto prop = properties.end();
ByteString fieldName = tpt_lua_toByteString(l, 2);
if (fieldName == "id")
{
lua_pushnumber(l, i);
return 1;
}
for (auto &alias : Particle::GetPropertyAliases())
{
if (fieldName == alias.from)
{
fieldName = alias.to;
}
}
prop = std::find_if(properties.begin(), properties.end(), [&fieldName](StructProperty const &p) {
return p.Name == fieldName;
});
if (prop == properties.end())
return luaL_error(l, "Invalid property");
//Calculate memory address of property
intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->parts[i]) + prop->Offset);
LuaScriptInterface::LuaGetProperty(l, *prop, propertyAddress);
return 1;
}
int luacon_partwrite(lua_State* l)
{
int i = cIndex;
if (i < 0 || i >= NPART)
return luaL_error(l, "Out of range");
if (!luacon_sim->parts[i].type)
return luaL_error(l, "Dead particle");
auto &properties = Particle::GetProperties();
auto prop = properties.end();
ByteString fieldName = tpt_lua_toByteString(l, 2);
for (auto &alias : Particle::GetPropertyAliases())
{
if (fieldName == alias.from)
{
fieldName = alias.to;
}
}
prop = std::find_if(properties.begin(), properties.end(), [&fieldName](StructProperty const &p) {
return p.Name == fieldName;
});
if (prop == properties.end())
return luaL_error(l, "Invalid property");
//Calculate memory address of property
intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->parts[i]) + prop->Offset);
LuaScriptInterface::LuaSetParticleProperty(l, i, *prop, propertyAddress, 3);
return 0;
}
int luacon_partsread(lua_State* l)
{
int i = luaL_optinteger(l, 2, 0);
if (i < 0 || i >= NPART)
return luaL_error(l, "array index out of bounds");
lua_rawgeti(l, LUA_REGISTRYINDEX, *tptPart);
cIndex = i;
return 1;
}
int luacon_partswrite(lua_State* l)
{
return luaL_error(l, "table readonly");
}
#endif
int luacon_transitionread(lua_State* l)
{
ByteString key = tpt_lua_optByteString(l, 2, "");
if (legacyTransitionNames.find(key) == legacyTransitionNames.end())
return luaL_error(l, "Invalid property");
StructProperty prop = legacyTransitionNames[key];
//Get Raw Index value for element
lua_pushliteral(l, "id");
lua_rawget(l, 1);
int i = lua_tointeger (l, lua_gettop(l));
lua_pop(l, 1);
if (!luacon_sim->IsElement(i))
{
return luaL_error(l, "Invalid index");
}
intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->elements[i]) + prop.Offset);
LuaScriptInterface::LuaGetProperty(l, prop, propertyAddress);
return 1;
}
int luacon_transitionwrite(lua_State* l)
{
ByteString key = tpt_lua_optByteString(l, 2, "");
if (legacyTransitionNames.find(key) == legacyTransitionNames.end())
return luaL_error(l, "Invalid property");
StructProperty prop = legacyTransitionNames[key];
//Get Raw Index value for element
lua_pushliteral(l, "id");
lua_rawget(l, 1);
int i = lua_tointeger (l, lua_gettop(l));
lua_pop(l, 1);
if (!luacon_sim->IsElement(i))
{
return luaL_error(l, "Invalid index");
}
if (prop.Type == StructProperty::TransitionType)
{
int type = luaL_checkinteger(l, 3);
if (!luacon_sim->IsElementOrNone(type) && type != NT && type != ST)
{
return luaL_error(l, "Invalid element");
}
}
intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->elements[i]) + prop.Offset);
LuaScriptInterface::LuaSetProperty(l, prop, propertyAddress, 3);
return 0;
}
int luacon_elementread(lua_State* l)
{
ByteString key = tpt_lua_optByteString(l, 2, "");
if (legacyPropNames.find(key) == legacyPropNames.end())
return luaL_error(l, "Invalid property");
StructProperty prop = legacyPropNames[key];
//Get Raw Index value for element
lua_pushliteral(l, "id");
lua_rawget(l, 1);
int i = lua_tointeger (l, lua_gettop(l));
lua_pop(l, 1);
if (!luacon_sim->IsElement(i))
{
return luaL_error(l, "Invalid index");
}
intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->elements[i]) + prop.Offset);
LuaScriptInterface::LuaGetProperty(l, prop, propertyAddress);
return 1;
}
int luacon_elementwrite(lua_State* l)
{
ByteString key = tpt_lua_optByteString(l, 2, "");
if (legacyPropNames.find(key) == legacyPropNames.end())
return luaL_error(l, "Invalid property");
StructProperty prop = legacyPropNames[key];
//Get Raw Index value for element
lua_pushliteral(l, "id");
lua_rawget(l, 1);
int i = lua_tointeger (l, lua_gettop(l));
lua_pop(l, 1);
if (!luacon_sim->IsElement(i))
{
return luaL_error(l, "Invalid index");
}
intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->elements[i]) + prop.Offset);
LuaScriptInterface::LuaSetProperty(l, prop, propertyAddress, 3);
luacon_model->BuildMenus();
luacon_sim->init_can_move();
std::fill(&luacon_ren->graphicscache[0], &luacon_ren->graphicscache[0] + PT_NUM, gcache_item());
return 0;
}
void luacon_hook(lua_State * l, lua_Debug * ar)
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
if (ar->event == LUA_HOOKCOUNT && Platform::GetTime() - luacon_ci->luaExecutionStart > 3000)
{
luaL_error(l, "Error: Script not responding");
luacon_ci->luaExecutionStart = Platform::GetTime();
}
}
String luacon_geterror()
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
luaL_tostring(luacon_ci->l, -1);
String err = tpt_lua_optString(luacon_ci->l, -1, "failed to execute");
lua_pop(luacon_ci->l, 1);
return err;
}
//tpt. api methods
int luatpt_getelement(lua_State *l)
{
int t;
if (lua_isnumber(l, 1))
{
t = luaL_optint(l, 1, 1);
if (!luacon_sim->IsElementOrNone(t))
{
return luaL_error(l, "Unrecognised element number '%d'", t);
}
tpt_lua_pushString(l, luacon_sim->elements[t].Name);
}
else
{
luaL_checktype(l, 1, LUA_TSTRING);
auto name = tpt_lua_optByteString(l, 1, "");
if ((t = luacon_sim->GetParticleType(name))==-1)
return luaL_error(l, "Unrecognised element '%s'", name.c_str());
lua_pushinteger(l, t);
}
return 1;
}
int luatpt_drawtext(lua_State* l)
{
int textx, texty, textred, textgreen, textblue, textalpha;
textx = luaL_optint(l, 1, 0);
texty = luaL_optint(l, 2, 0);
auto string = tpt_lua_optString(l, 3, "");
textred = luaL_optint(l, 4, 255);
textgreen = luaL_optint(l, 5, 255);
textblue = luaL_optint(l, 6, 255);
textalpha = luaL_optint(l, 7, 255);
if (textx<0 || texty<0 || textx>=WINDOWW || texty>=WINDOWH)
return luaL_error(l, "Screen coordinates out of range (%d,%d)", textx, texty);
if (textred<0) textred = 0;
if (textred>255) textred = 255;
if (textgreen<0) textgreen = 0;
if (textgreen>255) textgreen = 255;
if (textblue<0) textblue = 0;
if (textblue>255) textblue = 255;
if (textalpha<0) textalpha = 0;
if (textalpha>255) textalpha = 255;
luacon_g->BlendText({ textx, texty }, string, RGBA<uint8_t>(textred, textgreen, textblue, textalpha));
return 0;
}
int luatpt_create(lua_State* l)
{
int x, y, retid, t = -1;
x = abs(luaL_optint(l, 1, 0));
y = abs(luaL_optint(l, 2, 0));
if(x < XRES && y < YRES)
{
if (lua_isnumber(l, 3))
{
t = luaL_optint(l, 3, 0);
if (!luacon_sim->IsElement(t))
{
return luaL_error(l, "Unrecognised element number '%d'", t);
}
} else {
auto name = tpt_lua_optByteString(l, 3, "dust");
if ((t = luacon_sim->GetParticleType(name)) == -1)
return luaL_error(l,"Unrecognised element '%s'", name.c_str());
}
retid = luacon_sim->create_part(-1, x, y, t);
// failing to create a particle often happens (e.g. if space is already occupied) and isn't usually important, so don't raise an error
lua_pushinteger(l, retid);
return 1;
}
return luaL_error(l, "Coordinates out of range (%d,%d)", x, y);
}
int luatpt_setpause(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushnumber(l, luacon_model->GetPaused());
return 1;
}
int pausestate = luaL_checkinteger(l, 1);
luacon_model->SetPaused(pausestate==0?0:1);
return 0;
}
int luatpt_togglepause(lua_State* l)
{
luacon_model->SetPaused(!luacon_model->GetPaused());
lua_pushnumber(l, luacon_model->GetPaused());
return 1;
}
int luatpt_togglewater(lua_State* l)
{
luacon_sim->water_equal_test=!luacon_sim->water_equal_test;
lua_pushnumber(l, luacon_sim->water_equal_test);
return 1;
}
int luatpt_setconsole(lua_State* l)
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushnumber(l, luacon_ci->Window != ui::Engine::Ref().GetWindow());
return 1;
}
if (luaL_checkinteger(l, 1))
luacon_controller->ShowConsole();
else
luacon_controller->HideConsole();
return 0;
}
int luatpt_log(lua_State* l)
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
int args = lua_gettop(l);
String text;
bool hasText = false;
for(int i = 1; i <= args; i++)
{
luaL_tostring(l, -1);
if (hasText)
{
text = tpt_lua_optString(l, -1, "") + ", " + text;
}
else
{
text = tpt_lua_optString(l, -1, "");
hasText = true;
}
lua_pop(l, 2);
}
if ((*luacon_currentCommand))
{
if (luacon_hasLastError)
*luacon_lastError += "; ";
*luacon_lastError += text;
luacon_hasLastError = true;
}
else
luacon_ci->Log(CommandInterface::LogNotice, text);
return 0;
}
int luatpt_set_pressure(lua_State* l)
{
int nx, ny;
int x1, y1, width, height;
float value;
x1 = abs(luaL_optint(l, 1, 0));
y1 = abs(luaL_optint(l, 2, 0));
width = abs(luaL_optint(l, 3, XCELLS));
height = abs(luaL_optint(l, 4, YCELLS));
value = luaL_optnumber(l, 5, 0.0f);
if(value > MAX_PRESSURE)
value = MAX_PRESSURE;
else if(value < MIN_PRESSURE)
value = MIN_PRESSURE;
if(x1 > XCELLS-1)
x1 = XCELLS-1;
if(y1 > YCELLS-1)
y1 = YCELLS-1;
if(x1+width > XCELLS-1)
width = XCELLS-x1;
if(y1+height > YCELLS-1)
height = YCELLS-y1;
for (nx = x1; nx<x1+width; nx++)
for (ny = y1; ny<y1+height; ny++)
{
luacon_sim->pv[ny][nx] = value;
}
return 0;
}
int luatpt_set_gravity(lua_State* l)
{
int nx, ny;
int x1, y1, width, height;
float value;
x1 = abs(luaL_optint(l, 1, 0));
y1 = abs(luaL_optint(l, 2, 0));
width = abs(luaL_optint(l, 3, XCELLS));
height = abs(luaL_optint(l, 4, YCELLS));
value = luaL_optnumber(l, 5, 0.0f);
if(value > MAX_PRESSURE)
value = MAX_PRESSURE;
else if(value < MIN_PRESSURE)
value = MIN_PRESSURE;
if(x1 > XCELLS-1)
x1 = XCELLS-1;
if(y1 > YCELLS-1)
y1 = YCELLS-1;
if(x1+width > XCELLS-1)
width = XCELLS-x1;
if(y1+height > YCELLS-1)
height = YCELLS-y1;
for (nx = x1; nx<x1+width; nx++)
for (ny = y1; ny<y1+height; ny++)
{
luacon_sim->gravmap[ny*XCELLS+nx] = value;
}
return 0;
}
int luatpt_reset_gravity_field(lua_State* l)
{
int nx, ny;
int x1, y1, width, height;
x1 = abs(luaL_optint(l, 1, 0));
y1 = abs(luaL_optint(l, 2, 0));
width = abs(luaL_optint(l, 3, XCELLS));
height = abs(luaL_optint(l, 4, YCELLS));
if(x1 > XCELLS-1)
x1 = XCELLS-1;
if(y1 > YCELLS-1)
y1 = YCELLS-1;
if(x1+width > XCELLS-1)
width = XCELLS-x1;
if(y1+height > YCELLS-1)
height = YCELLS-y1;
for (nx = x1; nx<x1+width; nx++)
for (ny = y1; ny<y1+height; ny++)
{
luacon_sim->gravx[ny*XCELLS+nx] = 0;
luacon_sim->gravy[ny*XCELLS+nx] = 0;
luacon_sim->gravp[ny*XCELLS+nx] = 0;
}
return 0;
}
int luatpt_reset_velocity(lua_State* l)
{
int nx, ny;
int x1, y1, width, height;
x1 = abs(luaL_optint(l, 1, 0));
y1 = abs(luaL_optint(l, 2, 0));
width = abs(luaL_optint(l, 3, XCELLS));
height = abs(luaL_optint(l, 4, YCELLS));
if(x1 > XCELLS-1)
x1 = XCELLS-1;
if(y1 > YCELLS-1)
y1 = YCELLS-1;
if(x1+width > XCELLS-1)
width = XCELLS-x1;
if(y1+height > YCELLS-1)
height = YCELLS-y1;
for (nx = x1; nx<x1+width; nx++)
for (ny = y1; ny<y1+height; ny++)
{
luacon_sim->vx[ny][nx] = 0;
luacon_sim->vy[ny][nx] = 0;
}
return 0;
}
int luatpt_reset_spark(lua_State* l)
{
luacon_controller->ResetSpark();
return 0;
}
int luatpt_set_property(lua_State* l)
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
int r, i, x, y, w, h, t = 0, nx, ny, partsel = 0;
float f = 0;
int acount = lua_gettop(l);
auto prop = tpt_lua_optByteString(l, 1, "");
CommandInterface::FormatType format;
int offset = luacon_ci->GetPropertyOffset(prop, format);
if (offset == -1)
return luaL_error(l, "Invalid property '%s'", prop.c_str());
bool isX = byteStringEqualsLiteral(prop, "x");
bool isY = byteStringEqualsLiteral(prop, "y");
if (acount > 2)
{
if(!lua_isnumber(l, acount) && lua_isstring(l, acount))
{
auto name = tpt_lua_optByteString(l, acount, "none");
if ((partsel = luacon_sim->GetParticleType(name)) == -1)
return luaL_error(l, "Unrecognised element '%s'", name.c_str());
}
}
if (lua_isnumber(l, 2))
{
if (format == CommandInterface::FormatFloat)
f = luaL_optnumber(l, 2, 0);
else
t = luaL_optint(l, 2, 0);
if (byteStringEqualsLiteral(prop, "type") && !luacon_sim->IsElementOrNone(t))
return luaL_error(l, "Unrecognised element number '%d'", t);
}
else if (lua_isstring(l, 2))
{
auto name = tpt_lua_checkByteString(l, 2);
if ((t = luacon_sim->GetParticleType(name))==-1)
return luaL_error(l, "Unrecognised element '%s'", name.c_str());
}
else
luaL_error(l, "Expected number or element name as argument 2");
if (!lua_isnumber(l, 3) || acount >= 6)
{
// Got a region
if (acount < 6)
{
i = 0;
y = 0;
w = XRES;
h = YRES;
}
else
{
i = abs(luaL_checkint(l, 3));
y = abs(luaL_checkint(l, 4));
w = abs(luaL_checkint(l, 5));
h = abs(luaL_checkint(l, 6));
}
if (i>=XRES || y>=YRES)
return luaL_error(l, "Coordinates out of range (%d,%d)", i, y);
x = i;
if(x+w > XRES)
w = XRES-x;
if(y+h > YRES)
h = YRES-y;
Particle * parts = luacon_sim->parts;
for (i = 0; i < NPART; i++)
{
if (parts[i].type)
{
nx = (int)(parts[i].x + .5f);
ny = (int)(parts[i].y + .5f);
if (nx >= x && nx < x+w && ny >= y && ny < y+h && (!partsel || partsel == parts[i].type))
{
if (format == CommandInterface::FormatElement)
luacon_sim->part_change_type(i, nx, ny, t);
else if (isX || isY)
{
float x = luacon_sim->parts[i].x;
float y = luacon_sim->parts[i].y;
float nx = isX ? f : x;
float ny = isY ? f : y;
luacon_sim->move(i, (int)(x + 0.5f), (int)(y + 0.5f), nx, ny);
}
else if(format == CommandInterface::FormatFloat)
*((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = f;
else
*((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = t;
}
}
}
}
else
{
i = abs(luaL_checkint(l, 3));
// Got coords or particle index
if (lua_isnumber(l, 4))
{
y = abs(luaL_checkint(l, 4));
if (i>=XRES || y>=YRES)
return luaL_error(l, "Coordinates out of range (%d,%d)", i, y);
r = luacon_sim->pmap[y][i];
if (!r || (partsel && partsel != TYP(r)))
r = luacon_sim->photons[y][i];
if (!r || (partsel && partsel != TYP(r)))
return 0;
i = ID(r);
}
if (i < 0 || i >= NPART)
return luaL_error(l, "Invalid particle ID '%d'", i);
if (!luacon_sim->parts[i].type)
return 0;
if (partsel && partsel != luacon_sim->parts[i].type)
return 0;
if (format == CommandInterface::FormatElement)
luacon_sim->part_change_type(i, int(luacon_sim->parts[i].x + 0.5f), int(luacon_sim->parts[i].y + 0.5f), t);
else if (isX || isY)
{
float x = luacon_sim->parts[i].x;
float y = luacon_sim->parts[i].y;
float nx = isX ? f : x;
float ny = isY ? f : y;
luacon_sim->move(i, (int)(x + 0.5f), (int)(y + 0.5f), nx, ny);
}
else if (format == CommandInterface::FormatFloat)
*((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = f;
else
*((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = t;
}
return 0;
}
int luatpt_get_wallmap(lua_State* l)
{
int x1 = abs(luaL_optint(l, 1, 0));
int y1 = abs(luaL_optint(l, 2, 0));
if(x1 > XCELLS || y1 > YCELLS)
return luaL_error(l, "Out of range");
lua_pushinteger(l, luacon_sim->bmap[y1][x1]);
return 1;
}
int luatpt_set_wallmap(lua_State* l)
{
int args = lua_gettop(l);
if (args < 3 || args > 7 || args % 2 != 1)
return luaL_error(l, "Incorrect numbner of arguments");
int x = luaL_optint(l, 1, 0);
int y = luaL_optint(l, 2, 0);
int w = luaL_optint(l, 3, 0);
int h = luaL_optint(l, 4, 0);
float fvx = float(luaL_optnumber(l, 5, 0));
float fvy = float(luaL_optnumber(l, 6, 0));
int wallType = luaL_optint(l, args, 0);
if (wallType < 0 || wallType >= UI_WALLCOUNT)
{
return luaL_error(l, "Unrecognised wall number %d", wallType);
}
bool setFv = args == 7;
if (args < 5)
{
w = 1;
h = 1;
}
if (x < 0 ) x = 0 ;
if (y < 0 ) y = 0 ;
if (x > XCELLS ) x = XCELLS ;
if (y > YCELLS ) y = YCELLS ;
if (w > XCELLS - x) w = XCELLS - x;
if (h > YCELLS - y) h = YCELLS - y;
for (int yy = y; yy < y + h; ++yy)
{
for (int xx = x; xx < x + w; ++xx)
{
luacon_sim->bmap[yy][xx] = wallType;
if (setFv)
{
luacon_sim->fvx[yy][xx] = fvx;
luacon_sim->fvy[yy][xx] = fvy;
}
}
}
return 0;
}
int luatpt_set_elecmap(lua_State* l)
{
int nx, ny, acount;
int x1, y1, width, height;
unsigned char value;
acount = lua_gettop(l);
x1 = abs(luaL_optint(l, 1, 0));
y1 = abs(luaL_optint(l, 2, 0));
width = abs(luaL_optint(l, 3, XCELLS));
height = abs(luaL_optint(l, 4, YCELLS));
value = luaL_optint(l, acount, 0);
if(acount==5) //Draw rect
{
if(x1 > XCELLS)
x1 = XCELLS;
if(y1 > YCELLS)
y1 = YCELLS;
if(x1+width > XCELLS)
width = XCELLS-x1;
if(y1+height > YCELLS)
height = YCELLS-y1;
for (nx = x1; nx<x1+width; nx++)
for (ny = y1; ny<y1+height; ny++)
{
luacon_sim->emap[ny][nx] = value;
}
}
else //Set point
{
if(x1 > XCELLS)
x1 = XCELLS;
if(y1 > YCELLS)
y1 = YCELLS;
luacon_sim->emap[y1][x1] = value;
}
return 0;
}
int luatpt_get_elecmap(lua_State* l)
{
int x1 = abs(luaL_optint(l, 1, 0));
int y1 = abs(luaL_optint(l, 2, 0));
if(x1 > XCELLS || y1 > YCELLS)
return luaL_error(l, "Out of range");
lua_pushinteger(l, luacon_sim->emap[y1][x1]);
return 1;
}
int luatpt_get_property(lua_State* l)
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
ByteString prop = tpt_lua_optByteString(l, 1, "");
int i = luaL_optint(l, 2, 0); //x coord or particle index, depending on arguments
int y = luaL_optint(l, 3, -1);
if (y!=-1 && y<YRES && y>=0 && i < XRES && i>=0)
{
int r = luacon_sim->pmap[y][i];
if (!r)
{
r = luacon_sim->photons[y][i];
if (!r)
{
if (byteStringEqualsLiteral(prop, "type"))
{
lua_pushinteger(l, 0);
return 1;
}
return luaL_error(l, "Particle does not exist");
}
}
i = ID(r);
}
else if (y != -1)
return luaL_error(l, "Coordinates out of range (%d,%d)", i, y);
if (i < 0 || i >= NPART)
return luaL_error(l, "Invalid particle ID '%d'", i);
if (luacon_sim->parts[i].type)
{
int tempinteger;
float tempfloat;
CommandInterface::FormatType format;
int offset = luacon_ci->GetPropertyOffset(prop, format);
if (offset == -1)
{
if (byteStringEqualsLiteral(prop, "id"))
{
lua_pushnumber(l, i);
return 1;
}
else
return luaL_error(l, "Invalid property");
}
switch(format)
{
case CommandInterface::FormatInt:
case CommandInterface::FormatElement:
tempinteger = *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset));
lua_pushnumber(l, tempinteger);
break;
case CommandInterface::FormatFloat:
tempfloat = *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset));
lua_pushnumber(l, tempfloat);
break;
default:
break;
}
return 1;
}
else if (byteStringEqualsLiteral(prop, "type"))
{
lua_pushinteger(l, 0);
return 1;
}
return luaL_error(l, "Particle does not exist");
}
int luatpt_drawpixel(lua_State* l)
{
int x, y, r, g, b, a;
x = luaL_optint(l, 1, 0);
y = luaL_optint(l, 2, 0);
r = luaL_optint(l, 3, 255);
g = luaL_optint(l, 4, 255);
b = luaL_optint(l, 5, 255);
a = luaL_optint(l, 6, 255);
if (x<0 || y<0 || x>=WINDOWW || y>=WINDOWH)
return luaL_error(l, "Screen coordinates out of range (%d,%d)", x, y);
if (r<0) r = 0;
else if (r>255) r = 255;
if (g<0) g = 0;
else if (g>255) g = 255;
if (b<0) b = 0;
else if (b>255) b = 255;
if (a<0) a = 0;
else if (a>255) a = 255;
luacon_g->BlendPixel({ x, y }, RGBA<uint8_t>(r, g, b, a));
return 0;
}
int luatpt_drawrect(lua_State* l)
{
int x, y, w, h, r, g, b, a;
x = luaL_optint(l, 1, 0);
y = luaL_optint(l, 2, 0);
w = luaL_optint(l, 3, 10)+1;
h = luaL_optint(l, 4, 10)+1;
r = luaL_optint(l, 5, 255);
g = luaL_optint(l, 6, 255);
b = luaL_optint(l, 7, 255);
a = luaL_optint(l, 8, 255);
if (x<0 || y<0 || x>=WINDOWW || y>=WINDOWH)
return luaL_error(l, "Screen coordinates out of range (%d,%d)", x, y);
if(x+w > WINDOWW)
w = WINDOWW-x;
if(y+h > WINDOWH)
h = WINDOWH-y;
if (r<0) r = 0;
else if (r>255) r = 255;
if (g<0) g = 0;
else if (g>255) g = 255;
if (b<0) b = 0;
else if (b>255) b = 255;
if (a<0) a = 0;
else if (a>255) a = 255;
if (a == 255)
{
luacon_g->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGB<uint8_t>(r, g, b));
}
else
{
luacon_g->BlendRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGBA<uint8_t>(r, g, b, a));
}
return 0;
}
int luatpt_fillrect(lua_State* l)
{
int x,y,w,h,r,g,b,a;
x = luaL_optint(l, 1, 0)+1;
y = luaL_optint(l, 2, 0)+1;
w = luaL_optint(l, 3, 10)-1;
h = luaL_optint(l, 4, 10)-1;
r = luaL_optint(l, 5, 255);
g = luaL_optint(l, 6, 255);
b = luaL_optint(l, 7, 255);
a = luaL_optint(l, 8, 255);
if (x<0 || y<0 || x>=WINDOWW || y>=WINDOWH)
return luaL_error(l, "Screen coordinates out of range (%d,%d)", x, y);
if(x+w > WINDOWW)
w = WINDOWW-x;
if(y+h > WINDOWH)
h = WINDOWH-y;
if (r<0) r = 0;
else if (r>255) r = 255;
if (g<0) g = 0;
else if (g>255) g = 255;
if (b<0) b = 0;
else if (b>255) b = 255;
if (a<0) a = 0;
else if (a>255) a = 255;
if (a == 255)
{
luacon_g->DrawFilledRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGB<uint8_t>(r, g, b));
}
else
{
luacon_g->BlendFilledRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGBA<uint8_t>(r, g, b, a));
}
return 0;
}
int luatpt_drawline(lua_State* l)
{
int x1,y1,x2,y2,r,g,b,a;
x1 = luaL_optint(l, 1, 0);
y1 = luaL_optint(l, 2, 0);
x2 = luaL_optint(l, 3, 10);
y2 = luaL_optint(l, 4, 10);
r = luaL_optint(l, 5, 255);
g = luaL_optint(l, 6, 255);
b = luaL_optint(l, 7, 255);
a = luaL_optint(l, 8, 255);
//Don't need to check coordinates, as they are checked in blendpixel
if (r<0) r = 0;
else if (r>255) r = 255;
if (g<0) g = 0;
else if (g>255) g = 255;
if (b<0) b = 0;
else if (b>255) b = 255;
if (a<0) a = 0;
else if (a>255) a = 255;
if (a == 255)
{
luacon_g->DrawLine({ x1, y1 }, { x2, y2 }, RGB<uint8_t>(r, g, b));
}
else
{
luacon_g->BlendLine({ x1, y1 }, { x2, y2 }, RGBA<uint8_t>(r, g, b, a));
}
return 0;
}
int luatpt_textwidth(lua_State* l)
{
auto string = tpt_lua_optString(l, 1, "");
int strwidth = Graphics::TextSize(string).X - 1;
lua_pushinteger(l, strwidth);
return 1;
}
int luatpt_get_name(lua_State* l)
{
if (luacon_model->GetUser().UserID)
{
tpt_lua_pushByteString(l, luacon_model->GetUser().Username);
return 1;
}
lua_pushliteral(l, "");
return 1;
}
int luatpt_delete(lua_State* l)
{
int arg1, arg2;
arg1 = abs(luaL_optint(l, 1, 0));
arg2 = luaL_optint(l, 2, -1);
if (arg2 == -1 && arg1 < NPART)
{
luacon_sim->kill_part(arg1);
return 0;
}
arg2 = abs(arg2);
if(arg2 < YRES && arg1 < XRES)
{
luacon_sim->delete_part(arg1, arg2);
return 0;
}
return luaL_error(l,"Invalid coordinates or particle ID");
}
int luatpt_get_numOfParts(lua_State* l)
{
lua_pushinteger(l, luacon_sim->NUM_PARTS);
return 1;
}
int luatpt_start_getPartIndex(lua_State* l)
{
getPartIndex_curIdx = -1;
return 0;
}
int luatpt_next_getPartIndex(lua_State* l)
{
while(1)
{
getPartIndex_curIdx++;
if (getPartIndex_curIdx >= NPART)
{
getPartIndex_curIdx = -1;
lua_pushboolean(l, 0);
return 1;
}
if (luacon_sim->parts[getPartIndex_curIdx].type)
break;
}
lua_pushboolean(l, 1);
return 1;
}
int luatpt_getPartIndex(lua_State* l)
{
if(getPartIndex_curIdx < 0)
{
lua_pushinteger(l, -1);
return 1;
}
lua_pushinteger(l, getPartIndex_curIdx);
return 1;
}
int luatpt_hud(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, luacon_controller->GetHudEnable());
return 1;
}
int hudstate = luaL_checkint(l, 1);
if (hudstate)
luacon_controller->SetHudEnable(1);
else
luacon_controller->SetHudEnable(0);
return 0;
}
int luatpt_gravity(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, luacon_sim->grav->IsEnabled() ? 1 : 0);
return 1;
}
int gravstate = luaL_checkint(l, 1);
if(gravstate)
luacon_sim->grav->start_grav_async();
else
luacon_sim->grav->stop_grav_async();
luacon_model->UpdateQuickOptions();
return 0;
}
int luatpt_airheat(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, luacon_sim->aheat_enable);
return 1;
}
int aheatstate = luaL_checkint(l, 1);
luacon_sim->aheat_enable = (aheatstate==0?0:1);
luacon_model->UpdateQuickOptions();
return 0;
}
int luatpt_active_menu(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, luacon_model->GetActiveMenu());
return 1;
}
int menuid = luaL_checkint(l, 1);
if (menuid >= 0 && menuid < SC_TOTAL)
luacon_controller->SetActiveMenu(menuid);
else
return luaL_error(l, "Invalid menu");
return 0;
}
int luatpt_menu_enabled(lua_State* l)
{
int menusection = luaL_checkint(l, 1);
if (menusection < 0 || menusection >= SC_TOTAL)
return luaL_error(l, "Invalid menu");
int acount = lua_gettop(l);
if (acount == 1)
{
lua_pushboolean(l, luacon_sim->msections[menusection].doshow);
return 1;
}
luaL_checktype(l, 2, LUA_TBOOLEAN);
int enabled = lua_toboolean(l, 2);
luacon_sim->msections[menusection].doshow = enabled;
luacon_model->BuildMenus();
return 0;
}
int luatpt_num_menus(lua_State* l)
{
int acount = lua_gettop(l);
bool onlyEnabled = true;
if (acount > 0)
{
luaL_checktype(l, 1, LUA_TBOOLEAN);
onlyEnabled = lua_toboolean(l, 1);
}
lua_pushinteger(l, luacon_controller->GetNumMenus(onlyEnabled));
return 1;
}
int luatpt_decorations_enable(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, luacon_model->GetDecoration());
return 1;
}
int decostate = luaL_checkint(l, 1);
luacon_model->SetDecoration(decostate==0?false:true);
luacon_model->UpdateQuickOptions();
return 0;
}
int luatpt_heat(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, !luacon_sim->legacy_enable);
return 1;
}
int heatstate = luaL_checkint(l, 1);
luacon_sim->legacy_enable = (heatstate==1?0:1);
return 0;
}
int luatpt_cmode_set(lua_State* l)
{
int cmode = luaL_optint(l, 1, 3)+1;
if (cmode == 11)
cmode = 0;
if (cmode >= 0 && cmode <= 10)
luacon_controller->LoadRenderPreset(cmode);
else
return luaL_error(l, "Invalid display mode");
return 0;
}
int luatpt_setfire(lua_State* l)
{
int firesize = luaL_optint(l, 2, 4);
float fireintensity = (float)luaL_optnumber(l, 1, 1.0f);
luacon_model->GetRenderer()->prepare_alpha(firesize, fireintensity);
return 0;
}
int luatpt_setdebug(lua_State* l)
{
int debugFlags = luaL_optint(l, 1, 0);
luacon_controller->SetDebugFlags(debugFlags);
return 0;
}
int luatpt_setfpscap(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
auto fpsLimit = ui::Engine::Ref().GetFpsLimit();
if (std::holds_alternative<FpsLimitVsync>(fpsLimit))
{
lua_pushliteral(l, "vsync");
}
else if (std::holds_alternative<FpsLimitNone>(fpsLimit))
{
lua_pushnumber(l, 2);
}
else
{
lua_pushnumber(l, std::get<FpsLimitExplicit>(fpsLimit).value);
}
return 1;
}
if (lua_isstring(l, 1) && byteStringEqualsLiteral(tpt_lua_toByteString(l, 1), "vsync"))
{
ui::Engine::Ref().SetFpsLimit(FpsLimitVsync{});
return 0;
}
float fpscap = luaL_checknumber(l, 1);
if (fpscap < 2)
{
return luaL_error(l, "fps cap too small");
}
if (fpscap == 2)
{
ui::Engine::Ref().SetFpsLimit(FpsLimitNone{});
return 0;
}
ui::Engine::Ref().SetFpsLimit(FpsLimitExplicit{ fpscap });
return 0;
}
int luatpt_setdrawcap(lua_State* l)
{
int acount = lua_gettop(l);
if (acount == 0)
{
lua_pushinteger(l, ui::Engine::Ref().GetDrawingFrequencyLimit());
return 1;
}
int drawcap = luaL_checkint(l, 1);
if(drawcap < 0)
return luaL_error(l, "draw cap too small");
ui::Engine::Ref().SetDrawingFrequencyLimit(drawcap);
return 0;
}
int luatpt_setwindowsize(lua_State* l)
{
int scale = luaL_optint(l,1,1);
int kiosk = luaL_optint(l,2,0);
// TODO: handle this the same way as it's handled in PowderToySDL.cpp
// > maybe bind the maximum allowed scale to screen size somehow
if (scale < 1 || scale > 10)
{
scale = 1;
}
if (kiosk!=1)
{
kiosk = 0;
}
{
auto &prefs = GlobalPrefs::Ref();
Prefs::DeferWrite dw(prefs);
prefs.Set("Scale", scale);
prefs.Set("Fullscreen", bool(kiosk));
}
ui::Engine::Ref().SetScale(scale);
ui::Engine::Ref().SetFullscreen(kiosk);
return 0;
}
int screenshotIndex = 0;
int luatpt_screenshot(lua_State* l)
{
int captureUI = luaL_optint(l, 1, 0);
int fileType = luaL_optint(l, 2, 0);
ByteString filename = luacon_controller->TakeScreenshot(captureUI, fileType);
if (filename.size())
{
tpt_lua_pushByteString(l, filename);
return 1;
}
return 0;
}
int luatpt_record(lua_State* l)
{
if (!lua_isboolean(l, -1))
return luaL_typerror(l, 1, lua_typename(l, LUA_TBOOLEAN));
bool record = lua_toboolean(l, -1);
int recordingFolder = luacon_controller->Record(record);
lua_pushinteger(l, recordingFolder);
return 1;
}
int luatpt_perfectCircle(lua_State* l)
{
if (!lua_gettop(l))
{
lua_pushboolean(l, luacon_model->GetPerfectCircle());
return 1;
}
luaL_checktype(l, 1, LUA_TBOOLEAN);
luacon_model->SetPerfectCircle(lua_toboolean(l, 1));
return 0;
}