misc_helpers.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. function core.formspec_escape(text)
  158. if text ~= nil then
  159. text = string.gsub(text,"\\","\\\\")
  160. text = string.gsub(text,"%]","\\]")
  161. text = string.gsub(text,"%[","\\[")
  162. text = string.gsub(text,";","\\;")
  163. text = string.gsub(text,",","\\,")
  164. end
  165. return text
  166. end
  167. function core.splittext(text,charlimit)
  168. local retval = {}
  169. local current_idx = 1
  170. local start,stop = string.find(text," ",current_idx)
  171. local nl_start,nl_stop = string.find(text,"\n",current_idx)
  172. local gotnewline = false
  173. if nl_start ~= nil and (start == nil or nl_start < start) then
  174. start = nl_start
  175. stop = nl_stop
  176. gotnewline = true
  177. end
  178. local last_line = ""
  179. while start ~= nil do
  180. if string.len(last_line) + (stop-start) > charlimit then
  181. table.insert(retval,last_line)
  182. last_line = ""
  183. end
  184. if last_line ~= "" then
  185. last_line = last_line .. " "
  186. end
  187. last_line = last_line .. string.sub(text,current_idx,stop -1)
  188. if gotnewline then
  189. table.insert(retval,last_line)
  190. last_line = ""
  191. gotnewline = false
  192. end
  193. current_idx = stop+1
  194. start,stop = string.find(text," ",current_idx)
  195. nl_start,nl_stop = string.find(text,"\n",current_idx)
  196. if nl_start ~= nil and (start == nil or nl_start < start) then
  197. start = nl_start
  198. stop = nl_stop
  199. gotnewline = true
  200. end
  201. end
  202. --add last part of text
  203. if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
  204. table.insert(retval,last_line)
  205. table.insert(retval,string.sub(text,current_idx))
  206. else
  207. last_line = last_line .. " " .. string.sub(text,current_idx)
  208. table.insert(retval,last_line)
  209. end
  210. return retval
  211. end
  212. --------------------------------------------------------------------------------
  213. if INIT == "game" then
  214. local dirs1 = {9, 18, 7, 12}
  215. local dirs2 = {20, 23, 22, 21}
  216. function core.rotate_and_place(itemstack, placer, pointed_thing,
  217. infinitestacks, orient_flags)
  218. orient_flags = orient_flags or {}
  219. local unode = core.get_node_or_nil(pointed_thing.under)
  220. if not unode then
  221. return
  222. end
  223. local undef = core.registered_nodes[unode.name]
  224. if undef and undef.on_rightclick then
  225. undef.on_rightclick(pointed_thing.under, unode, placer,
  226. itemstack, pointed_thing)
  227. return
  228. end
  229. local pitch = placer:get_look_pitch()
  230. local fdir = core.dir_to_facedir(placer:get_look_dir())
  231. local wield_name = itemstack:get_name()
  232. local above = pointed_thing.above
  233. local under = pointed_thing.under
  234. local iswall = (above.y == under.y)
  235. local isceiling = not iswall and (above.y < under.y)
  236. local anode = core.get_node_or_nil(above)
  237. if not anode then
  238. return
  239. end
  240. local pos = pointed_thing.above
  241. local node = anode
  242. if undef and undef.buildable_to then
  243. pos = pointed_thing.under
  244. node = unode
  245. iswall = false
  246. end
  247. if core.is_protected(pos, placer:get_player_name()) then
  248. core.record_protection_violation(pos,
  249. placer:get_player_name())
  250. return
  251. end
  252. local ndef = core.registered_nodes[node.name]
  253. if not ndef or not ndef.buildable_to then
  254. return
  255. end
  256. if orient_flags.force_floor then
  257. iswall = false
  258. isceiling = false
  259. elseif orient_flags.force_ceiling then
  260. iswall = false
  261. isceiling = true
  262. elseif orient_flags.force_wall then
  263. iswall = true
  264. isceiling = false
  265. elseif orient_flags.invert_wall then
  266. iswall = not iswall
  267. end
  268. if iswall then
  269. core.set_node(pos, {name = wield_name,
  270. param2 = dirs1[fdir+1]})
  271. elseif isceiling then
  272. if orient_flags.force_facedir then
  273. core.set_node(pos, {name = wield_name,
  274. param2 = 20})
  275. else
  276. core.set_node(pos, {name = wield_name,
  277. param2 = dirs2[fdir+1]})
  278. end
  279. else -- place right side up
  280. if orient_flags.force_facedir then
  281. core.set_node(pos, {name = wield_name,
  282. param2 = 0})
  283. else
  284. core.set_node(pos, {name = wield_name,
  285. param2 = fdir})
  286. end
  287. end
  288. if not infinitestacks then
  289. itemstack:take_item()
  290. return itemstack
  291. end
  292. end
  293. --------------------------------------------------------------------------------
  294. --Wrapper for rotate_and_place() to check for sneak and assume Creative mode
  295. --implies infinite stacks when performing a 6d rotation.
  296. --------------------------------------------------------------------------------
  297. core.rotate_node = function(itemstack, placer, pointed_thing)
  298. core.rotate_and_place(itemstack, placer, pointed_thing,
  299. core.setting_getbool("creative_mode"),
  300. {invert_wall = placer:get_player_control().sneak})
  301. return itemstack
  302. end
  303. end
  304. --------------------------------------------------------------------------------
  305. function core.explode_table_event(evt)
  306. if evt ~= nil then
  307. local parts = evt:split(":")
  308. if #parts == 3 then
  309. local t = parts[1]:trim()
  310. local r = tonumber(parts[2]:trim())
  311. local c = tonumber(parts[3]:trim())
  312. if type(r) == "number" and type(c) == "number" and t ~= "INV" then
  313. return {type=t, row=r, column=c}
  314. end
  315. end
  316. end
  317. return {type="INV", row=0, column=0}
  318. end
  319. --------------------------------------------------------------------------------
  320. function core.explode_textlist_event(evt)
  321. if evt ~= nil then
  322. local parts = evt:split(":")
  323. if #parts == 2 then
  324. local t = parts[1]:trim()
  325. local r = tonumber(parts[2]:trim())
  326. if type(r) == "number" and t ~= "INV" then
  327. return {type=t, index=r}
  328. end
  329. end
  330. end
  331. return {type="INV", index=0}
  332. end
  333. function core.pos_to_string(pos)
  334. return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
  335. end
  336. --------------------------------------------------------------------------------
  337. -- mainmenu only functions
  338. --------------------------------------------------------------------------------
  339. if INIT == "mainmenu" then
  340. function core.get_game(index)
  341. local games = game.get_games()
  342. if index > 0 and index <= #games then
  343. return games[index]
  344. end
  345. return nil
  346. end
  347. function fgettext(text, ...)
  348. text = core.gettext(text)
  349. local arg = {n=select('#', ...), ...}
  350. if arg.n >= 1 then
  351. -- Insert positional parameters ($1, $2, ...)
  352. result = ''
  353. pos = 1
  354. while pos <= text:len() do
  355. newpos = text:find('[$]', pos)
  356. if newpos == nil then
  357. result = result .. text:sub(pos)
  358. pos = text:len() + 1
  359. else
  360. paramindex = tonumber(text:sub(newpos+1, newpos+1))
  361. result = result .. text:sub(pos, newpos-1) .. tostring(arg[paramindex])
  362. pos = newpos + 2
  363. end
  364. end
  365. text = result
  366. end
  367. return core.formspec_escape(text)
  368. end
  369. end