misc_helpers.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. -- Minetest: builtin/misc_helpers.lua
  2. --------------------------------------------------------------------------------
  3. function basic_dump2(o)
  4. if type(o) == "number" then
  5. return tostring(o)
  6. elseif type(o) == "string" then
  7. return string.format("%q", o)
  8. elseif type(o) == "boolean" then
  9. return tostring(o)
  10. elseif type(o) == "function" then
  11. return "<function>"
  12. elseif type(o) == "userdata" then
  13. return "<userdata>"
  14. elseif type(o) == "nil" then
  15. return "nil"
  16. else
  17. error("cannot dump a " .. type(o))
  18. return nil
  19. end
  20. end
  21. --------------------------------------------------------------------------------
  22. function dump2(o, name, dumped)
  23. name = name or "_"
  24. dumped = dumped or {}
  25. io.write(name, " = ")
  26. if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
  27. or type(o) == "function" or type(o) == "nil"
  28. or type(o) == "userdata" then
  29. io.write(basic_dump2(o), "\n")
  30. elseif type(o) == "table" then
  31. if dumped[o] then
  32. io.write(dumped[o], "\n")
  33. else
  34. dumped[o] = name
  35. io.write("{}\n") -- new table
  36. for k,v in pairs(o) do
  37. local fieldname = string.format("%s[%s]", name, basic_dump2(k))
  38. dump2(v, fieldname, dumped)
  39. end
  40. end
  41. else
  42. error("cannot dump a " .. type(o))
  43. return nil
  44. end
  45. end
  46. --------------------------------------------------------------------------------
  47. function dump(o, dumped)
  48. dumped = dumped or {}
  49. if type(o) == "number" then
  50. return tostring(o)
  51. elseif type(o) == "string" then
  52. return string.format("%q", o)
  53. elseif type(o) == "table" then
  54. if dumped[o] then
  55. return "<circular reference>"
  56. end
  57. dumped[o] = true
  58. local t = {}
  59. for k,v in pairs(o) do
  60. t[#t+1] = "[" .. dump(k, dumped) .. "] = " .. dump(v, dumped)
  61. end
  62. return "{" .. table.concat(t, ", ") .. "}"
  63. elseif type(o) == "boolean" then
  64. return tostring(o)
  65. elseif type(o) == "function" then
  66. return "<function>"
  67. elseif type(o) == "userdata" then
  68. return "<userdata>"
  69. elseif type(o) == "nil" then
  70. return "nil"
  71. else
  72. error("cannot dump a " .. type(o))
  73. return nil
  74. end
  75. end
  76. --------------------------------------------------------------------------------
  77. function string:split(sep)
  78. local sep, fields = sep or ",", {}
  79. local pattern = string.format("([^%s]+)", sep)
  80. self:gsub(pattern, function(c) fields[#fields+1] = c end)
  81. return fields
  82. end
  83. --------------------------------------------------------------------------------
  84. function file_exists(filename)
  85. local f = io.open(filename, "r")
  86. if f==nil then
  87. return false
  88. else
  89. f:close()
  90. return true
  91. end
  92. end
  93. --------------------------------------------------------------------------------
  94. function string:trim()
  95. return (self:gsub("^%s*(.-)%s*$", "%1"))
  96. end
  97. assert(string.trim("\n \t\tfoo bar\t ") == "foo bar")
  98. --------------------------------------------------------------------------------
  99. function math.hypot(x, y)
  100. local t
  101. x = math.abs(x)
  102. y = math.abs(y)
  103. t = math.min(x, y)
  104. x = math.max(x, y)
  105. if x == 0 then return 0 end
  106. t = t / x
  107. return x * math.sqrt(1 + t * t)
  108. end
  109. --------------------------------------------------------------------------------
  110. function get_last_folder(text,count)
  111. local parts = text:split(DIR_DELIM)
  112. if count == nil then
  113. return parts[#parts]
  114. end
  115. local retval = ""
  116. for i=1,count,1 do
  117. retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
  118. end
  119. return retval
  120. end
  121. --------------------------------------------------------------------------------
  122. function cleanup_path(temppath)
  123. local parts = temppath:split("-")
  124. temppath = ""
  125. for i=1,#parts,1 do
  126. if temppath ~= "" then
  127. temppath = temppath .. "_"
  128. end
  129. temppath = temppath .. parts[i]
  130. end
  131. parts = temppath:split(".")
  132. temppath = ""
  133. for i=1,#parts,1 do
  134. if temppath ~= "" then
  135. temppath = temppath .. "_"
  136. end
  137. temppath = temppath .. parts[i]
  138. end
  139. parts = temppath:split("'")
  140. temppath = ""
  141. for i=1,#parts,1 do
  142. if temppath ~= "" then
  143. temppath = temppath .. ""
  144. end
  145. temppath = temppath .. parts[i]
  146. end
  147. parts = temppath:split(" ")
  148. temppath = ""
  149. for i=1,#parts,1 do
  150. if temppath ~= "" then
  151. temppath = temppath
  152. end
  153. temppath = temppath .. parts[i]
  154. end
  155. return temppath
  156. end
  157. local tbl = engine or minetest
  158. function tbl.formspec_escape(text)
  159. if text ~= nil then
  160. text = string.gsub(text,"\\","\\\\")
  161. text = string.gsub(text,"%]","\\]")
  162. text = string.gsub(text,"%[","\\[")
  163. text = string.gsub(text,";","\\;")
  164. text = string.gsub(text,",","\\,")
  165. end
  166. return text
  167. end
  168. function tbl.splittext(text,charlimit)
  169. local retval = {}
  170. local current_idx = 1
  171. local start,stop = string.find(text," ",current_idx)
  172. local nl_start,nl_stop = string.find(text,"\n",current_idx)
  173. local gotnewline = false
  174. if nl_start ~= nil and (start == nil or nl_start < start) then
  175. start = nl_start
  176. stop = nl_stop
  177. gotnewline = true
  178. end
  179. local last_line = ""
  180. while start ~= nil do
  181. if string.len(last_line) + (stop-start) > charlimit then
  182. table.insert(retval,last_line)
  183. last_line = ""
  184. end
  185. if last_line ~= "" then
  186. last_line = last_line .. " "
  187. end
  188. last_line = last_line .. string.sub(text,current_idx,stop -1)
  189. if gotnewline then
  190. table.insert(retval,last_line)
  191. last_line = ""
  192. gotnewline = false
  193. end
  194. current_idx = stop+1
  195. start,stop = string.find(text," ",current_idx)
  196. nl_start,nl_stop = string.find(text,"\n",current_idx)
  197. if nl_start ~= nil and (start == nil or nl_start < start) then
  198. start = nl_start
  199. stop = nl_stop
  200. gotnewline = true
  201. end
  202. end
  203. --add last part of text
  204. if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
  205. table.insert(retval,last_line)
  206. table.insert(retval,string.sub(text,current_idx))
  207. else
  208. last_line = last_line .. " " .. string.sub(text,current_idx)
  209. table.insert(retval,last_line)
  210. end
  211. return retval
  212. end
  213. --------------------------------------------------------------------------------
  214. if minetest then
  215. local dirs1 = {9, 18, 7, 12}
  216. local dirs2 = {20, 23, 22, 21}
  217. function minetest.rotate_and_place(itemstack, placer, pointed_thing,
  218. infinitestacks, orient_flags)
  219. orient_flags = orient_flags or {}
  220. local unode = minetest.get_node_or_nil(pointed_thing.under)
  221. if not unode then
  222. return
  223. end
  224. local undef = minetest.registered_nodes[unode.name]
  225. if undef and undef.on_rightclick then
  226. undef.on_rightclick(pointed_thing.under, unode, placer,
  227. itemstack, pointed_thing)
  228. return
  229. end
  230. local pitch = placer:get_look_pitch()
  231. local fdir = minetest.dir_to_facedir(placer:get_look_dir())
  232. local wield_name = itemstack:get_name()
  233. local above = pointed_thing.above
  234. local under = pointed_thing.under
  235. local iswall = (above.y == under.y)
  236. local isceiling = not iswall and (above.y < under.y)
  237. local anode = minetest.get_node_or_nil(above)
  238. if not anode then
  239. return
  240. end
  241. local pos = pointed_thing.above
  242. local node = anode
  243. if undef and undef.buildable_to then
  244. pos = pointed_thing.under
  245. node = unode
  246. iswall = false
  247. end
  248. if minetest.is_protected(pos, placer:get_player_name()) then
  249. minetest.record_protection_violation(pos,
  250. placer:get_player_name())
  251. return
  252. end
  253. local ndef = minetest.registered_nodes[node.name]
  254. if not ndef or not ndef.buildable_to then
  255. return
  256. end
  257. if orient_flags.force_floor then
  258. iswall = false
  259. isceiling = false
  260. elseif orient_flags.force_ceiling then
  261. iswall = false
  262. isceiling = true
  263. elseif orient_flags.force_wall then
  264. iswall = true
  265. isceiling = false
  266. elseif orient_flags.invert_wall then
  267. iswall = not iswall
  268. end
  269. if iswall then
  270. minetest.set_node(pos, {name = wield_name,
  271. param2 = dirs1[fdir+1]})
  272. elseif isceiling then
  273. if orient_flags.force_facedir then
  274. minetest.set_node(pos, {name = wield_name,
  275. param2 = 20})
  276. else
  277. minetest.set_node(pos, {name = wield_name,
  278. param2 = dirs2[fdir+1]})
  279. end
  280. else -- place right side up
  281. if orient_flags.force_facedir then
  282. minetest.set_node(pos, {name = wield_name,
  283. param2 = 0})
  284. else
  285. minetest.set_node(pos, {name = wield_name,
  286. param2 = fdir})
  287. end
  288. end
  289. if not infinitestacks then
  290. itemstack:take_item()
  291. return itemstack
  292. end
  293. end
  294. --------------------------------------------------------------------------------
  295. --Wrapper for rotate_and_place() to check for sneak and assume Creative mode
  296. --implies infinite stacks when performing a 6d rotation.
  297. --------------------------------------------------------------------------------
  298. minetest.rotate_node = function(itemstack, placer, pointed_thing)
  299. minetest.rotate_and_place(itemstack, placer, pointed_thing,
  300. minetest.setting_getbool("creative_mode"),
  301. {invert_wall = placer:get_player_control().sneak})
  302. return itemstack
  303. end
  304. end
  305. --------------------------------------------------------------------------------
  306. function tbl.explode_table_event(evt)
  307. if evt ~= nil then
  308. local parts = evt:split(":")
  309. if #parts == 3 then
  310. local t = parts[1]:trim()
  311. local r = tonumber(parts[2]:trim())
  312. local c = tonumber(parts[3]:trim())
  313. if type(r) == "number" and type(c) == "number" and t ~= "INV" then
  314. return {type=t, row=r, column=c}
  315. end
  316. end
  317. end
  318. return {type="INV", row=0, column=0}
  319. end
  320. --------------------------------------------------------------------------------
  321. function tbl.explode_textlist_event(evt)
  322. if evt ~= nil then
  323. local parts = evt:split(":")
  324. if #parts == 2 then
  325. local t = parts[1]:trim()
  326. local r = tonumber(parts[2]:trim())
  327. if type(r) == "number" and t ~= "INV" then
  328. return {type=t, index=r}
  329. end
  330. end
  331. end
  332. return {type="INV", index=0}
  333. end
  334. --------------------------------------------------------------------------------
  335. -- mainmenu only functions
  336. --------------------------------------------------------------------------------
  337. if engine ~= nil then
  338. engine.get_game = function(index)
  339. local games = game.get_games()
  340. if index > 0 and index <= #games then
  341. return games[index]
  342. end
  343. return nil
  344. end
  345. function fgettext(text, ...)
  346. text = engine.gettext(text)
  347. local arg = {n=select('#', ...), ...}
  348. if arg.n >= 1 then
  349. -- Insert positional parameters ($1, $2, ...)
  350. result = ''
  351. pos = 1
  352. while pos <= text:len() do
  353. newpos = text:find('[$]', pos)
  354. if newpos == nil then
  355. result = result .. text:sub(pos)
  356. pos = text:len() + 1
  357. else
  358. paramindex = tonumber(text:sub(newpos+1, newpos+1))
  359. result = result .. text:sub(pos, newpos-1) .. tostring(arg[paramindex])
  360. pos = newpos + 2
  361. end
  362. end
  363. text = result
  364. end
  365. return engine.formspec_escape(text)
  366. end
  367. end
  368. --------------------------------------------------------------------------------
  369. -- core only fct
  370. --------------------------------------------------------------------------------
  371. if minetest ~= nil then
  372. --------------------------------------------------------------------------------
  373. function minetest.pos_to_string(pos)
  374. return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
  375. end
  376. end