From 20bd6bdb685af11548c35d3a48e5aa33f4222397 Mon Sep 17 00:00:00 2001 From: Lexi Hale <5723574+velartrill@users.noreply.github.com> Date: Wed, 13 Jul 2022 11:57:12 +0200 Subject: Animated particlespawners and more (#11545) Co-authored-by: Lars Mueller Co-authored-by: sfan5 Co-authored-by: Dmitry Kostenko --- src/script/lua_api/l_particleparams.h | 279 +++++++++++++++++++++++++++++++ src/script/lua_api/l_particles.cpp | 214 +++++++++++++++--------- src/script/lua_api/l_particles_local.cpp | 114 +++++++++---- 3 files changed, 488 insertions(+), 119 deletions(-) create mode 100644 src/script/lua_api/l_particleparams.h (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_particleparams.h b/src/script/lua_api/l_particleparams.h new file mode 100644 index 000000000..4fefc5e3a --- /dev/null +++ b/src/script/lua_api/l_particleparams.h @@ -0,0 +1,279 @@ +/* +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"); + lua_isnil(L,-1) || (readLuaValue(L, field.style), true); + lua_pop(L, 1); + + lua_getfield(L, -1, "reps"); + lua_isnil(L,-1) || (readLuaValue(L, field.reps), true); + lua_pop(L, 1); + + lua_getfield(L, -1, "start"); + lua_isnil(L,-1) || (readLuaValue(L, field.beginning), true); + 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 = ObjectRef::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); +} diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index a51c4fe20..586c7dc73 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -20,30 +20,50 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_particles.h" #include "lua_api/l_object.h" #include "lua_api/l_internal.h" +#include "lua_api/l_particleparams.h" #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" #include "particles.h" -// add_particle({pos=, velocity=, acceleration=, expirationtime=, -// size=, collisiondetection=, collision_removal=, object_collision=, -// vertical=, texture=, player=}) -// pos/velocity/acceleration = {x=num, y=num, z=num} -// expirationtime = num (seconds) -// size = num -// collisiondetection = bool -// collision_removal = bool -// object_collision = bool -// vertical = bool -// texture = e.g."default_wood.png" -// animation = TileAnimation definition -// glow = num +void LuaParticleParams::readTexValue(lua_State* L, ServerParticleTexture& tex) +{ + StackUnroller unroll(L); + + tex.animated = false; + if (lua_isstring(L, -1)) { + tex.string = lua_tostring(L, -1); + return; + } + + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "name"); + tex.string = luaL_checkstring(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "animation"); + if (! lua_isnil(L, -1)) { + tex.animated = true; + tex.animation = read_animation_definition(L, -1); + } + lua_pop(L, 1); + + lua_getfield(L, -1, "blend"); + LuaParticleParams::readLuaValue(L, tex.blendmode); + lua_pop(L, 1); + + LuaParticleParams::readTweenTable(L, "alpha", tex.alpha); + LuaParticleParams::readTweenTable(L, "scale", tex.scale); + +} + +// add_particle({...}) int ModApiParticles::l_add_particle(lua_State *L) { NO_MAP_LOCK_REQUIRED; // Get parameters - struct ParticleParameters p; + ParticleParameters p; std::string playername; if (lua_gettop(L) > 1) // deprecated @@ -56,7 +76,7 @@ int ModApiParticles::l_add_particle(lua_State *L) p.expirationtime = luaL_checknumber(L, 4); p.size = luaL_checknumber(L, 5); p.collisiondetection = readParam(L, 6); - p.texture = luaL_checkstring(L, 7); + p.texture.string = luaL_checkstring(L, 7); if (lua_gettop(L) == 8) // only spawn for a single player playername = luaL_checkstring(L, 8); } @@ -108,7 +128,12 @@ int ModApiParticles::l_add_particle(lua_State *L) p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - p.texture = getstringfield_default(L, 1, "texture", p.texture); + lua_getfield(L, 1, "texture"); + if (!lua_isnil(L, -1)) { + LuaParticleParams::readTexValue(L, p.texture); + } + lua_pop(L, 1); + p.glow = getintfield_default(L, 1, "glow", p.glow); lua_getfield(L, 1, "node"); @@ -119,34 +144,26 @@ int ModApiParticles::l_add_particle(lua_State *L) p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); playername = getstringfield_default(L, 1, "playername", ""); + + lua_getfield(L, 1, "drag"); + if (lua_istable(L, -1)) + p.drag = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "jitter"); + LuaParticleParams::readLuaValue(L, p.jitter); + lua_pop(L, 1); + + lua_getfield(L, 1, "bounce"); + LuaParticleParams::readLuaValue(L, p.bounce); + lua_pop(L, 1); } getServer(L)->spawnParticle(playername, p); return 1; } -// add_particlespawner({amount=, time=, -// minpos=, maxpos=, -// minvel=, maxvel=, -// minacc=, maxacc=, -// minexptime=, maxexptime=, -// minsize=, maxsize=, -// collisiondetection=, -// collision_removal=, -// object_collision=, -// vertical=, -// texture=, -// player=}) -// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num} -// minexptime/maxexptime = num (seconds) -// minsize/maxsize = num -// collisiondetection = bool -// collision_removal = bool -// object_collision = bool -// vertical = bool -// texture = e.g."default_wood.png" -// animation = TileAnimation definition -// glow = num +// add_particlespawner({...}) int ModApiParticles::l_add_particlespawner(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -156,24 +173,31 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) ServerActiveObject *attached = NULL; std::string playername; + using namespace ParticleParamTypes; if (lua_gettop(L) > 1) //deprecated { log_deprecated(L, "Deprecated add_particlespawner call with " "individual parameters instead of definition"); p.amount = luaL_checknumber(L, 1); p.time = luaL_checknumber(L, 2); - p.minpos = check_v3f(L, 3); - p.maxpos = check_v3f(L, 4); - p.minvel = check_v3f(L, 5); - p.maxvel = check_v3f(L, 6); - p.minacc = check_v3f(L, 7); - p.maxacc = check_v3f(L, 8); - p.minexptime = luaL_checknumber(L, 9); - p.maxexptime = luaL_checknumber(L, 10); - p.minsize = luaL_checknumber(L, 11); - p.maxsize = luaL_checknumber(L, 12); + auto minpos = check_v3f(L, 3); + auto maxpos = check_v3f(L, 4); + auto minvel = check_v3f(L, 5); + auto maxvel = check_v3f(L, 6); + auto minacc = check_v3f(L, 7); + auto maxacc = check_v3f(L, 8); + auto minexptime = luaL_checknumber(L, 9); + auto maxexptime = luaL_checknumber(L, 10); + auto minsize = luaL_checknumber(L, 11); + auto maxsize = luaL_checknumber(L, 12); + p.pos = v3fRange(minpos, maxpos); + p.vel = v3fRange(minvel, maxvel); + p.acc = v3fRange(minacc, maxacc); + p.exptime = f32Range(minexptime, maxexptime); + p.size = f32Range(minsize, maxsize); + p.collisiondetection = readParam(L, 13); - p.texture = luaL_checkstring(L, 14); + p.texture.string = luaL_checkstring(L, 14); if (lua_gettop(L) == 15) // only spawn for a single player playername = luaL_checkstring(L, 15); } @@ -182,40 +206,46 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) p.amount = getintfield_default(L, 1, "amount", p.amount); p.time = getfloatfield_default(L, 1, "time", p.time); - lua_getfield(L, 1, "minpos"); - if (lua_istable(L, -1)) - p.minpos = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "maxpos"); - if (lua_istable(L, -1)) - p.maxpos = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "minvel"); - if (lua_istable(L, -1)) - p.minvel = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "maxvel"); - if (lua_istable(L, -1)) - p.maxvel = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "minacc"); - if (lua_istable(L, -1)) - p.minacc = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "maxacc"); - if (lua_istable(L, -1)) - p.maxacc = check_v3f(L, -1); - lua_pop(L, 1); + // set default values + p.exptime = 1; + p.size = 1; + + // read spawner parameters from the table + LuaParticleParams::readTweenTable(L, "pos", p.pos); + LuaParticleParams::readTweenTable(L, "vel", p.vel); + LuaParticleParams::readTweenTable(L, "acc", p.acc); + LuaParticleParams::readTweenTable(L, "size", p.size); + LuaParticleParams::readTweenTable(L, "exptime", p.exptime); + LuaParticleParams::readTweenTable(L, "drag", p.drag); + LuaParticleParams::readTweenTable(L, "jitter", p.jitter); + LuaParticleParams::readTweenTable(L, "bounce", p.bounce); + lua_getfield(L, 1, "attract"); + if (!lua_isnil(L, -1)) { + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "kind"); + LuaParticleParams::readLuaValue(L, p.attractor_kind); + lua_pop(L,1); + + lua_getfield(L, -1, "die_on_contact"); + if (!lua_isnil(L, -1)) + p.attractor_kill = readParam(L, -1); + lua_pop(L,1); + + if (p.attractor_kind != AttractorKind::none) { + LuaParticleParams::readTweenTable(L, "strength", p.attract); + LuaParticleParams::readTweenTable(L, "origin", p.attractor_origin); + p.attractor_attachment = LuaParticleParams::readAttachmentID(L, "origin_attached"); + if (p.attractor_kind != AttractorKind::point) { + LuaParticleParams::readTweenTable(L, "direction", p.attractor_direction); + p.attractor_direction_attachment = LuaParticleParams::readAttachmentID(L, "direction_attached"); + } + } + } else { + p.attractor_kind = AttractorKind::none; + } + lua_pop(L,1); + LuaParticleParams::readTweenTable(L, "radius", p.radius); - p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); - p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); - p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); - p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); p.collisiondetection = getboolfield_default(L, 1, "collisiondetection", p.collisiondetection); p.collision_removal = getboolfield_default(L, 1, @@ -234,11 +264,29 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) attached = ObjectRef::getobject(ref); } + lua_getfield(L, 1, "texture"); + if (!lua_isnil(L, -1)) { + LuaParticleParams::readTexValue(L, p.texture); + } + lua_pop(L, 1); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); - p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "texpool"); + if (lua_istable(L, -1)) { + size_t tl = lua_objlen(L, -1); + p.texpool.reserve(tl); + for (size_t i = 0; i < tl; ++i) { + lua_pushinteger(L, i+1), lua_gettable(L, -2); + p.texpool.emplace_back(); + LuaParticleParams::readTexValue(L, p.texpool.back()); + lua_pop(L,1); + } + } + lua_pop(L, 1); + lua_getfield(L, 1, "node"); if (lua_istable(L, -1)) p.node = readnode(L, -1, getGameDef(L)->ndef()); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index cc68b13a5..62cbab8e9 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "lua_api/l_internal.h" #include "lua_api/l_object.h" +#include "lua_api/l_particleparams.h" #include "client/particles.h" #include "client/client.h" #include "client/clientevent.h" @@ -49,6 +50,19 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) p.acc = check_v3f(L, -1); lua_pop(L, 1); + lua_getfield(L, 1, "drag"); + if (lua_istable(L, -1)) + p.drag = check_v3f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "jitter"); + LuaParticleParams::readLuaValue(L, p.jitter); + lua_pop(L, 1); + + lua_getfield(L, 1, "bounce"); + LuaParticleParams::readLuaValue(L, p.bounce); + lua_pop(L, 1); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", p.expirationtime); p.size = getfloatfield_default(L, 1, "size", p.size); @@ -64,7 +78,11 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - p.texture = getstringfield_default(L, 1, "texture", p.texture); + lua_getfield(L, 1, "texture"); + if (!lua_isnil(L, -1)) { + LuaParticleParams::readTexValue(L,p.texture); + } + lua_pop(L, 1); p.glow = getintfield_default(L, 1, "glow", p.glow); lua_getfield(L, 1, "node"); @@ -88,44 +106,50 @@ int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) // Get parameters ParticleSpawnerParameters p; - p.amount = getintfield_default(L, 1, "amount", p.amount); p.time = getfloatfield_default(L, 1, "time", p.time); - lua_getfield(L, 1, "minpos"); - if (lua_istable(L, -1)) - p.minpos = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "maxpos"); - if (lua_istable(L, -1)) - p.maxpos = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "minvel"); - if (lua_istable(L, -1)) - p.minvel = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "maxvel"); - if (lua_istable(L, -1)) - p.maxvel = check_v3f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 1, "minacc"); - if (lua_istable(L, -1)) - p.minacc = check_v3f(L, -1); - lua_pop(L, 1); + // set default values + p.exptime = 1; + p.size = 1; + + // read spawner parameters from the table + using namespace ParticleParamTypes; + LuaParticleParams::readTweenTable(L, "pos", p.pos); + LuaParticleParams::readTweenTable(L, "vel", p.vel); + LuaParticleParams::readTweenTable(L, "acc", p.acc); + LuaParticleParams::readTweenTable(L, "size", p.size); + LuaParticleParams::readTweenTable(L, "exptime", p.exptime); + LuaParticleParams::readTweenTable(L, "drag", p.drag); + LuaParticleParams::readTweenTable(L, "jitter", p.jitter); + LuaParticleParams::readTweenTable(L, "bounce", p.bounce); + lua_getfield(L, 1, "attract"); + if (!lua_isnil(L, -1)) { + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "kind"); + LuaParticleParams::readLuaValue(L, p.attractor_kind); + lua_pop(L,1); + + lua_getfield(L, -1, "die_on_contact"); + if (!lua_isnil(L, -1)) + p.attractor_kill = readParam(L, -1); + lua_pop(L,1); + + if (p.attractor_kind != AttractorKind::none) { + LuaParticleParams::readTweenTable(L, "strength", p.attract); + LuaParticleParams::readTweenTable(L, "origin", p.attractor_origin); + p.attractor_attachment = LuaParticleParams::readAttachmentID(L, "origin_attached"); + if (p.attractor_kind != AttractorKind::point) { + LuaParticleParams::readTweenTable(L, "direction", p.attractor_direction); + p.attractor_direction_attachment = LuaParticleParams::readAttachmentID(L, "direction_attached"); + } + } + } else { + p.attractor_kind = AttractorKind::none; + } + lua_pop(L,1); + LuaParticleParams::readTweenTable(L, "radius", p.radius); - lua_getfield(L, 1, "maxacc"); - if (lua_istable(L, -1)) - p.maxacc = check_v3f(L, -1); - lua_pop(L, 1); - - p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); - p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); - p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); - p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); p.collisiondetection = getboolfield_default(L, 1, "collisiondetection", p.collisiondetection); p.collision_removal = getboolfield_default(L, 1, @@ -137,10 +161,28 @@ int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) p.animation = read_animation_definition(L, -1); lua_pop(L, 1); + lua_getfield(L, 1, "texture"); + if (!lua_isnil(L, -1)) { + LuaParticleParams::readTexValue(L, p.texture); + } + lua_pop(L, 1); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); - p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "texpool"); + if (lua_istable(L, -1)) { + size_t tl = lua_objlen(L, -1); + p.texpool.reserve(tl); + for (size_t i = 0; i < tl; ++i) { + lua_pushinteger(L, i+1), lua_gettable(L, -2); + p.texpool.emplace_back(); + LuaParticleParams::readTexValue(L, p.texpool.back()); + lua_pop(L,1); + } + } + lua_pop(L, 1); + lua_getfield(L, 1, "node"); if (lua_istable(L, -1)) p.node = readnode(L, -1, getGameDef(L)->ndef()); -- cgit v1.2.3