#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 #include #include #include std::map legacyPropNames; std::map legacyTransitionNames; void initLegacyProps() { std::vector properties = Element::GetProperties(); for (auto prop : properties) { if (prop.Name == "MenuVisible") legacyPropNames.insert(std::pair("menu", prop)); else if (prop.Name == "PhotonReflectWavelengths") continue; else if (prop.Name == "CarriesTypeIn") continue; else if (prop.Name == "Temperature") legacyPropNames.insert(std::pair("heat", prop)); else if (prop.Name == "HeatConduct") legacyPropNames.insert(std::pair("hconduct", prop)); // Put all transition stuff into separate map else if (prop.Name == "LowPressure") legacyTransitionNames.insert(std::pair("presLowValue", prop)); else if (prop.Name == "LowPressureTransition") legacyTransitionNames.insert(std::pair("presLowType", prop)); else if (prop.Name == "HighPressure") legacyTransitionNames.insert(std::pair("presHighValue", prop)); else if (prop.Name == "HighPressureTransition") legacyTransitionNames.insert(std::pair("presHighType", prop)); else if (prop.Name == "LowTemperature") legacyTransitionNames.insert(std::pair("tempLowValue", prop)); else if (prop.Name == "LowTemperatureTransition") legacyTransitionNames.insert(std::pair("tempLowType", prop)); else if (prop.Name == "HighTemperature") legacyTransitionNames.insert(std::pair("tempHighValue", prop)); else if (prop.Name == "HighTemperatureTransition") legacyTransitionNames.insert(std::pair("tempHighType", prop)); else { legacyPropNames.insert(std::pair(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(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(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(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(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(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; nxpv[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; nxgravmap[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; nxgravx[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; nxvx[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(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; nxemap[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(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=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(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(r, g, b)); } else { luacon_g->BlendRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGBA(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(r, g, b)); } else { luacon_g->BlendFilledRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGBA(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(r, g, b)); } else { luacon_g->BlendLine({ x1, y1 }, { x2, y2 }, RGBA(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(fpsLimit)) { lua_pushliteral(l, "vsync"); } else if (std::holds_alternative(fpsLimit)) { lua_pushnumber(l, 2); } else { lua_pushnumber(l, std::get(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; }