aboutsummaryrefslogtreecommitdiff
path: root/src/script/lua_api/l_particles.cpp
blob: f6c1725de37ef389c06451381f1d4df33e441125 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

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.
*/

#include "lua_api/l_particles.h"
#include "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "server.h"

// add_particle({pos=, velocity=, acceleration=, expirationtime=,
// 		size=, collisiondetection=, vertical=, texture=, player=})
// pos/velocity/acceleration = {x=num, y=num, z=num}
// expirationtime = num (seconds)
// size = num
// collisiondetection = bool
// vertical = bool
// texture = e.g."default_wood.png"
int ModApiParticles::l_add_particle(lua_State *L)
{
	MAP_LOCK_REQUIRED;

	// Get parameters
	v3f pos, vel, acc;
	pos = vel = acc = v3f(0, 0, 0);

	float expirationtime, size;
	expirationtime = size = 1;

	bool collisiondetection, vertical;
	collisiondetection = vertical = false;

	std::string texture = "";
	std::string playername = "";

	if (lua_gettop(L) > 1) // deprecated
	{
		log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition");
		pos = check_v3f(L, 1);
		vel = check_v3f(L, 2);
		acc = check_v3f(L, 3);
		expirationtime = luaL_checknumber(L, 4);
		size = luaL_checknumber(L, 5);
		collisiondetection = lua_toboolean(L, 6);
		texture = luaL_checkstring(L, 7);
		if (lua_gettop(L) == 8) // only spawn for a single player
			playername = luaL_checkstring(L, 8);
	}
	else if (lua_istable(L, 1))
	{
		lua_getfield(L, 1, "pos");
		pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f();
		lua_pop(L, 1);

		lua_getfield(L, 1, "vel");
		if (lua_istable(L, -1)) {
			vel = check_v3f(L, -1);
			log_deprecated(L, "The use of vel is deprecated. "
				"Use velocity instead");
		}
		lua_pop(L, 1);

		lua_getfield(L, 1, "velocity");
		vel = lua_istable(L, -1) ? check_v3f(L, -1) : vel;
		lua_pop(L, 1);

		lua_getfield(L, 1, "acc");
		if (lua_istable(L, -1)) {
			acc = check_v3f(L, -1);
			log_deprecated(L, "The use of acc is deprecated. "
				"Use acceleration instead");
		}
		lua_pop(L, 1);

		lua_getfield(L, 1, "acceleration");
		acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc;
		lua_pop(L, 1);

		expirationtime = getfloatfield_default(L, 1, "expirationtime", 1);
		size = getfloatfield_default(L, 1, "size", 1);
		collisiondetection = getboolfield_default(L, 1,
			"collisiondetection", collisiondetection);
		vertical = getboolfield_default(L, 1, "vertical", vertical);
		texture = getstringfield_default(L, 1, "texture", "");
		playername = getstringfield_default(L, 1, "playername", "");
	}
	getServer(L)->spawnParticle(playername, pos, vel, acc,
			expirationtime, size, collisiondetection, vertical, texture);
	return 1;
}

// add_particlespawner({amount=, time=,
//				minpos=, maxpos=,
//				minvel=, maxvel=,
//				minacc=, maxacc=,
//				minexptime=, maxexptime=,
//				minsize=, maxsize=,
//				collisiondetection=,
//				vertical=,
//				texture=,
//				player=})
// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num}
// minexptime/maxexptime = num (seconds)
// minsize/maxsize = num
// collisiondetection = bool
// vertical = bool
// texture = e.g."default_wood.png"
int ModApiParticles::l_add_particlespawner(lua_State *L)
{
	MAP_LOCK_REQUIRED;

	// Get parameters
	u16 amount = 1;
	v3f minpos, maxpos, minvel, maxvel, minacc, maxacc;
	    minpos= maxpos= minvel= maxvel= minacc= maxacc= v3f(0, 0, 0);
	float time, minexptime, maxexptime, minsize, maxsize;
	      time= minexptime= maxexptime= minsize= maxsize= 1;
	bool collisiondetection, vertical;
	     collisiondetection= vertical= false;
	std::string texture = "";
	std::string playername = "";

	if (lua_gettop(L) > 1) //deprecated
	{
		log_deprecated(L,"Deprecated add_particlespawner call with individual parameters instead of definition");
		amount = luaL_checknumber(L, 1);
		time = luaL_checknumber(L, 2);
		minpos = check_v3f(L, 3);
		maxpos = check_v3f(L, 4);
		minvel = check_v3f(L, 5);
		maxvel = check_v3f(L, 6);
		minacc = check_v3f(L, 7);
		maxacc = check_v3f(L, 8);
		minexptime = luaL_checknumber(L, 9);
		maxexptime = luaL_checknumber(L, 10);
		minsize = luaL_checknumber(L, 11);
		maxsize = luaL_checknumber(L, 12);
		collisiondetection = lua_toboolean(L, 13);
		texture = luaL_checkstring(L, 14);
		if (lua_gettop(L) == 15) // only spawn for a single player
			playername = luaL_checkstring(L, 15);
	}
	else if (lua_istable(L, 1))
	{
		amount = getintfield_default(L, 1, "amount", amount);
		time = getfloatfield_default(L, 1, "time", time);

		lua_getfield(L, 1, "minpos");
		minpos = lua_istable(L, -1) ? check_v3f(L, -1) : minpos;
		lua_pop(L, 1);

		lua_getfield(L, 1, "maxpos");
		maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : maxpos;
		lua_pop(L, 1);

		lua_getfield(L, 1, "minvel");
		minvel = lua_istable(L, -1) ? check_v3f(L, -1) : minvel;
		lua_pop(L, 1);

		lua_getfield(L, 1, "maxvel");
		maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : maxvel;
		lua_pop(L, 1);

		lua_getfield(L, 1, "minacc");
		minacc = lua_istable(L, -1) ? check_v3f(L, -1) : minacc;
		lua_pop(L, 1);

		lua_getfield(L, 1, "maxacc");
		maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : maxacc;
		lua_pop(L, 1);

		minexptime = getfloatfield_default(L, 1, "minexptime", minexptime);
		maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime);
		minsize = getfloatfield_default(L, 1, "minsize", minsize);
		maxsize = getfloatfield_default(L, 1, "maxsize", maxsize);
		collisiondetection = getboolfield_default(L, 1,
			"collisiondetection", collisiondetection);
		vertical = getboolfield_default(L, 1, "vertical", vertical);
		texture = getstringfield_default(L, 1, "texture", "");
		playername = getstringfield_default(L, 1, "playername", "");
	}

	u32 id = getServer(L)->addParticleSpawner(amount, time,
			minpos, maxpos,
			minvel, maxvel,
			minacc, maxacc,
			minexptime, maxexptime,
			minsize, maxsize,
			collisiondetection,
			vertical,
			texture, playername);
	lua_pushnumber(L, id);

	return 1;
}

// delete_particlespawner(id, player)
// player (string) is optional
int ModApiParticles::l_delete_particlespawner(lua_State *L)
{
	MAP_LOCK_REQUIRED;

	// Get parameters
	u32 id = luaL_checknumber(L, 1);
	std::string playername = "";
	if (lua_gettop(L) == 2) {
		playername = luaL_checkstring(L, 2);
	}

	getServer(L)->deleteParticleSpawner(playername, id);
	return 1;
}

void ModApiParticles::Initialize(lua_State *L, int top)
{
	API_FCT(add_particle);
	API_FCT(add_particlespawner);
	API_FCT(delete_particlespawner);
}

an> default = default, min = min, max = max, comment = current_comment, }) return end if setting_type == "enum" then local default, values = remaining_line:match("^" -- first value (default) may be empty (i.e. is optional) .. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*" .. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. "$") if not default or values == "" then return "Invalid enum setting" end table.insert(settings, { name = name, readable_name = readable_name, type = "enum", default = default, values = values:split(",", true), comment = current_comment, }) return end if setting_type == "path" then local default = remaining_line:match("^(.*)$") if not default then return "Invalid path setting" end table.insert(settings, { name = name, readable_name = readable_name, type = "path", default = default, comment = current_comment, }) return end if setting_type == "flags" then local default, possible = remaining_line:match("^" -- first value (default) may be empty (i.e. is optional) -- this is implemented by making the last value optional, and -- swapping them around if it turns out empty. .. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*" .. "(" .. CHAR_CLASSES.FLAGS .. "*)" .. "$") if not default or not possible then return "Invalid flags setting" end if possible == "" then possible = default default = "" end table.insert(settings, { name = name, readable_name = readable_name, type = "flags", default = default, possible = possible, comment = current_comment, }) return end return "Invalid setting type \"" .. setting_type .. "\"" end local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure) -- store this helper variable in the table so it's easier to pass to parse_setting_line() result.current_comment = "" local line = file:read("*line") while line do local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure) if error_msg then core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"") end line = file:read("*line") end result.current_comment = nil end -- read_all: whether to ignore certain setting types for GUI or not -- parse_mods: whether to parse settingtypes.txt in mods and games local function parse_config_file(read_all, parse_mods) local builtin_path = core.get_builtin_path() .. DIR_DELIM .. FILENAME local file = io.open(builtin_path, "r") local settings = {} if not file then core.log("error", "Can't load " .. FILENAME) return settings end parse_single_file(file, builtin_path, read_all, settings, 0, true) file:close() if parse_mods then -- Parse games local games_category_initialized = false local index = 1 local game = gamemgr.get_game(index) while game do local path = game.path .. DIR_DELIM .. FILENAME local file = io.open(path, "r") if file then if not games_category_initialized then local translation = fgettext_ne("Games"), -- not used, but needed for xgettext table.insert(settings, { name = "Games", level = 0, type = "category", }) games_category_initialized = true end table.insert(settings, { name = game.name, level = 1, type = "category", }) parse_single_file(file, path, read_all, settings, 2, false) file:close() end index = index + 1 game = gamemgr.get_game(index) end -- Parse mods local mods_category_initialized = false local mods = {} get_mods(core.get_modpath(), mods) for _, mod in ipairs(mods) do local path = mod.path .. DIR_DELIM .. FILENAME local file = io.open(path, "r") if file then if not mods_category_initialized then local translation = fgettext_ne("Mods"), -- not used, but needed for xgettext table.insert(settings, { name = "Mods", level = 0, type = "category", }) mods_category_initialized = true end table.insert(settings, { name = mod.name, level = 1, type = "category", }) parse_single_file(file, path, read_all, settings, 2, false) file:close() end end end return settings end local settings = parse_config_file(false, true) local selected_setting = 1 local function get_current_value(setting) local value = core.setting_get(setting.name) if value == nil then value = setting.default end return value end local function create_change_setting_formspec(dialogdata) local setting = settings[selected_setting] local formspec = "size[10,5.2,true]" .. "button[5,4.5;2,1;btn_done;" .. fgettext("Save") .. "]" .. "button[3,4.5;2,1;btn_cancel;" .. fgettext("Cancel") .. "]" .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. "table[0,0;10,3;info;" if setting.readable_name then formspec = formspec .. "#FFFF00," .. fgettext(setting.readable_name) .. " (" .. core.formspec_escape(setting.name) .. ")," else formspec = formspec .. "#FFFF00," .. core.formspec_escape(setting.name) .. "," end formspec = formspec .. ",," local comment_text = "" if setting.comment == "" then comment_text = fgettext_ne("(No description of setting given)") else comment_text = fgettext_ne(setting.comment) end for _, comment_line in ipairs(comment_text:split("\n", true)) do formspec = formspec .. "," .. core.formspec_escape(comment_line) .. "," end if setting.type == "flags" then formspec = formspec .. ",," .. "," .. fgettext("Please enter a comma seperated list of flags.") .. "," .. "," .. fgettext("Possible values are: ") .. core.formspec_escape(setting.possible:gsub(",", ", ")) .. "," elseif setting.type == "noise_params" then formspec = formspec .. ",," .. "," .. fgettext("Format: <offset>, <scale>, (<spreadX>, <spreadY>, <spreadZ>), <seed>, <octaves>, <persistence>") .. "," .. "," .. fgettext("Optionally the lacunarity can be appended with a leading comma.") .. "," elseif setting.type == "v3f" then formspec = formspec .. ",," .. "," .. fgettext_ne("Format is 3 numbers separated by commas and inside brackets.") .. "," end formspec = formspec:sub(1, -2) -- remove trailing comma formspec = formspec .. ";1]" if setting.type == "bool" then local selected_index if core.is_yes(get_current_value(setting)) then selected_index = 2 else selected_index = 1 end formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;" .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";" .. selected_index .. "]" elseif setting.type == "enum" then local selected_index = 0 formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;" for index, value in ipairs(setting.values) do -- translating value is not possible, since it's the value -- that we set the setting to formspec = formspec .. core.formspec_escape(value) .. "," if get_current_value(setting) == value then selected_index = index end end if #setting.values > 0 then formspec = formspec:sub(1, -2) -- remove trailing comma end formspec = formspec .. ";" .. selected_index .. "]" elseif setting.type == "path" then local current_value = dialogdata.selected_path if not current_value then current_value = get_current_value(setting) end formspec = formspec .. "field[0.5,4;7.5,1;te_setting_value;;" .. core.formspec_escape(current_value) .. "]" .. "button[8,3.75;2,1;btn_browser_path;" .. fgettext("Browse") .. "]" else -- TODO: fancy input for float, int, flags, noise_params, v3f local width = 10 local text = get_current_value(setting) if dialogdata.error_message then formspec = formspec .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. "table[5,3.9;5,0.6;error_message;#FF0000," .. core.formspec_escape(dialogdata.error_message) .. ";0]" width = 5 if dialogdata.entered_text then text = dialogdata.entered_text end end formspec = formspec .. "field[0.5,4;" .. width .. ",1;te_setting_value;;" .. core.formspec_escape(text) .. "]" end return formspec end local function handle_change_setting_buttons(this, fields) if fields["btn_done"] or fields["key_enter"] then local setting = settings[selected_setting] if setting.type == "bool" then