minetest/src/script/lua_api/l_particleparams.h

280 lines
7.2 KiB
C++

/*
Minetest
Copyright (C) 2021 velartrill, Lexi Hale <lexi@hale.su>
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<typename T>
inline void readNumericLuaValue(lua_State* L, T& ret)
{
if (lua_isnil(L,-1))
return;
if (std::is_integral<T>())
ret = lua_tointeger(L, -1);
else
ret = lua_tonumber(L, -1);
}
template <typename T, size_t N>
inline void readNumericLuaValue(lua_State* L, Parameter<T,N>& ret)
{
readNumericLuaValue<T>(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 <typename T> void
readLuaValue(lua_State* L, RangedParameter<T>& 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 <typename T> void
readLegacyValue(lua_State* L, const char* name, T& field) {}
template <typename T> void
readLegacyValue(lua_State* L, const char* name, RangedParameter<T>& 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 <typename T> void
readTweenTable(lua_State* L, const char* name, TweenedParameter<T>& 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);
}