1 -- Minetest: builtin/misc_helpers.lua
3 --------------------------------------------------------------------------------
4 function basic_dump2(o)
5 if type(o) == "number" then
7 elseif type(o) == "string" then
8 return string.format("%q", o)
9 elseif type(o) == "boolean" then
11 elseif type(o) == "function" then
13 elseif type(o) == "userdata" then
15 elseif type(o) == "nil" then
18 error("cannot dump a " .. type(o))
23 --------------------------------------------------------------------------------
24 function dump2(o, name, dumped)
28 if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
29 or type(o) == "function" or type(o) == "nil"
30 or type(o) == "userdata" then
31 io.write(basic_dump2(o), "\n")
32 elseif type(o) == "table" then
34 io.write(dumped[o], "\n")
37 io.write("{}\n") -- new table
38 for k,v in pairs(o) do
39 local fieldname = string.format("%s[%s]", name, basic_dump2(k))
40 dump2(v, fieldname, dumped)
44 error("cannot dump a " .. type(o))
49 --------------------------------------------------------------------------------
50 function dump(o, dumped)
52 if type(o) == "number" then
54 elseif type(o) == "string" then
55 return string.format("%q", o)
56 elseif type(o) == "table" then
58 return "<circular reference>"
62 for k,v in pairs(o) do
63 t[#t+1] = "[" .. dump(k, dumped) .. "] = " .. dump(v, dumped)
65 return "{" .. table.concat(t, ", ") .. "}"
66 elseif type(o) == "boolean" then
68 elseif type(o) == "function" then
70 elseif type(o) == "userdata" then
72 elseif type(o) == "nil" then
75 error("cannot dump a " .. type(o))
80 --------------------------------------------------------------------------------
81 function string:split(sep)
82 local sep, fields = sep or ",", {}
83 local pattern = string.format("([^%s]+)", sep)
84 self:gsub(pattern, function(c) fields[#fields+1] = c end)
88 --------------------------------------------------------------------------------
89 function file_exists(filename)
90 local f = io.open(filename, "r")
99 --------------------------------------------------------------------------------
100 function string:trim()
101 return (self:gsub("^%s*(.-)%s*$", "%1"))
104 assert(string.trim("\n \t\tfoo bar\t ") == "foo bar")
106 --------------------------------------------------------------------------------
107 function math.hypot(x, y)
113 if x == 0 then return 0 end
115 return x * math.sqrt(1 + t * t)
118 --------------------------------------------------------------------------------
119 function get_last_folder(text,count)
120 local parts = text:split(DIR_DELIM)
128 retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
134 --------------------------------------------------------------------------------
135 function cleanup_path(temppath)
137 local parts = temppath:split("-")
140 if temppath ~= "" then
141 temppath = temppath .. "_"
143 temppath = temppath .. parts[i]
146 parts = temppath:split(".")
149 if temppath ~= "" then
150 temppath = temppath .. "_"
152 temppath = temppath .. parts[i]
155 parts = temppath:split("'")
158 if temppath ~= "" then
159 temppath = temppath .. ""
161 temppath = temppath .. parts[i]
164 parts = temppath:split(" ")
167 if temppath ~= "" then
170 temppath = temppath .. parts[i]
176 local tbl = engine or minetest
177 function tbl.formspec_escape(text)
179 text = string.gsub(text,"\\","\\\\")
180 text = string.gsub(text,"%]","\\]")
181 text = string.gsub(text,"%[","\\[")
182 text = string.gsub(text,";","\\;")
183 text = string.gsub(text,",","\\,")
189 function tbl.splittext(text,charlimit)
192 local current_idx = 1
194 local start,stop = string.find(text," ",current_idx)
195 local nl_start,nl_stop = string.find(text,"\n",current_idx)
196 local gotnewline = false
197 if nl_start ~= nil and (start == nil or nl_start < start) then
203 while start ~= nil do
204 if string.len(last_line) + (stop-start) > charlimit then
205 table.insert(retval,last_line)
209 if last_line ~= "" then
210 last_line = last_line .. " "
213 last_line = last_line .. string.sub(text,current_idx,stop -1)
216 table.insert(retval,last_line)
222 start,stop = string.find(text," ",current_idx)
223 nl_start,nl_stop = string.find(text,"\n",current_idx)
225 if nl_start ~= nil and (start == nil or nl_start < start) then
232 --add last part of text
233 if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
234 table.insert(retval,last_line)
235 table.insert(retval,string.sub(text,current_idx))
237 last_line = last_line .. " " .. string.sub(text,current_idx)
238 table.insert(retval,last_line)
244 --------------------------------------------------------------------------------
247 local dirs1 = {9, 18, 7, 12}
248 local dirs2 = {20, 23, 22, 21}
250 function minetest.rotate_and_place(itemstack, placer, pointed_thing,
251 infinitestacks, orient_flags)
252 orient_flags = orient_flags or {}
254 local unode = minetest.get_node_or_nil(pointed_thing.under)
258 local undef = minetest.registered_nodes[unode.name]
259 if undef and undef.on_rightclick then
260 undef.on_rightclick(pointed_thing.under, unode, placer,
261 itemstack, pointed_thing)
264 local pitch = placer:get_look_pitch()
265 local fdir = minetest.dir_to_facedir(placer:get_look_dir())
266 local wield_name = itemstack:get_name()
268 local above = pointed_thing.above
269 local under = pointed_thing.under
270 local iswall = (above.y == under.y)
271 local isceiling = not iswall and (above.y < under.y)
272 local anode = minetest.get_node_or_nil(above)
276 local pos = pointed_thing.above
279 if undef and undef.buildable_to then
280 pos = pointed_thing.under
285 if minetest.is_protected(pos, placer:get_player_name()) then
286 minetest.record_protection_violation(pos,
287 placer:get_player_name())
291 local ndef = minetest.registered_nodes[node.name]
292 if not ndef or not ndef.buildable_to then
296 if orient_flags.force_floor then
299 elseif orient_flags.force_ceiling then
302 elseif orient_flags.force_wall then
305 elseif orient_flags.invert_wall then
310 minetest.set_node(pos, {name = wield_name,
311 param2 = dirs1[fdir+1]})
312 elseif isceiling then
313 if orient_flags.force_facedir then
314 minetest.set_node(pos, {name = wield_name,
317 minetest.set_node(pos, {name = wield_name,
318 param2 = dirs2[fdir+1]})
320 else -- place right side up
321 if orient_flags.force_facedir then
322 minetest.set_node(pos, {name = wield_name,
325 minetest.set_node(pos, {name = wield_name,
330 if not infinitestacks then
331 itemstack:take_item()
337 --------------------------------------------------------------------------------
338 --Wrapper for rotate_and_place() to check for sneak and assume Creative mode
339 --implies infinite stacks when performing a 6d rotation.
340 --------------------------------------------------------------------------------
343 minetest.rotate_node = function(itemstack, placer, pointed_thing)
344 minetest.rotate_and_place(itemstack, placer, pointed_thing,
345 minetest.setting_getbool("creative_mode"),
346 {invert_wall = placer:get_player_control().sneak})
351 --------------------------------------------------------------------------------
352 function tbl.explode_table_event(evt)
354 local parts = evt:split(":")
356 local t = parts[1]:trim()
357 local r = tonumber(parts[2]:trim())
358 local c = tonumber(parts[3]:trim())
359 if type(r) == "number" and type(c) == "number" and t ~= "INV" then
360 return {type=t, row=r, column=c}
364 return {type="INV", row=0, column=0}
367 --------------------------------------------------------------------------------
368 function tbl.explode_textlist_event(evt)
370 local parts = evt:split(":")
372 local t = parts[1]:trim()
373 local r = tonumber(parts[2]:trim())
374 if type(r) == "number" and t ~= "INV" then
375 return {type=t, index=r}
379 return {type="INV", index=0}
382 --------------------------------------------------------------------------------
383 -- mainmenu only functions
384 --------------------------------------------------------------------------------
385 if engine ~= nil then
386 engine.get_game = function(index)
387 local games = game.get_games()
389 if index > 0 and index <= #games then
396 function fgettext(text, ...)
397 text = engine.gettext(text)
398 local arg = {n=select('#', ...), ...}
400 -- Insert positional parameters ($1, $2, ...)
403 while pos <= text:len() do
404 newpos = text:find('[$]', pos)
405 if newpos == nil then
406 result = result .. text:sub(pos)
409 paramindex = tonumber(text:sub(newpos+1, newpos+1))
410 result = result .. text:sub(pos, newpos-1) .. tostring(arg[paramindex])
416 return engine.formspec_escape(text)
419 --------------------------------------------------------------------------------
421 --------------------------------------------------------------------------------
422 if minetest ~= nil then
423 --------------------------------------------------------------------------------
424 function minetest.pos_to_string(pos)
425 return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"