init.lua 12 KB

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