init.lua 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. local S = minetest.get_translator("testtools")
  2. local F = minetest.formspec_escape
  3. dofile(minetest.get_modpath("testtools") .. "/light.lua")
  4. -- TODO: Add a Node Metadata tool
  5. minetest.register_tool("testtools:param2tool", {
  6. description = S("Param2 Tool") .."\n"..
  7. S("Modify param2 value of nodes") .."\n"..
  8. S("Punch: +1") .."\n"..
  9. S("Sneak+Punch: +8") .."\n"..
  10. S("Place: -1") .."\n"..
  11. S("Sneak+Place: -8"),
  12. inventory_image = "testtools_param2tool.png",
  13. groups = { testtool = 1, disable_repair = 1 },
  14. on_use = function(itemstack, user, pointed_thing)
  15. local pos = minetest.get_pointed_thing_position(pointed_thing)
  16. if pointed_thing.type ~= "node" or (not pos) then
  17. return
  18. end
  19. local add = 1
  20. if user then
  21. local ctrl = user:get_player_control()
  22. if ctrl.sneak then
  23. add = 8
  24. end
  25. end
  26. local node = minetest.get_node(pos)
  27. node.param2 = node.param2 + add
  28. minetest.swap_node(pos, node)
  29. end,
  30. on_place = function(itemstack, user, pointed_thing)
  31. local pos = minetest.get_pointed_thing_position(pointed_thing)
  32. if pointed_thing.type ~= "node" or (not pos) then
  33. return
  34. end
  35. local add = -1
  36. if user then
  37. local ctrl = user:get_player_control()
  38. if ctrl.sneak then
  39. add = -8
  40. end
  41. end
  42. local node = minetest.get_node(pos)
  43. node.param2 = node.param2 + add
  44. minetest.swap_node(pos, node)
  45. end,
  46. })
  47. minetest.register_tool("testtools:node_setter", {
  48. description = S("Node Setter") .."\n"..
  49. S("Replace pointed node with something else") .."\n"..
  50. S("Punch: Select pointed node") .."\n"..
  51. S("Place on node: Replace node with selected node") .."\n"..
  52. S("Place in air: Manually select a node"),
  53. inventory_image = "testtools_node_setter.png",
  54. groups = { testtool = 1, disable_repair = 1 },
  55. on_use = function(itemstack, user, pointed_thing)
  56. local pos = minetest.get_pointed_thing_position(pointed_thing)
  57. if pointed_thing.type == "nothing" then
  58. local meta = itemstack:get_meta()
  59. meta:set_string("node", "air")
  60. meta:set_int("node_param2", 0)
  61. if user and user:is_player() then
  62. minetest.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", "air", 0))
  63. end
  64. return itemstack
  65. elseif pointed_thing.type ~= "node" or (not pos) then
  66. return
  67. end
  68. local node = minetest.get_node(pos)
  69. local meta = itemstack:get_meta()
  70. meta:set_string("node", node.name)
  71. meta:set_int("node_param2", node.param2)
  72. if user and user:is_player() then
  73. minetest.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", node.name, node.param2))
  74. end
  75. return itemstack
  76. end,
  77. on_secondary_use = function(itemstack, user, pointed_thing)
  78. local meta = itemstack:get_meta()
  79. local nodename = meta:get_string("node") or ""
  80. local param2 = meta:get_int("node_param2") or 0
  81. minetest.show_formspec(user:get_player_name(), "testtools:node_setter",
  82. "size[4,4]"..
  83. "field[0.5,1;3,1;nodename;"..F(S("Node name (itemstring):"))..";"..F(nodename).."]"..
  84. "field[0.5,2;3,1;param2;"..F(S("param2:"))..";"..F(tostring(param2)).."]"..
  85. "button_exit[0.5,3;3,1;submit;"..F(S("Submit")).."]"
  86. )
  87. end,
  88. on_place = function(itemstack, user, pointed_thing)
  89. local pos = minetest.get_pointed_thing_position(pointed_thing)
  90. local meta = itemstack:get_meta()
  91. local nodename = meta:get_string("node")
  92. if nodename == "" and user and user:is_player() then
  93. minetest.chat_send_player(user:get_player_name(), S("Punch a node first!"))
  94. return
  95. end
  96. local param2 = meta:get_int("node_param2")
  97. if not param2 then
  98. param2 = 0
  99. end
  100. local node = { name = nodename, param2 = param2 }
  101. if not minetest.registered_nodes[nodename] then
  102. minetest.chat_send_player(user:get_player_name(), S("Cannot set unknown node: @1", nodename))
  103. return
  104. end
  105. minetest.set_node(pos, node)
  106. end,
  107. })
  108. minetest.register_on_player_receive_fields(function(player, formname, fields)
  109. if formname == "testtools:node_setter" then
  110. local playername = player:get_player_name()
  111. local witem = player:get_wielded_item()
  112. if witem:get_name() == "testtools:node_setter" then
  113. if fields.nodename and fields.param2 then
  114. local param2 = tonumber(fields.param2)
  115. if not param2 then
  116. return
  117. end
  118. local meta = witem:get_meta()
  119. meta:set_string("node", fields.nodename)
  120. meta:set_int("node_param2", param2)
  121. player:set_wielded_item(witem)
  122. end
  123. end
  124. end
  125. end)
  126. minetest.register_tool("testtools:remover", {
  127. description = S("Remover") .."\n"..
  128. S("Punch: Remove pointed node or object"),
  129. inventory_image = "testtools_remover.png",
  130. groups = { testtool = 1, disable_repair = 1 },
  131. on_use = function(itemstack, user, pointed_thing)
  132. local pos = minetest.get_pointed_thing_position(pointed_thing)
  133. if pointed_thing.type == "node" and pos ~= nil then
  134. minetest.remove_node(pos)
  135. elseif pointed_thing.type == "object" then
  136. local obj = pointed_thing.ref
  137. if not obj:is_player() then
  138. obj:remove()
  139. else
  140. minetest.chat_send_player(user:get_player_name(), S("Can't remove players!"))
  141. end
  142. end
  143. end,
  144. })
  145. minetest.register_tool("testtools:falling_node_tool", {
  146. description = S("Falling Node Tool") .."\n"..
  147. S("Punch: Make pointed node fall") .."\n"..
  148. S("Place: Move pointed node 2 units upwards, then make it fall"),
  149. inventory_image = "testtools_falling_node_tool.png",
  150. groups = { testtool = 1, disable_repair = 1 },
  151. on_place = function(itemstack, user, pointed_thing)
  152. -- Teleport node 1-2 units upwards (if possible) and make it fall
  153. local pos = minetest.get_pointed_thing_position(pointed_thing)
  154. if pointed_thing.type ~= "node" or (not pos) then
  155. return
  156. end
  157. local ok = false
  158. local highest
  159. for i=1,2 do
  160. local above = {x=pos.x,y=pos.y+i,z=pos.z}
  161. local n2 = minetest.get_node(above)
  162. local def2 = minetest.registered_nodes[n2.name]
  163. if def2 and (not def2.walkable) then
  164. highest = above
  165. else
  166. break
  167. end
  168. end
  169. if highest then
  170. local node = minetest.get_node(pos)
  171. local metatable = minetest.get_meta(pos):to_table()
  172. minetest.remove_node(pos)
  173. minetest.set_node(highest, node)
  174. local meta_highest = minetest.get_meta(highest)
  175. meta_highest:from_table(metatable)
  176. ok = minetest.spawn_falling_node(highest)
  177. else
  178. ok = minetest.spawn_falling_node(pos)
  179. end
  180. if not ok and user and user:is_player() then
  181. minetest.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!"))
  182. end
  183. end,
  184. on_use = function(itemstack, user, pointed_thing)
  185. local pos = minetest.get_pointed_thing_position(pointed_thing)
  186. if pointed_thing.type ~= "node" or (not pos) then
  187. return
  188. end
  189. local ok = minetest.spawn_falling_node(pos)
  190. if not ok and user and user:is_player() then
  191. minetest.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!"))
  192. end
  193. end,
  194. })
  195. minetest.register_tool("testtools:rotator", {
  196. description = S("Entity Rotator") .. "\n" ..
  197. S("Rotate pointed entity") .."\n"..
  198. S("Punch: Yaw") .."\n"..
  199. S("Sneak+Punch: Pitch") .."\n"..
  200. S("Aux1+Punch: Roll"),
  201. inventory_image = "testtools_entity_rotator.png",
  202. groups = { testtool = 1, disable_repair = 1 },
  203. on_use = function(itemstack, user, pointed_thing)
  204. if pointed_thing.type ~= "object" then
  205. return
  206. end
  207. local obj = pointed_thing.ref
  208. if obj:is_player() then
  209. -- No player rotation
  210. return
  211. else
  212. local axis = "y"
  213. if user and user:is_player() then
  214. local ctrl = user:get_player_control()
  215. if ctrl.sneak then
  216. axis = "x"
  217. elseif ctrl.aux1 then
  218. axis = "z"
  219. end
  220. end
  221. local rot = obj:get_rotation()
  222. rot[axis] = rot[axis] + math.pi/8
  223. if rot[axis] > math.pi*2 then
  224. rot[axis] = rot[axis] - math.pi*2
  225. end
  226. obj:set_rotation(rot)
  227. end
  228. end,
  229. })
  230. local mover_config = function(itemstack, user, pointed_thing)
  231. if not (user and user:is_player()) then
  232. return
  233. end
  234. local name = user:get_player_name()
  235. local ctrl = user:get_player_control()
  236. local meta = itemstack:get_meta()
  237. local dist = 1.0
  238. if meta:contains("distance") then
  239. dist = meta:get_int("distance")
  240. end
  241. if ctrl.sneak then
  242. dist = dist - 1
  243. else
  244. dist = dist + 1
  245. end
  246. meta:set_int("distance", dist)
  247. minetest.chat_send_player(user:get_player_name(), S("distance=@1/10", dist*2))
  248. return itemstack
  249. end
  250. minetest.register_tool("testtools:object_mover", {
  251. description = S("Object Mover") .."\n"..
  252. S("Move pointed object towards or away from you") .."\n"..
  253. S("Punch: Move by distance").."\n"..
  254. S("Sneak+Punch: Move by negative distance").."\n"..
  255. S("Place: Increase distance").."\n"..
  256. S("Sneak+Place: Decrease distance"),
  257. inventory_image = "testtools_object_mover.png",
  258. groups = { testtool = 1, disable_repair = 1 },
  259. on_place = mover_config,
  260. on_secondary_use = mover_config,
  261. on_use = function(itemstack, user, pointed_thing)
  262. if pointed_thing.type ~= "object" then
  263. return
  264. end
  265. local obj = pointed_thing.ref
  266. if not (user and user:is_player()) then
  267. return
  268. end
  269. local yaw = user:get_look_horizontal()
  270. local dir = minetest.yaw_to_dir(yaw)
  271. local pos = obj:get_pos()
  272. local pitch = user:get_look_vertical()
  273. if pitch > 0.25 * math.pi then
  274. dir.y = -1
  275. dir.x = 0
  276. dir.z = 0
  277. elseif pitch < -0.25 * math.pi then
  278. dir.y = 1
  279. dir.x = 0
  280. dir.z = 0
  281. end
  282. local ctrl = user:get_player_control()
  283. if ctrl.sneak then
  284. dir = vector.multiply(dir, -1)
  285. end
  286. local meta = itemstack:get_meta()
  287. if meta:contains("distance") then
  288. local dist = meta:get_int("distance")
  289. dir = vector.multiply(dir, dist*0.2)
  290. end
  291. pos = vector.add(pos, dir)
  292. obj:set_pos(pos)
  293. end,
  294. })
  295. minetest.register_tool("testtools:entity_scaler", {
  296. description = S("Entity Visual Scaler") .."\n"..
  297. S("Scale visual size of entities") .."\n"..
  298. S("Punch: Increase size") .."\n"..
  299. S("Sneak+Punch: Decrease scale"),
  300. inventory_image = "testtools_entity_scaler.png",
  301. groups = { testtool = 1, disable_repair = 1 },
  302. on_use = function(itemstack, user, pointed_thing)
  303. if pointed_thing.type ~= "object" then
  304. return
  305. end
  306. local obj = pointed_thing.ref
  307. if obj:is_player() then
  308. -- No player scaling
  309. return
  310. else
  311. local diff = 0.1
  312. if user and user:is_player() then
  313. local ctrl = user:get_player_control()
  314. if ctrl.sneak then
  315. diff = -0.1
  316. end
  317. end
  318. local prop = obj:get_properties()
  319. if not prop.visual_size then
  320. prop.visual_size = { x=1, y=1, z=1 }
  321. else
  322. prop.visual_size = { x=prop.visual_size.x+diff, y=prop.visual_size.y+diff, z=prop.visual_size.z+diff }
  323. if prop.visual_size.x <= 0.1 then
  324. prop.visual_size.x = 0.1
  325. end
  326. if prop.visual_size.y <= 0.1 then
  327. prop.visual_size.y = 0.1
  328. end
  329. if prop.visual_size.z <= 0.1 then
  330. prop.visual_size.z = 0.1
  331. end
  332. end
  333. obj:set_properties(prop)
  334. end
  335. end,
  336. })
  337. local selections = {}
  338. local entity_list
  339. local function get_entity_list()
  340. if entity_list then
  341. return entity_list
  342. end
  343. local ents = minetest.registered_entities
  344. local list = {}
  345. for k,_ in pairs(ents) do
  346. table.insert(list, k)
  347. end
  348. table.sort(list)
  349. entity_list = list
  350. return entity_list
  351. end
  352. minetest.register_tool("testtools:entity_spawner", {
  353. description = S("Entity Spawner") .."\n"..
  354. S("Spawns entities") .."\n"..
  355. S("Punch: Select entity to spawn") .."\n"..
  356. S("Place: Spawn selected entity"),
  357. inventory_image = "testtools_entity_spawner.png",
  358. groups = { testtool = 1, disable_repair = 1 },
  359. on_place = function(itemstack, user, pointed_thing)
  360. local name = user:get_player_name()
  361. if pointed_thing.type == "node" then
  362. if selections[name] then
  363. local pos = pointed_thing.above
  364. minetest.add_entity(pos, get_entity_list()[selections[name]])
  365. else
  366. minetest.chat_send_player(name, S("Select an entity first (with punch key)!"))
  367. end
  368. end
  369. end,
  370. on_use = function(itemstack, user, pointed_thing)
  371. if pointed_thing.type == "object" then
  372. return
  373. end
  374. if user and user:is_player() then
  375. local list = table.concat(get_entity_list(), ",")
  376. local name = user:get_player_name()
  377. local sel = selections[name] or ""
  378. minetest.show_formspec(name, "testtools:entity_list",
  379. "size[9,9]"..
  380. "textlist[0,0;9,8;entity_list;"..list..";"..sel..";false]"..
  381. "button[0,8;4,1;spawn;Spawn entity]"
  382. )
  383. end
  384. end,
  385. })
  386. local function prop_to_string(property)
  387. if type(property) == "string" then
  388. return "\"" .. property .. "\""
  389. elseif type(property) == "table" then
  390. return tostring(dump(property)):gsub("\n", "")
  391. else
  392. return tostring(property)
  393. end
  394. end
  395. local property_formspec_data = {}
  396. local property_formspec_index = {}
  397. local selected_objects = {}
  398. local function get_object_properties_form(obj, playername)
  399. if not playername then return "" end
  400. local props = obj:get_properties()
  401. local str = ""
  402. property_formspec_data[playername] = {}
  403. local proplist = {}
  404. for k,_ in pairs(props) do
  405. table.insert(proplist, k)
  406. end
  407. table.sort(proplist)
  408. for p=1, #proplist do
  409. local k = proplist[p]
  410. local v = props[k]
  411. local newline = ""
  412. newline = k .. " = "
  413. newline = newline .. prop_to_string(v)
  414. str = str .. F(newline)
  415. if p < #proplist then
  416. str = str .. ","
  417. end
  418. table.insert(property_formspec_data[playername], k)
  419. end
  420. return str
  421. end
  422. local editor_formspec_selindex = {}
  423. local editor_formspec = function(playername, obj, value, sel)
  424. if not value then
  425. value = ""
  426. end
  427. if not sel then
  428. sel = ""
  429. end
  430. local list = get_object_properties_form(obj, playername)
  431. local title
  432. if obj:is_player() then
  433. title = S("Object properties of player “@1”", obj:get_player_name())
  434. else
  435. local ent = obj:get_luaentity()
  436. title = S("Object properties of @1", ent.name)
  437. end
  438. minetest.show_formspec(playername, "testtools:object_editor",
  439. "size[9,9]"..
  440. "label[0,0;"..F(title).."]"..
  441. "textlist[0,0.5;9,7.5;object_props;"..list..";"..sel..";false]"..
  442. "field[0.2,8.75;8,1;value;"..F(S("Value"))..";"..F(value).."]"..
  443. "field_close_on_enter[value;false]"..
  444. "button[8,8.5;1,1;submit;"..F(S("Submit")).."]"
  445. )
  446. end
  447. minetest.register_tool("testtools:object_editor", {
  448. description = S("Object Property Editor") .."\n"..
  449. S("Edit properties of objects") .."\n"..
  450. S("Punch object: Edit object") .."\n"..
  451. S("Punch air: Edit yourself"),
  452. inventory_image = "testtools_object_editor.png",
  453. groups = { testtool = 1, disable_repair = 1 },
  454. on_use = function(itemstack, user, pointed_thing)
  455. if user and user:is_player() then
  456. local name = user:get_player_name()
  457. if pointed_thing.type == "object" then
  458. selected_objects[name] = pointed_thing.ref
  459. elseif pointed_thing.type == "nothing" then
  460. -- Use on yourself if pointing nothing
  461. selected_objects[name] = user
  462. else
  463. -- Unsupported pointed thing
  464. return
  465. end
  466. local sel = editor_formspec_selindex[name]
  467. local val
  468. if selected_objects[name] and selected_objects[name]:get_properties() then
  469. local props = selected_objects[name]:get_properties()
  470. local keys = property_formspec_data[name]
  471. if property_formspec_index[name] and props then
  472. local key = keys[property_formspec_index[name]]
  473. val = prop_to_string(props[key])
  474. end
  475. end
  476. editor_formspec(name, selected_objects[name], val, sel)
  477. end
  478. end,
  479. })
  480. local ent_parent = {}
  481. local ent_child = {}
  482. local DEFAULT_ATTACH_OFFSET_Y = 11
  483. local attacher_config = function(itemstack, user, pointed_thing)
  484. if not (user and user:is_player()) then
  485. return
  486. end
  487. if pointed_thing.type == "object" then
  488. return
  489. end
  490. local name = user:get_player_name()
  491. local ctrl = user:get_player_control()
  492. local meta = itemstack:get_meta()
  493. if ctrl.aux1 then
  494. local rot_x = meta:get_float("rot_x")
  495. if ctrl.sneak then
  496. rot_x = rot_x - math.pi/8
  497. else
  498. rot_x = rot_x + math.pi/8
  499. end
  500. if rot_x > 6.2 then
  501. rot_x = 0
  502. elseif rot_x < 0 then
  503. rot_x = math.pi * (15/8)
  504. end
  505. minetest.chat_send_player(name, S("rotation=@1", minetest.pos_to_string({x=rot_x,y=0,z=0})))
  506. meta:set_float("rot_x", rot_x)
  507. else
  508. local pos_y
  509. if meta:contains("pos_y") then
  510. pos_y = meta:get_int("pos_y")
  511. else
  512. pos_y = DEFAULT_ATTACH_OFFSET_Y
  513. end
  514. if ctrl.sneak then
  515. pos_y = pos_y - 1
  516. else
  517. pos_y = pos_y + 1
  518. end
  519. minetest.chat_send_player(name, S("position=@1", minetest.pos_to_string({x=0,y=pos_y,z=0})))
  520. meta:set_int("pos_y", pos_y)
  521. end
  522. return itemstack
  523. end
  524. minetest.register_tool("testtools:object_attacher", {
  525. description = S("Object Attacher") .."\n"..
  526. S("Attach object to another") .."\n"..
  527. S("Punch objects to first select parent object, then the child object to attach") .."\n"..
  528. S("Punch air to select yourself") .."\n"..
  529. S("Place: Incease attachment Y offset") .."\n"..
  530. S("Sneak+Place: Decease attachment Y offset") .."\n"..
  531. S("Aux1+Place: Incease attachment rotation") .."\n"..
  532. S("Aux1+Sneak+Place: Decrease attachment rotation"),
  533. inventory_image = "testtools_object_attacher.png",
  534. groups = { testtool = 1, disable_repair = 1 },
  535. on_place = attacher_config,
  536. on_secondary_use = attacher_config,
  537. on_use = function(itemstack, user, pointed_thing)
  538. if user and user:is_player() then
  539. local name = user:get_player_name()
  540. local selected_object
  541. if pointed_thing.type == "object" then
  542. selected_object = pointed_thing.ref
  543. elseif pointed_thing.type == "nothing" then
  544. selected_object = user
  545. else
  546. return
  547. end
  548. local ctrl = user:get_player_control()
  549. if ctrl.sneak then
  550. if selected_object:get_attach() then
  551. selected_object:set_detach()
  552. minetest.chat_send_player(name, S("Object detached!"))
  553. else
  554. minetest.chat_send_player(name, S("Object is not attached!"))
  555. end
  556. return
  557. end
  558. local parent = ent_parent[name]
  559. local child = ent_child[name]
  560. local ename = S("<unknown>")
  561. if not parent then
  562. parent = selected_object
  563. ent_parent[name] = parent
  564. elseif not child then
  565. child = selected_object
  566. ent_child[name] = child
  567. end
  568. local entity = selected_object:get_luaentity()
  569. if entity then
  570. ename = entity.name
  571. elseif selected_object:is_player() then
  572. ename = selected_object:get_player_name()
  573. end
  574. if selected_object == parent then
  575. minetest.chat_send_player(name, S("Parent object selected: @1", ename))
  576. elseif selected_object == child then
  577. minetest.chat_send_player(name, S("Child object selected: @1", ename))
  578. end
  579. if parent and child then
  580. if parent == child then
  581. minetest.chat_send_player(name, S("Can't attach an object to itself!"))
  582. ent_parent[name] = nil
  583. ent_child[name] = nil
  584. return
  585. end
  586. local meta = itemstack:get_meta()
  587. local y
  588. if meta:contains("pos_y") then
  589. y = meta:get_int("pos_y")
  590. else
  591. y = DEFAULT_ATTACH_OFFSET_Y
  592. end
  593. local rx = meta:get_float("rot_x") or 0
  594. local offset = {x=0,y=y,z=0}
  595. local angle = {x=rx,y=0,z=0}
  596. child:set_attach(parent, "", offset, angle)
  597. local check_parent = child:get_attach()
  598. if check_parent then
  599. minetest.chat_send_player(name, S("Object attached! position=@1, rotation=@2",
  600. minetest.pos_to_string(offset), minetest.pos_to_string(angle)))
  601. else
  602. minetest.chat_send_player(name, S("Attachment failed!"))
  603. end
  604. ent_parent[name] = nil
  605. ent_child[name] = nil
  606. end
  607. end
  608. end,
  609. })
  610. -- Use loadstring to parse param as a Lua value
  611. local function use_loadstring(param, player)
  612. -- For security reasons, require 'server' priv, just in case
  613. -- someone is actually crazy enough to run this on a public server.
  614. local privs = minetest.get_player_privs(player:get_player_name())
  615. if not privs.server then
  616. return false, "You need 'server' privilege to change object properties!"
  617. end
  618. if not param then
  619. return false, "Failed: parameter is nil"
  620. end
  621. --[[ DANGER ZONE ]]
  622. -- Interpret string as Lua value
  623. local func, errormsg = loadstring("return (" .. param .. ")")
  624. if not func then
  625. return false, "Failed: " .. errormsg
  626. end
  627. -- Apply sandbox here using setfenv
  628. setfenv(func, {})
  629. -- Run it
  630. local good, errOrResult = pcall(func)
  631. if not good then
  632. -- A Lua error was thrown
  633. return false, "Failed: " .. errOrResult
  634. end
  635. -- errOrResult will be the value
  636. return true, errOrResult
  637. end
  638. minetest.register_on_player_receive_fields(function(player, formname, fields)
  639. if not (player and player:is_player()) then
  640. return
  641. end
  642. if formname == "testtools:entity_list" then
  643. local name = player:get_player_name()
  644. if fields.entity_list then
  645. local expl = minetest.explode_textlist_event(fields.entity_list)
  646. if expl.type == "DCL" then
  647. local pos = vector.add(player:get_pos(), {x=0,y=1,z=0})
  648. selections[name] = expl.index
  649. minetest.add_entity(pos, get_entity_list()[expl.index])
  650. return
  651. elseif expl.type == "CHG" then
  652. selections[name] = expl.index
  653. return
  654. end
  655. elseif fields.spawn and selections[name] then
  656. local pos = vector.add(player:get_pos(), {x=0,y=1,z=0})
  657. minetest.add_entity(pos, get_entity_list()[selections[name]])
  658. return
  659. end
  660. elseif formname == "testtools:object_editor" then
  661. local name = player:get_player_name()
  662. if fields.object_props then
  663. local expl = minetest.explode_textlist_event(fields.object_props)
  664. if expl.type == "DCL" or expl.type == "CHG" then
  665. property_formspec_index[name] = expl.index
  666. local props = selected_objects[name]:get_properties()
  667. local keys = property_formspec_data[name]
  668. if (not property_formspec_index[name]) or (not props) then
  669. return
  670. end
  671. local key = keys[property_formspec_index[name]]
  672. editor_formspec_selindex[name] = expl.index
  673. editor_formspec(name, selected_objects[name], prop_to_string(props[key]), expl.index)
  674. return
  675. end
  676. end
  677. if fields.key_enter_field == "value" or fields.submit then
  678. local props = selected_objects[name]:get_properties()
  679. local keys = property_formspec_data[name]
  680. if (not property_formspec_index[name]) or (not props) then
  681. return
  682. end
  683. local key = keys[property_formspec_index[name]]
  684. if not key then
  685. return
  686. end
  687. local success, str = use_loadstring(fields.value, player)
  688. if success then
  689. props[key] = str
  690. else
  691. minetest.chat_send_player(name, str)
  692. return
  693. end
  694. selected_objects[name]:set_properties(props)
  695. local sel = editor_formspec_selindex[name]
  696. editor_formspec(name, selected_objects[name], prop_to_string(props[key]), sel)
  697. return
  698. end
  699. end
  700. end)