init.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. screwdriver = screwdriver or {}
  2. local tmp = {}
  3. local should_return_item = minetest.settings:get_bool("itemframes.return_item", false)
  4. local log_actions = minetest.settings:get_bool("itemframes.log_actions", false)
  5. -- item entity
  6. minetest.register_entity("itemframes:item", {
  7. hp_max = 1,
  8. visual = "wielditem",
  9. visual_size = {x = 0.33, y = 0.33},
  10. collisionbox = {0, 0, 0, 0, 0, 0},
  11. physical = false,
  12. textures = {"air"},
  13. static_save = false,
  14. on_activate = function(self, staticdata)
  15. if tmp.nodename and tmp.texture then
  16. self.nodename = tmp.nodename ; tmp.nodename = nil
  17. self.texture = tmp.texture ; tmp.texture = nil
  18. self.glow = tmp.glow ; tmp.glow = nil
  19. else
  20. if staticdata and staticdata ~= "" then
  21. local data = staticdata:split(";")
  22. if data and data[1] and data[2] then
  23. self.nodename = data[1]
  24. self.texture = data[2]
  25. self.glow = data[3]
  26. end
  27. end
  28. end
  29. if self.texture then
  30. self.object:set_properties({textures = {self.texture}})
  31. end
  32. if self.nodename == "itemframes:pedestal" then
  33. self.object:set_properties({automatic_rotate = 1})
  34. end
  35. if self.glow then
  36. self.object:set_properties({glow = self.glow})
  37. end
  38. end,
  39. get_staticdata = function(self)
  40. if self.nodename and self.texture then
  41. return self.nodename .. ";" .. self.texture .. ";" .. (self.glow or "")
  42. end
  43. return ""
  44. end,
  45. on_blast = function(self, damage)
  46. return false, false, {}
  47. end
  48. })
  49. -- helper table
  50. local facedir = {
  51. [0] = {x = 0, y = 0, z = 1},
  52. [12] = {x = 0, y = 0, z = 1},
  53. [16] = {x = 0, y = 0, z = 1},
  54. [20] = {x = 0, y = 0, z = 1},
  55. [1] = {x = 1, y = 0, z = 0},
  56. [5] = {x = 1, y = 0, z = 0},
  57. [9] = {x = 1, y = 0, z = 0},
  58. [23] = {x = 1, y = 0, z = 0},
  59. [2] = {x = 0, y = 0, z = -1},
  60. [14] = {x = 0, y = 0, z = -1},
  61. [18] = {x = 0, y = 0, z = -1},
  62. [22] = {x = 0, y = 0, z = -1},
  63. [3] = {x = -1, y = 0, z = 0},
  64. [7] = {x = -1, y = 0, z = 0},
  65. [11] = {x = -1, y = 0, z = 0},
  66. [21] = {x = -1, y = 0, z = 0},
  67. [4] = -0.4, -- flat frames
  68. [10] = -0.4,
  69. [13] = -0.4,
  70. [19] = -0.4,
  71. [8] = 0.4, -- upside down flat frames
  72. [6] = 0.4,
  73. [15] = 0.4,
  74. [17] = 0.4
  75. }
  76. -- remove entities
  77. local remove_item = function(pos, ntype)
  78. local ypos = 0
  79. if ntype == "pedestal" then
  80. ypos = 1
  81. end
  82. local objs = minetest.get_objects_inside_radius({
  83. x = pos.x, y = pos.y + ypos, z = pos.z}, 0.5)
  84. if objs then
  85. for _, obj in pairs(objs) do
  86. if obj and obj:get_luaentity()
  87. and obj:get_luaentity().name == "itemframes:item" then
  88. obj:remove()
  89. end
  90. end
  91. end
  92. end
  93. -- update entity
  94. local update_item = function(pos, ntype, node)
  95. remove_item(pos, ntype)
  96. local meta = minetest.get_meta(pos)
  97. if not meta then return end
  98. local item = meta:get_string("item")
  99. if item == "" then return end
  100. local pitch = 0
  101. local p2 = node.param2
  102. if ntype == "frame" then
  103. local posad = facedir[p2]
  104. if not posad then return end
  105. if type(posad) == "table" then
  106. pos.x = pos.x + posad.x * 6.5 / 16
  107. pos.y = pos.y + posad.y * 6.5 / 16
  108. pos.z = pos.z + posad.z * 6.5 / 16
  109. else
  110. pitch = 4.7
  111. pos.y = pos.y + posad
  112. end
  113. elseif ntype == "pedestal" then
  114. pos.y = pos.y + 12 / 16 + 0.33
  115. end
  116. tmp.nodename = node.name
  117. tmp.texture = ItemStack(item):get_name()
  118. local def = core.registered_items[item]
  119. tmp.glow = def and def.light_source
  120. local e = minetest.add_entity(pos, "itemframes:item")
  121. if ntype == "frame" then
  122. --local yaw = math.pi * 2 - node.param2 * math.pi / 2
  123. local yaw = 6.28 - p2 * 1.57
  124. e:set_rotation({
  125. x = pitch, -- pitch
  126. y = yaw, -- yaw
  127. z = 0 -- roll
  128. })
  129. end
  130. end
  131. -- remove entity and drop as item
  132. local drop_item = function(pos, ntype, metadata)
  133. local meta = metadata or minetest.get_meta(pos)
  134. if not meta then return end
  135. local item = meta:get_string("item")
  136. meta:set_string("item", "")
  137. if item ~= "" then
  138. remove_item(pos, ntype)
  139. if ntype == "pedestal" then
  140. pos.y = pos.y + 1
  141. end
  142. minetest.add_item(pos, item)
  143. end
  144. end
  145. -- return item to a player's inventory
  146. local return_item = function(pos, ntype, metadata, clicker, itemstack)
  147. local meta = metadata or minetest.get_meta(pos)
  148. if not meta then return end
  149. local item = meta:get_string("item")
  150. if item == "" then return end
  151. local remaining = itemstack:add_item(item)
  152. if remaining:is_empty() then
  153. meta:set_string("item", "")
  154. remove_item(pos, ntype)
  155. return itemstack
  156. end
  157. local inv = clicker:get_inventory()
  158. if not inv then
  159. drop_item(pos, ntype, metadata)
  160. return
  161. end
  162. remaining = inv:add_item("main", remaining)
  163. if remaining:is_empty() then
  164. meta:set_string("item", "")
  165. remove_item(pos, ntype)
  166. else
  167. drop_item(pos, ntype, metadata)
  168. end
  169. end
  170. -- on_place helper function
  171. local frame_place = function(itemstack, placer, pointed_thing)
  172. if pointed_thing.type ~= "node" then return end
  173. local above = pointed_thing.above
  174. local under = pointed_thing.under
  175. local dir = {
  176. x = under.x - above.x,
  177. y = under.y - above.y,
  178. z = under.z - above.z
  179. }
  180. local wdir = minetest.dir_to_wallmounted(dir)
  181. local placer_pos = placer:get_pos()
  182. if placer_pos then
  183. dir = {
  184. x = above.x - placer_pos.x,
  185. y = above.y - placer_pos.y,
  186. z = above.z - placer_pos.z
  187. }
  188. end
  189. local fdir = minetest.dir_to_facedir(dir)
  190. local p2 = fdir
  191. if wdir == 0 then
  192. p2 = 8
  193. elseif wdir == 1 then
  194. p2 = 4
  195. end
  196. return minetest.item_place(itemstack, placer, pointed_thing, p2)
  197. end
  198. -- action logging helper
  199. local function show_msg(message)
  200. if log_actions then
  201. minetest.log("action", message)
  202. end
  203. end
  204. -- itemframe node and recipe
  205. minetest.register_node("itemframes:frame",{
  206. description = "Item frame",
  207. drawtype = "nodebox",
  208. node_box = {
  209. type = "fixed",
  210. fixed = {-0.5, -0.5, 7/16, 0.5, 0.5, 0.5}
  211. },
  212. selection_box = {
  213. type = "fixed",
  214. fixed = {-0.5, -0.5, 7/16, 0.5, 0.5, 0.5}
  215. },
  216. tiles = {"itemframes_frame.png"},
  217. inventory_image = "itemframes_frame.png",
  218. wield_image = "itemframes_frame.png",
  219. paramtype = "light",
  220. paramtype2 = "facedir",
  221. sunlight_propagates = true,
  222. groups = {choppy = 2, dig_immediate = 2, flammable = 2},
  223. sounds = default.node_sound_defaults(),
  224. on_place = frame_place,
  225. after_place_node = function(pos, placer, itemstack)
  226. local meta = minetest.get_meta(pos)
  227. meta:set_string("infotext","Item frame (right-click to add or remove item)")
  228. end,
  229. on_rightclick = function(pos, node, clicker, itemstack)
  230. if not itemstack
  231. or minetest.is_protected(pos, clicker:get_player_name()) then
  232. return
  233. end
  234. local meta = minetest.get_meta(pos)
  235. if not meta then return end
  236. if meta:get_string("item") ~= "" then
  237. show_msg(clicker:get_player_name()
  238. .. " removed " .. meta:get_string("item")
  239. .. " from Itemframe at " .. minetest.pos_to_string(pos))
  240. if should_return_item then
  241. return return_item(pos, "frame", meta, clicker, itemstack)
  242. else
  243. drop_item(pos, "frame", meta)
  244. end
  245. else
  246. local s = itemstack:take_item()
  247. meta:set_string("item", s:to_string())
  248. update_item(pos, "frame", node)
  249. show_msg(clicker:get_player_name()
  250. .. " inserted " .. meta:get_string("item")
  251. .. " into Itemframe at " .. minetest.pos_to_string(pos))
  252. return itemstack
  253. end
  254. end,
  255. on_destruct = function(pos)
  256. drop_item(pos, "frame")
  257. end,
  258. on_punch = function(pos, node, puncher)
  259. update_item(pos, "frame", node)
  260. end,
  261. on_blast = function(pos, intensity)
  262. drop_item(pos, "frame")
  263. minetest.add_item(pos, {name = "itemframes:frame"})
  264. minetest.remove_node(pos)
  265. end,
  266. on_burn = function(pos)
  267. drop_item(pos, "frame")
  268. minetest.remove_node(pos)
  269. end
  270. })
  271. minetest.register_craft({
  272. output = "itemframes:frame",
  273. recipe = {
  274. {"default:stick", "default:stick", "default:stick"},
  275. {"default:stick", "default:paper", "default:stick"},
  276. {"default:stick", "default:stick", "default:stick"}
  277. }
  278. })
  279. -- invisible itemframe node and recipe
  280. minetest.register_node("itemframes:frame_invis",{
  281. description = "Invisible Item frame",
  282. drawtype = "nodebox",
  283. node_box = {
  284. type = "fixed",
  285. fixed = {-0.5, -0.5, 7/16, 0.5, 0.5, 0.5}
  286. },
  287. selection_box = {
  288. type = "fixed",
  289. fixed = {-0.5, -0.5, 7/16, 0.5, 0.5, 0.5}
  290. },
  291. tiles = {"itemframes_clear.png"},
  292. inventory_image = "itemframes_frame_invis.png",
  293. wield_image = "itemframes_frame_invis.png",
  294. paramtype = "light",
  295. paramtype2 = "facedir",
  296. sunlight_propagates = true,
  297. use_texture_alpha = "clip",
  298. groups = {choppy = 2, dig_immediate = 2, flammable = 2},
  299. sounds = default.node_sound_defaults(),
  300. on_place = frame_place,
  301. after_place_node = function(pos, placer, itemstack)
  302. local meta = minetest.get_meta(pos)
  303. meta:set_string("infotext","Item frame (right-click to add or remove item)")
  304. end,
  305. on_rightclick = function(pos, node, clicker, itemstack)
  306. if not itemstack
  307. or minetest.is_protected(pos, clicker:get_player_name()) then
  308. return
  309. end
  310. local meta = minetest.get_meta(pos)
  311. if not meta then return end
  312. if meta:get_string("item") ~= "" then
  313. show_msg(clicker:get_player_name()
  314. .. " removed " .. meta:get_string("item")
  315. .. " from Itemframe at " .. minetest.pos_to_string(pos))
  316. if should_return_item then
  317. return return_item(pos, "frame", meta, clicker, itemstack)
  318. else
  319. drop_item(pos, "frame", meta)
  320. end
  321. else
  322. local s = itemstack:take_item()
  323. meta:set_string("item", s:to_string())
  324. update_item(pos, "frame", node)
  325. show_msg(clicker:get_player_name()
  326. .. " inserted " .. meta:get_string("item")
  327. .. " into Itemframe at " .. minetest.pos_to_string(pos))
  328. return itemstack
  329. end
  330. end,
  331. on_destruct = function(pos)
  332. drop_item(pos, "frame")
  333. end,
  334. on_punch = function(pos, node, puncher)
  335. update_item(pos, "frame", node)
  336. end,
  337. on_blast = function(pos, intensity)
  338. drop_item(pos, "frame")
  339. minetest.add_item(pos, {name = "itemframes:frame_invis"})
  340. minetest.remove_node(pos)
  341. end,
  342. on_burn = function(pos)
  343. drop_item(pos, "frame")
  344. minetest.remove_node(pos)
  345. end
  346. })
  347. minetest.register_craft({
  348. output = "itemframes:frame_invis",
  349. recipe = {
  350. {"default:glass", "default:glass", "default:glass"},
  351. {"default:glass", "default:paper", "default:glass"},
  352. {"default:glass", "default:glass", "default:glass"}
  353. }
  354. })
  355. -- pedestal node and recipe
  356. minetest.register_node("itemframes:pedestal",{
  357. description = "Pedestal",
  358. drawtype = "nodebox",
  359. node_box = {
  360. type = "fixed", fixed = {
  361. {-7/16, -8/16, -7/16, 7/16, -7/16, 7/16}, -- bottom plate
  362. {-6/16, -7/16, -6/16, 6/16, -6/16, 6/16}, -- bottom plate (upper)
  363. {-0.25, -6/16, -0.25, 0.25, 11/16, 0.25}, -- pillar
  364. {-7/16, 11/16, -7/16, 7/16, 12/16, 7/16}, -- top plate
  365. }
  366. },
  367. selection_box = {
  368. type = "fixed",
  369. fixed = {-7/16, -0.5, -7/16, 7/16, 12/16, 7/16}
  370. },
  371. tiles = {"itemframes_pedestal_top.png","itemframes_pedestal_btm.png","itemframes_pedestal.png"},
  372. paramtype = "light",
  373. groups = {cracky = 3},
  374. sounds = default.node_sound_defaults(),
  375. on_rotate = screwdriver.disallow,
  376. after_place_node = function(pos, placer, itemstack)
  377. local meta = minetest.get_meta(pos)
  378. meta:set_string("infotext","Pedestal (right-click to add or remove item)")
  379. end,
  380. on_rightclick = function(pos, node, clicker, itemstack)
  381. if not itemstack
  382. or minetest.is_protected(pos, clicker:get_player_name()) then
  383. return
  384. end
  385. local meta = minetest.get_meta(pos)
  386. if not meta then return end
  387. if meta:get_string("item") ~= "" then
  388. show_msg(clicker:get_player_name()
  389. .. " removed " .. meta:get_string("item")
  390. .. " from Pedestal at " .. minetest.pos_to_string(pos))
  391. if should_return_item then
  392. return return_item(pos, "pedestal", meta, clicker, itemstack)
  393. else
  394. drop_item(pos, "pedestal", meta)
  395. end
  396. else
  397. local s = itemstack:take_item()
  398. meta:set_string("item", s:to_string())
  399. update_item(pos, "pedestal", node)
  400. show_msg(clicker:get_player_name()
  401. .. " inserted " .. meta:get_string("item")
  402. .. " into Pedestal at " .. minetest.pos_to_string(pos))
  403. return itemstack
  404. end
  405. end,
  406. on_destruct = function(pos)
  407. drop_item(pos, "pedestal")
  408. end,
  409. on_punch = function(pos, node, puncher)
  410. update_item(pos, "pedestal", node)
  411. end,
  412. on_blast = function(pos, intensity)
  413. drop_item(pos, "pedestal")
  414. minetest.add_item(pos, {name = "itemframes:pedestal"})
  415. minetest.remove_node(pos)
  416. end
  417. })
  418. minetest.register_craft({
  419. output = "itemframes:pedestal",
  420. recipe = {
  421. {"default:stone", "default:stone", "default:stone"},
  422. {"", "default:stone", ""},
  423. {"default:stone", "default:stone", "default:stone"}
  424. }
  425. })
  426. -- automatically restore entities lost from frames/pedestals
  427. -- due to /clearobjects or similar
  428. minetest.register_lbm({
  429. label = "Restore itemframe entities",
  430. name = "itemframes:restore_entities",
  431. nodenames = {"itemframes:frame", "itemframes:pedestal", "itemframes:frame_invis"},
  432. run_at_every_load = true,
  433. action = function(pos, node)
  434. local ypos = 0
  435. local ntype = "frame"
  436. if node.name == "itemframes:pedestal" then
  437. ypos = 1
  438. ntype = "pedestal"
  439. end
  440. pos.y = pos.y + ypos
  441. local objs = minetest.get_objects_inside_radius(pos, 0.5)
  442. for _, obj in ipairs(objs) do
  443. local e = obj:get_luaentity()
  444. if e and e.name == "itemframes:item" then
  445. return
  446. end
  447. end
  448. pos.y = pos.y - ypos
  449. update_item(pos, ntype, node)
  450. end
  451. })
  452. -- stop mesecon pistons from pushing itemframe and pedestals
  453. if minetest.get_modpath("mesecons_mvps") then
  454. mesecon.register_mvps_stopper("itemframes:frame")
  455. mesecon.register_mvps_stopper("itemframes:frame_invis")
  456. mesecon.register_mvps_stopper("itemframes:pedestal")
  457. end
  458. print("[MOD] Itemframes loaded")