/* Minetest Copyright (C) 2021 velartrill, Lexi Hale This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "lua_api/l_particles.h" #include "lua_api/l_object.h" #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" #include "particles.h" namespace LuaParticleParams { using namespace ParticleParamTypes; template inline void readNumericLuaValue(lua_State* L, T& ret) { if (lua_isnil(L,-1)) return; if (std::is_integral()) ret = lua_tointeger(L, -1); else ret = lua_tonumber(L, -1); } template inline void readNumericLuaValue(lua_State* L, Parameter& ret) { readNumericLuaValue(L, ret.val); } // these are unfortunately necessary as C++ intentionally disallows function template // specialization and there's no way to make template overloads reliably resolve correctly inline void readLuaValue(lua_State* L, f32Parameter& ret) { readNumericLuaValue(L, ret); } inline void readLuaValue(lua_State* L, f32& ret) { readNumericLuaValue(L, ret); } inline void readLuaValue(lua_State* L, u16& ret) { readNumericLuaValue(L, ret); } inline void readLuaValue(lua_State* L, u8& ret) { readNumericLuaValue(L, ret); } inline void readLuaValue(lua_State* L, v3fParameter& ret) { if (lua_isnil(L, -1)) return; if (lua_isnumber(L, -1)) { // shortcut for uniform vectors auto n = lua_tonumber(L, -1); ret = v3fParameter(n,n,n); } else { ret = (v3fParameter)check_v3f(L, -1); } } inline void readLuaValue(lua_State* L, v2fParameter& ret) { if (lua_isnil(L, -1)) return; if (lua_isnumber(L, -1)) { // shortcut for uniform vectors auto n = lua_tonumber(L, -1); ret = v2fParameter(n,n); } else { ret = (v2fParameter)check_v2f(L, -1); } } inline void readLuaValue(lua_State* L, TweenStyle& ret) { if (lua_isnil(L, -1)) return; static const EnumString opts[] = { {(int)TweenStyle::fwd, "fwd"}, {(int)TweenStyle::rev, "rev"}, {(int)TweenStyle::pulse, "pulse"}, {(int)TweenStyle::flicker, "flicker"}, {0, nullptr}, }; luaL_checktype(L, -1, LUA_TSTRING); int v = (int)TweenStyle::fwd; if (!string_to_enum(opts, v, lua_tostring(L, -1))) { throw LuaError("tween style must be one of ('fwd', 'rev', 'pulse', 'flicker')"); } ret = (TweenStyle)v; } inline void readLuaValue(lua_State* L, AttractorKind& ret) { if (lua_isnil(L, -1)) return; static const EnumString opts[] = { {(int)AttractorKind::none, "none"}, {(int)AttractorKind::point, "point"}, {(int)AttractorKind::line, "line"}, {(int)AttractorKind::plane, "plane"}, {0, nullptr}, }; luaL_checktype(L, -1, LUA_TSTRING); int v = (int)AttractorKind::none; if (!string_to_enum(opts, v, lua_tostring(L, -1))) { throw LuaError("attractor kind must be one of ('none', 'point', 'line', 'plane')"); } ret = (AttractorKind)v; } inline void readLuaValue(lua_State* L, BlendMode& ret) { if (lua_isnil(L, -1)) return; static const EnumString opts[] = { {(int)BlendMode::alpha, "alpha"}, {(int)BlendMode::add, "add"}, {(int)BlendMode::sub, "sub"}, {(int)BlendMode::screen, "screen"}, {0, nullptr}, }; luaL_checktype(L, -1, LUA_TSTRING); int v = (int)BlendMode::alpha; if (!string_to_enum(opts, v, lua_tostring(L, -1))) { throw LuaError("blend mode must be one of ('alpha', 'add', 'sub', 'screen')"); } ret = (BlendMode)v; } template void readLuaValue(lua_State* L, RangedParameter& field) { if (lua_isnil(L,-1)) return; if (!lua_istable(L,-1)) // is this is just a literal value? goto set_uniform; lua_getfield(L, -1, "min"); // handle convenience syntax for non-range values if (lua_isnil(L,-1)) { lua_pop(L, 1); goto set_uniform; } readLuaValue(L,field.min); lua_pop(L, 1); lua_getfield(L, -1, "max"); readLuaValue(L,field.max); lua_pop(L, 1); lua_getfield(L, -1, "bias"); if (!lua_isnil(L,-1)) readLuaValue(L,field.bias); lua_pop(L, 1); return; set_uniform: readLuaValue(L, field.min); readLuaValue(L, field.max); } template void readLegacyValue(lua_State* L, const char* name, T& field) {} template void readLegacyValue(lua_State* L, const char* name, RangedParameter& field) { int tbl = lua_gettop(L); lua_pushliteral(L, "min"); lua_pushstring(L, name); lua_concat(L, 2); lua_gettable(L, tbl); if (!lua_isnil(L, -1)) { readLuaValue(L, field.min); } lua_settop(L, tbl); lua_pushliteral(L, "max"); lua_pushstring(L, name); lua_concat(L, 2); lua_gettable(L, tbl); if (!lua_isnil(L, -1)) { readLuaValue(L, field.max); } lua_settop(L, tbl); } template void readTweenTable(lua_State* L, const char* name, TweenedParameter& field) { int tbl = lua_gettop(L); lua_pushstring(L, name); lua_pushliteral(L, "_tween"); lua_concat(L, 2); lua_gettable(L, tbl); if(lua_istable(L, -1)) { int tween = lua_gettop(L); // get the starting value lua_pushinteger(L, 1), lua_gettable(L, tween); readLuaValue(L, field.start); lua_pop(L, 1); // get the final value -- use len instead of 2 so that this // gracefully degrades if keyframe support is later added lua_pushinteger(L, (lua_Integer)lua_objlen(L, -1)), lua_gettable(L, tween); readLuaValue(L, field.end); lua_pop(L, 1); // get the effect settings lua_getfield(L, -1, "style"); if (!lua_isnil(L,-1)) readLuaValue(L, field.style); lua_pop(L, 1); lua_getfield(L, -1, "reps"); if (!lua_isnil(L,-1)) readLuaValue(L, field.reps); lua_pop(L, 1); lua_getfield(L, -1, "start"); if (!lua_isnil(L,-1)) readLuaValue(L, field.beginning); lua_pop(L, 1); goto done; } else { lua_pop(L,1); } // the table is not present; check for nonanimated values lua_getfield(L, tbl, name); if(!lua_isnil(L, -1)) { readLuaValue(L, field.start); lua_settop(L, tbl); goto set_uniform; } else { lua_pop(L,1); } // the goto did not trigger, so this table is not present either // check for pre-5.6.0 legacy values readLegacyValue(L, name, field.start); set_uniform: field.end = field.start; done: lua_settop(L, tbl); // clean up after ourselves } inline u16 readAttachmentID(lua_State* L, const char* name) { u16 id = 0; lua_getfield(L, -1, name); if (!lua_isnil(L, -1)) { ObjectRef *ref = ModApiBase::checkObject(L, -1); if (auto obj = ObjectRef::getobject(ref)) id = obj->getId(); } lua_pop(L, 1); return id; } void readTexValue(lua_State* L, ServerParticleTexture& tex); }