chests.lua 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. default.chest = {}
  2. -- support for MT game translation.
  3. local S = default.get_translator
  4. function default.chest.get_chest_formspec(pos)
  5. local spos = pos.x .. "," .. pos.y .. "," .. pos.z
  6. local formspec =
  7. "size[8,9]" ..
  8. "list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]" ..
  9. "list[current_player;main;0,4.85;8,1;]" ..
  10. "list[current_player;main;0,6.08;8,3;8]" ..
  11. "listring[nodemeta:" .. spos .. ";main]" ..
  12. "listring[current_player;main]" ..
  13. default.get_hotbar_bg(0,4.85)
  14. return formspec
  15. end
  16. function default.chest.chest_lid_obstructed(pos)
  17. local above = {x = pos.x, y = pos.y + 1, z = pos.z}
  18. local def = minetest.registered_nodes[minetest.get_node(above).name]
  19. -- allow ladders, signs, wallmounted things and torches to not obstruct
  20. if def and
  21. (def.drawtype == "airlike" or
  22. def.drawtype == "signlike" or
  23. def.drawtype == "torchlike" or
  24. (def.drawtype == "nodebox" and def.paramtype2 == "wallmounted")) then
  25. return false
  26. end
  27. return true
  28. end
  29. function default.chest.chest_lid_close(pn)
  30. local chest_open_info = default.chest.open_chests[pn]
  31. local pos = chest_open_info.pos
  32. local sound = chest_open_info.sound
  33. local swap = chest_open_info.swap
  34. default.chest.open_chests[pn] = nil
  35. for k, v in pairs(default.chest.open_chests) do
  36. if v.pos.x == pos.x and v.pos.y == pos.y and v.pos.z == pos.z then
  37. return true
  38. end
  39. end
  40. local node = minetest.get_node(pos)
  41. minetest.after(0.2, minetest.swap_node, pos, { name = swap,
  42. param2 = node.param2 })
  43. minetest.sound_play(sound, {gain = 0.3, pos = pos,
  44. max_hear_distance = 10}, true)
  45. end
  46. default.chest.open_chests = {}
  47. minetest.register_on_player_receive_fields(function(player, formname, fields)
  48. if formname ~= "default:chest" then
  49. return
  50. end
  51. if not player or not fields.quit then
  52. return
  53. end
  54. local pn = player:get_player_name()
  55. if not default.chest.open_chests[pn] then
  56. return
  57. end
  58. default.chest.chest_lid_close(pn)
  59. return true
  60. end)
  61. minetest.register_on_leaveplayer(function(player)
  62. local pn = player:get_player_name()
  63. if default.chest.open_chests[pn] then
  64. default.chest.chest_lid_close(pn)
  65. end
  66. end)
  67. function default.chest.register_chest(prefixed_name, d)
  68. local name = prefixed_name:sub(1,1) == ':' and prefixed_name:sub(2,-1) or prefixed_name
  69. local def = table.copy(d)
  70. def.drawtype = "mesh"
  71. def.visual = "mesh"
  72. def.paramtype = "light"
  73. def.paramtype2 = "facedir"
  74. def.legacy_facedir_simple = true
  75. def.is_ground_content = false
  76. if def.protected then
  77. def.on_construct = function(pos)
  78. local meta = minetest.get_meta(pos)
  79. meta:set_string("infotext", S("Locked Chest"))
  80. meta:set_string("owner", "")
  81. local inv = meta:get_inventory()
  82. inv:set_size("main", 8*4)
  83. end
  84. def.after_place_node = function(pos, placer)
  85. local meta = minetest.get_meta(pos)
  86. meta:set_string("owner", placer:get_player_name() or "")
  87. meta:set_string("infotext", S("Locked Chest (owned by @1)", meta:get_string("owner")))
  88. end
  89. def.can_dig = function(pos,player)
  90. local meta = minetest.get_meta(pos);
  91. local inv = meta:get_inventory()
  92. return inv:is_empty("main") and
  93. default.can_interact_with_node(player, pos)
  94. end
  95. def.allow_metadata_inventory_move = function(pos, from_list, from_index,
  96. to_list, to_index, count, player)
  97. if not default.can_interact_with_node(player, pos) then
  98. return 0
  99. end
  100. return count
  101. end
  102. def.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  103. if not default.can_interact_with_node(player, pos) then
  104. return 0
  105. end
  106. return stack:get_count()
  107. end
  108. def.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  109. if not default.can_interact_with_node(player, pos) then
  110. return 0
  111. end
  112. return stack:get_count()
  113. end
  114. def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
  115. if not default.can_interact_with_node(clicker, pos) then
  116. return itemstack
  117. end
  118. minetest.sound_play(def.sound_open, {gain = 0.3,
  119. pos = pos, max_hear_distance = 10}, true)
  120. if not default.chest.chest_lid_obstructed(pos) then
  121. minetest.swap_node(pos,
  122. { name = name .. "_open",
  123. param2 = node.param2 })
  124. end
  125. minetest.after(0.2, minetest.show_formspec,
  126. clicker:get_player_name(),
  127. "default:chest", default.chest.get_chest_formspec(pos))
  128. default.chest.open_chests[clicker:get_player_name()] = { pos = pos,
  129. sound = def.sound_close, swap = name }
  130. end
  131. def.on_blast = function() end
  132. def.on_key_use = function(pos, player)
  133. local secret = minetest.get_meta(pos):get_string("key_lock_secret")
  134. local itemstack = player:get_wielded_item()
  135. local key_meta = itemstack:get_meta()
  136. if itemstack:get_metadata() == "" then
  137. return
  138. end
  139. if key_meta:get_string("secret") == "" then
  140. key_meta:set_string("secret", minetest.parse_json(itemstack:get_metadata()).secret)
  141. itemstack:set_metadata("")
  142. end
  143. if secret ~= key_meta:get_string("secret") then
  144. return
  145. end
  146. minetest.show_formspec(
  147. player:get_player_name(),
  148. "default:chest_locked",
  149. default.chest.get_chest_formspec(pos)
  150. )
  151. end
  152. def.on_skeleton_key_use = function(pos, player, newsecret)
  153. local meta = minetest.get_meta(pos)
  154. local owner = meta:get_string("owner")
  155. local pn = player:get_player_name()
  156. -- verify placer is owner of lockable chest
  157. if owner ~= pn then
  158. minetest.record_protection_violation(pos, pn)
  159. minetest.chat_send_player(pn, S("You do not own this chest."))
  160. return nil
  161. end
  162. local secret = meta:get_string("key_lock_secret")
  163. if secret == "" then
  164. secret = newsecret
  165. meta:set_string("key_lock_secret", secret)
  166. end
  167. return secret, S("a locked chest"), owner
  168. end
  169. else
  170. def.on_construct = function(pos)
  171. local meta = minetest.get_meta(pos)
  172. meta:set_string("infotext", S("Chest"))
  173. local inv = meta:get_inventory()
  174. inv:set_size("main", 8*4)
  175. end
  176. def.can_dig = function(pos,player)
  177. local meta = minetest.get_meta(pos);
  178. local inv = meta:get_inventory()
  179. return inv:is_empty("main")
  180. end
  181. def.on_rightclick = function(pos, node, clicker)
  182. minetest.sound_play(def.sound_open, {gain = 0.3, pos = pos,
  183. max_hear_distance = 10}, true)
  184. if not default.chest.chest_lid_obstructed(pos) then
  185. minetest.swap_node(pos, {
  186. name = name .. "_open",
  187. param2 = node.param2 })
  188. end
  189. minetest.after(0.2, minetest.show_formspec,
  190. clicker:get_player_name(),
  191. "default:chest", default.chest.get_chest_formspec(pos))
  192. default.chest.open_chests[clicker:get_player_name()] = { pos = pos,
  193. sound = def.sound_close, swap = name }
  194. end
  195. def.on_blast = function(pos)
  196. local drops = {}
  197. default.get_inventory_drops(pos, "main", drops)
  198. drops[#drops+1] = name
  199. minetest.remove_node(pos)
  200. return drops
  201. end
  202. end
  203. default.set_inventory_action_loggers(def, "chest")
  204. local def_opened = table.copy(def)
  205. local def_closed = table.copy(def)
  206. def_opened.mesh = "chest_open.obj"
  207. for i = 1, #def_opened.tiles do
  208. if type(def_opened.tiles[i]) == "string" then
  209. def_opened.tiles[i] = {name = def_opened.tiles[i], backface_culling = true}
  210. elseif def_opened.tiles[i].backface_culling == nil then
  211. def_opened.tiles[i].backface_culling = true
  212. end
  213. end
  214. def_opened.drop = name
  215. def_opened.groups.not_in_creative_inventory = 1
  216. def_opened.selection_box = {
  217. type = "fixed",
  218. fixed = { -1/2, -1/2, -1/2, 1/2, 3/16, 1/2 },
  219. }
  220. def_opened.can_dig = function()
  221. return false
  222. end
  223. def_opened.on_blast = function() end
  224. def_closed.mesh = nil
  225. def_closed.drawtype = nil
  226. def_closed.tiles[6] = def.tiles[5] -- swap textures around for "normal"
  227. def_closed.tiles[5] = def.tiles[3] -- drawtype to make them match the mesh
  228. def_closed.tiles[3] = def.tiles[3].."^[transformFX"
  229. minetest.register_node(prefixed_name, def_closed)
  230. minetest.register_node(prefixed_name .. "_open", def_opened)
  231. -- convert old chests to this new variant
  232. if name == "default:chest" or name == "default:chest_locked" then
  233. minetest.register_lbm({
  234. label = "update chests to opening chests",
  235. name = "default:upgrade_" .. name:sub(9,-1) .. "_v2",
  236. nodenames = {name},
  237. action = function(pos, node)
  238. local meta = minetest.get_meta(pos)
  239. meta:set_string("formspec", nil)
  240. local inv = meta:get_inventory()
  241. local list = inv:get_list("default:chest")
  242. if list then
  243. inv:set_size("main", 8*4)
  244. inv:set_list("main", list)
  245. inv:set_list("default:chest", nil)
  246. end
  247. end
  248. })
  249. end
  250. end
  251. default.chest.register_chest("default:chest", {
  252. description = S("Chest"),
  253. tiles = {
  254. "default_chest_top.png",
  255. "default_chest_top.png",
  256. "default_chest_side.png",
  257. "default_chest_side.png",
  258. "default_chest_front.png",
  259. "default_chest_inside.png"
  260. },
  261. sounds = default.node_sound_wood_defaults(),
  262. sound_open = "default_chest_open",
  263. sound_close = "default_chest_close",
  264. groups = {choppy = 2, oddly_breakable_by_hand = 2},
  265. })
  266. default.chest.register_chest("default:chest_locked", {
  267. description = S("Locked Chest"),
  268. tiles = {
  269. "default_chest_top.png",
  270. "default_chest_top.png",
  271. "default_chest_side.png",
  272. "default_chest_side.png",
  273. "default_chest_lock.png",
  274. "default_chest_inside.png"
  275. },
  276. sounds = default.node_sound_wood_defaults(),
  277. sound_open = "default_chest_open",
  278. sound_close = "default_chest_close",
  279. groups = {choppy = 2, oddly_breakable_by_hand = 2},
  280. protected = true,
  281. })
  282. minetest.register_craft({
  283. output = "default:chest",
  284. recipe = {
  285. {"group:wood", "group:wood", "group:wood"},
  286. {"group:wood", "", "group:wood"},
  287. {"group:wood", "group:wood", "group:wood"},
  288. }
  289. })
  290. minetest.register_craft({
  291. output = "default:chest_locked",
  292. recipe = {
  293. {"group:wood", "group:wood", "group:wood"},
  294. {"group:wood", "default:steel_ingot", "group:wood"},
  295. {"group:wood", "group:wood", "group:wood"},
  296. }
  297. })
  298. minetest.register_craft( {
  299. type = "shapeless",
  300. output = "default:chest_locked",
  301. recipe = {"default:chest", "default:steel_ingot"},
  302. })
  303. minetest.register_craft({
  304. type = "fuel",
  305. recipe = "default:chest",
  306. burntime = 30,
  307. })
  308. minetest.register_craft({
  309. type = "fuel",
  310. recipe = "default:chest_locked",
  311. burntime = 30,
  312. })