doors_chest.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. -- Since the doors mod has changed in the latest daily builds I have taken the
  2. -- WTFPL licenced code from the old doors mod and included an edited version
  3. -- within this mod for local use.
  4. local S = protector.intllib
  5. local F = minetest.formspec_escape
  6. -- Registers a door
  7. function register_door(name, def)
  8. def.groups.not_in_creative_inventory = 1
  9. local box = {{-0.5, -0.5, -0.5, 0.5, 0.5, -0.5+1.5/16}}
  10. def.node_box_bottom = box
  11. def.node_box_top = box
  12. def.selection_box_bottom = box
  13. def.selection_box_top = box
  14. def.sound_close_door = "doors_door_close"
  15. def.sound_open_door = "doors_door_open"
  16. minetest.register_craftitem(name, {
  17. description = def.description,
  18. inventory_image = def.inventory_image,
  19. on_place = function(itemstack, placer, pointed_thing)
  20. if not pointed_thing.type == "node" then
  21. return itemstack
  22. end
  23. local ptu = pointed_thing.under
  24. local nu = minetest.get_node(ptu)
  25. if minetest.registered_nodes[nu.name]
  26. and minetest.registered_nodes[nu.name].on_rightclick then
  27. return minetest.registered_nodes[nu.name].on_rightclick(ptu, nu, placer, itemstack)
  28. end
  29. local pt = pointed_thing.above
  30. local pt2 = {x=pt.x, y=pt.y, z=pt.z}
  31. pt2.y = pt2.y+1
  32. if
  33. not minetest.registered_nodes[minetest.get_node(pt).name].buildable_to or
  34. not minetest.registered_nodes[minetest.get_node(pt2).name].buildable_to or
  35. not placer or
  36. not placer:is_player()
  37. then
  38. return itemstack
  39. end
  40. if minetest.is_protected(pt, placer:get_player_name()) or
  41. minetest.is_protected(pt2, placer:get_player_name()) then
  42. minetest.record_protection_violation(pt, placer:get_player_name())
  43. return itemstack
  44. end
  45. local p2 = minetest.dir_to_facedir(placer:get_look_dir())
  46. local pt3 = {x=pt.x, y=pt.y, z=pt.z}
  47. if p2 == 0 then
  48. pt3.x = pt3.x-1
  49. elseif p2 == 1 then
  50. pt3.z = pt3.z+1
  51. elseif p2 == 2 then
  52. pt3.x = pt3.x+1
  53. elseif p2 == 3 then
  54. pt3.z = pt3.z-1
  55. end
  56. if minetest.get_item_group(minetest.get_node(pt3).name, "door") == 0 then
  57. minetest.set_node(pt, {name=name.."_b_1", param2=p2})
  58. minetest.set_node(pt2, {name=name.."_t_1", param2=p2})
  59. else
  60. minetest.set_node(pt, {name=name.."_b_2", param2=p2})
  61. minetest.set_node(pt2, {name=name.."_t_2", param2=p2})
  62. minetest.get_meta(pt):set_int("right", 1)
  63. minetest.get_meta(pt2):set_int("right", 1)
  64. end
  65. if not minetest.setting_getbool("creative_mode") then
  66. itemstack:take_item()
  67. end
  68. return itemstack
  69. end,
  70. })
  71. local tt = def.tiles_top
  72. local tb = def.tiles_bottom
  73. local function after_dig_node(pos, name, digger)
  74. local node = minetest.get_node(pos)
  75. if node.name == name then
  76. minetest.node_dig(pos, node, digger)
  77. end
  78. end
  79. local function on_rightclick(pos, dir, check_name, replace, replace_dir, params)
  80. pos.y = pos.y+dir
  81. if minetest.get_node(pos).name ~= check_name then
  82. return
  83. end
  84. local p2 = minetest.get_node(pos).param2
  85. p2 = params[p2+1]
  86. minetest.swap_node(pos, {name=replace_dir, param2=p2})
  87. pos.y = pos.y-dir
  88. minetest.swap_node(pos, {name=replace, param2=p2})
  89. local snd_1 = def.sound_close_door
  90. local snd_2 = def.sound_open_door
  91. if params[1] == 3 then
  92. snd_1 = def.sound_open_door
  93. snd_2 = def.sound_close_door
  94. end
  95. if minetest.get_meta(pos):get_int("right") ~= 0 then
  96. minetest.sound_play(snd_1, {pos = pos, gain = 0.3, max_hear_distance = 10})
  97. else
  98. minetest.sound_play(snd_2, {pos = pos, gain = 0.3, max_hear_distance = 10})
  99. end
  100. end
  101. local function on_rotate(pos, node, dir, user, check_name, mode, new_param2)
  102. if mode ~= screwdriver.ROTATE_FACE then
  103. return false
  104. end
  105. pos.y = pos.y + dir
  106. if not minetest.get_node(pos).name == check_name then
  107. return false
  108. end
  109. if minetest.is_protected(pos, user:get_player_name()) then
  110. minetest.record_protection_violation(pos, user:get_player_name())
  111. return false
  112. end
  113. local node2 = minetest.get_node(pos)
  114. node2.param2 = (node2.param2 + 1) % 4
  115. minetest.swap_node(pos, node2)
  116. pos.y = pos.y - dir
  117. node.param2 = (node.param2 + 1) % 4
  118. minetest.swap_node(pos, node)
  119. return true
  120. end
  121. minetest.register_node(name.."_b_1", {
  122. tiles = {tb[2], tb[2], tb[2], tb[2], tb[1], tb[1].."^[transformfx"},
  123. paramtype = "light",
  124. paramtype2 = "facedir",
  125. is_ground_content = false,
  126. drop = name,
  127. drawtype = "nodebox",
  128. node_box = {
  129. type = "fixed",
  130. fixed = def.node_box_bottom
  131. },
  132. selection_box = {
  133. type = "fixed",
  134. fixed = def.selection_box_bottom
  135. },
  136. groups = def.groups,
  137. after_dig_node = function(pos, oldnode, oldmetadata, digger)
  138. pos.y = pos.y+1
  139. after_dig_node(pos, name.."_t_1", digger)
  140. end,
  141. on_rightclick = function(pos, node, clicker)
  142. if not minetest.is_protected(pos, clicker:get_player_name()) then
  143. on_rightclick(pos, 1, name.."_t_1", name.."_b_2", name.."_t_2", {1,2,3,0})
  144. end
  145. end,
  146. on_rotate = function(pos, node, user, mode, new_param2)
  147. return on_rotate(pos, node, 1, user, name.."_t_1", mode)
  148. end,
  149. sounds = def.sounds,
  150. sunlight_propagates = def.sunlight,
  151. on_blast = function() end,
  152. })
  153. minetest.register_node(name.."_t_1", {
  154. tiles = {tt[2], tt[2], tt[2], tt[2], tt[1], tt[1].."^[transformfx"},
  155. paramtype = "light",
  156. paramtype2 = "facedir",
  157. is_ground_content = false,
  158. drop = "",
  159. drawtype = "nodebox",
  160. node_box = {
  161. type = "fixed",
  162. fixed = def.node_box_top
  163. },
  164. selection_box = {
  165. type = "fixed",
  166. fixed = def.selection_box_top
  167. },
  168. groups = def.groups,
  169. after_dig_node = function(pos, oldnode, oldmetadata, digger)
  170. pos.y = pos.y-1
  171. after_dig_node(pos, name.."_b_1", digger)
  172. end,
  173. on_rightclick = function(pos, node, clicker)
  174. if not minetest.is_protected(pos, clicker:get_player_name()) then
  175. on_rightclick(pos, -1, name.."_b_1", name.."_t_2", name.."_b_2", {1,2,3,0})
  176. end
  177. end,
  178. on_rotate = function(pos, node, user, mode, new_param2)
  179. return on_rotate(pos, node, -1, user, name.."_b_1", mode)
  180. end,
  181. sounds = def.sounds,
  182. sunlight_propagates = def.sunlight,
  183. on_blast = function() end,
  184. })
  185. minetest.register_node(name.."_b_2", {
  186. tiles = {tb[2], tb[2], tb[2], tb[2], tb[1].."^[transformfx", tb[1]},
  187. paramtype = "light",
  188. paramtype2 = "facedir",
  189. is_ground_content = false,
  190. drop = name,
  191. drawtype = "nodebox",
  192. node_box = {
  193. type = "fixed",
  194. fixed = def.node_box_bottom
  195. },
  196. selection_box = {
  197. type = "fixed",
  198. fixed = def.selection_box_bottom
  199. },
  200. groups = def.groups,
  201. after_dig_node = function(pos, oldnode, oldmetadata, digger)
  202. pos.y = pos.y+1
  203. after_dig_node(pos, name.."_t_2", digger)
  204. end,
  205. on_rightclick = function(pos, node, clicker)
  206. if not minetest.is_protected(pos, clicker:get_player_name()) then
  207. on_rightclick(pos, 1, name.."_t_2", name.."_b_1", name.."_t_1", {3,0,1,2})
  208. end
  209. end,
  210. on_rotate = function(pos, node, user, mode, new_param2)
  211. return on_rotate(pos, node, 1, user, name.."_t_2", mode)
  212. end,
  213. sounds = def.sounds,
  214. sunlight_propagates = def.sunlight,
  215. on_blast = function() end,
  216. })
  217. minetest.register_node(name.."_t_2", {
  218. tiles = {tt[2], tt[2], tt[2], tt[2], tt[1].."^[transformfx", tt[1]},
  219. paramtype = "light",
  220. paramtype2 = "facedir",
  221. is_ground_content = false,
  222. drop = "",
  223. drawtype = "nodebox",
  224. node_box = {
  225. type = "fixed",
  226. fixed = def.node_box_top
  227. },
  228. selection_box = {
  229. type = "fixed",
  230. fixed = def.selection_box_top
  231. },
  232. groups = def.groups,
  233. after_dig_node = function(pos, oldnode, oldmetadata, digger)
  234. pos.y = pos.y-1
  235. after_dig_node(pos, name.."_b_2", digger)
  236. end,
  237. on_rightclick = function(pos, node, clicker)
  238. if not minetest.is_protected(pos, clicker:get_player_name()) then
  239. on_rightclick(pos, -1, name.."_b_2", name.."_t_1", name.."_b_1", {3,0,1,2})
  240. end
  241. end,
  242. on_rotate = function(pos, node, user, mode, new_param2)
  243. return on_rotate(pos, node, -1, user, name.."_b_2", mode)
  244. end,
  245. sounds = def.sounds,
  246. sunlight_propagates = def.sunlight,
  247. on_blast = function() end,
  248. })
  249. end
  250. -- Protected Wooden Door
  251. local name = "protector:door_wood"
  252. register_door(name, {
  253. description = S("Protected Wooden Door"),
  254. inventory_image = "doors_wood.png^protector_logo.png",
  255. groups = {
  256. snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
  257. unbreakable = 1, --door = 1
  258. },
  259. tiles_bottom = {"doors_wood_b.png^protector_logo.png", "doors_brown.png"},
  260. tiles_top = {"doors_wood_a.png", "doors_brown.png"},
  261. sounds = default.node_sound_wood_defaults(),
  262. sunlight = false,
  263. })
  264. minetest.register_craft({
  265. output = name,
  266. recipe = {
  267. {"group:wood", "group:wood"},
  268. {"group:wood", "default:copper_ingot"},
  269. {"group:wood", "group:wood"}
  270. }
  271. })
  272. minetest.register_craft({
  273. output = name,
  274. recipe = {
  275. {"doors:door_wood", "default:copper_ingot"}
  276. }
  277. })
  278. -- Protected Steel Door
  279. local name = "protector:door_steel"
  280. register_door(name, {
  281. description = S("Protected Steel Door"),
  282. inventory_image = "doors_steel.png^protector_logo.png",
  283. groups = {
  284. snappy = 1, bendy = 2, cracky = 1,
  285. level = 2, unbreakable = 1, -- door = 1
  286. },
  287. tiles_bottom = {"doors_steel_b.png^protector_logo.png", "doors_grey.png"},
  288. tiles_top = {"doors_steel_a.png", "doors_grey.png"},
  289. sounds = default.node_sound_wood_defaults(),
  290. sunlight = false,
  291. })
  292. minetest.register_craft({
  293. output = name,
  294. recipe = {
  295. {"default:steel_ingot", "default:steel_ingot"},
  296. {"default:steel_ingot", "default:copper_ingot"},
  297. {"default:steel_ingot", "default:steel_ingot"}
  298. }
  299. })
  300. minetest.register_craft({
  301. output = name,
  302. recipe = {
  303. {"doors:door_steel", "default:copper_ingot"}
  304. }
  305. })
  306. ----trapdoor----
  307. function register_trapdoor(name, def)
  308. local name_closed = name
  309. local name_opened = name.."_open"
  310. def.on_rightclick = function (pos, node, clicker, itemstack, pointed_thing)
  311. if minetest.is_protected(pos, clicker:get_player_name()) then
  312. return
  313. end
  314. local newname = node.name == name_closed and name_opened or name_closed
  315. local sound = false
  316. if node.name == name_closed then sound = "doors_door_open" end
  317. if node.name == name_opened then sound = "doors_door_close" end
  318. if sound then
  319. minetest.sound_play(sound, {pos = pos, gain = 0.3, max_hear_distance = 10})
  320. end
  321. minetest.swap_node(pos, {name = newname, param1 = node.param1, param2 = node.param2})
  322. end
  323. -- Common trapdoor configuration
  324. def.drawtype = "nodebox"
  325. def.paramtype = "light"
  326. def.paramtype2 = "facedir"
  327. def.is_ground_content = false
  328. local def_opened = table.copy(def)
  329. local def_closed = table.copy(def)
  330. def_closed.node_box = {
  331. type = "fixed",
  332. fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
  333. }
  334. def_closed.selection_box = {
  335. type = "fixed",
  336. fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
  337. }
  338. def_closed.tiles = { def.tile_front, def.tile_front, def.tile_side, def.tile_side,
  339. def.tile_side, def.tile_side }
  340. def_opened.node_box = {
  341. type = "fixed",
  342. fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
  343. }
  344. def_opened.selection_box = {
  345. type = "fixed",
  346. fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
  347. }
  348. def_opened.tiles = { def.tile_side, def.tile_side,
  349. def.tile_side .. '^[transform3',
  350. def.tile_side .. '^[transform1',
  351. def.tile_front, def.tile_front }
  352. def_opened.drop = name_closed
  353. def_opened.groups.not_in_creative_inventory = 1
  354. minetest.register_node(name_opened, def_opened)
  355. minetest.register_node(name_closed, def_closed)
  356. end
  357. -- Protected Wooden Trapdoor
  358. register_trapdoor("protector:trapdoor", {
  359. description = S("Protected Trapdoor"),
  360. inventory_image = "doors_trapdoor.png^protector_logo.png",
  361. wield_image = "doors_trapdoor.png^protector_logo.png",
  362. tile_front = "doors_trapdoor.png^protector_logo.png",
  363. tile_side = "doors_trapdoor_side.png",
  364. groups = {
  365. snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
  366. unbreakable = 1, --door = 1
  367. },
  368. sounds = default.node_sound_wood_defaults(),
  369. })
  370. minetest.register_craft({
  371. output = 'protector:trapdoor 2',
  372. recipe = {
  373. {'group:wood', 'default:copper_ingot', 'group:wood'},
  374. {'group:wood', 'group:wood', 'group:wood'},
  375. {'', '', ''},
  376. }
  377. })
  378. minetest.register_craft({
  379. output = "protector:trapdoor",
  380. recipe = {
  381. {"doors:trapdoor", "default:copper_ingot"}
  382. }
  383. })
  384. -- Protected Steel Trapdoor
  385. register_trapdoor("protector:trapdoor_steel", {
  386. description = S("Protected Steel Trapdoor"),
  387. inventory_image = "doors_trapdoor_steel.png^protector_logo.png",
  388. wield_image = "doors_trapdoor_steel.png^protector_logo.png",
  389. tile_front = "doors_trapdoor_steel.png^protector_logo.png",
  390. tile_side = "doors_trapdoor_steel_side.png",
  391. groups = {
  392. snappy = 1, bendy = 2, cracky = 1, melty = 2, level = 2,
  393. unbreakable = 1, --door = 1
  394. },
  395. sounds = default.node_sound_wood_defaults(),
  396. })
  397. minetest.register_craft({
  398. output = 'protector:trapdoor_steel',
  399. recipe = {
  400. {'default:copper_ingot', 'default:steel_ingot'},
  401. {'default:steel_ingot', 'default:steel_ingot'},
  402. }
  403. })
  404. minetest.register_craft({
  405. output = "protector:trapdoor_steel",
  406. recipe = {
  407. {"doors:trapdoor_steel", "default:copper_ingot"}
  408. }
  409. })
  410. -- Protected Chest
  411. minetest.register_node("protector:chest", {
  412. description = S("Protected Chest"),
  413. tiles = {
  414. "default_chest_top.png", "default_chest_top.png",
  415. "default_chest_side.png", "default_chest_side.png",
  416. "default_chest_side.png", "default_chest_front.png^protector_logo.png"
  417. },
  418. paramtype2 = "facedir",
  419. groups = {choppy = 2, oddly_breakable_by_hand = 2, unbreakable = 1},
  420. legacy_facedir_simple = true,
  421. is_ground_content = false,
  422. sounds = default.node_sound_wood_defaults(),
  423. on_construct = function(pos)
  424. local meta = minetest.get_meta(pos)
  425. local inv = meta:get_inventory()
  426. meta:set_string("infotext", S("Protected Chest"))
  427. meta:set_string("name", "")
  428. inv:set_size("main", 8 * 4)
  429. end,
  430. can_dig = function(pos,player)
  431. local meta = minetest.get_meta(pos)
  432. local inv = meta:get_inventory()
  433. if inv:is_empty("main") then
  434. if not minetest.is_protected(pos, player:get_player_name()) then
  435. return true
  436. end
  437. end
  438. end,
  439. on_metadata_inventory_put = function(pos, listname, index, stack, player)
  440. minetest.log("action", S("@1 moves stuff to protected chest at @2",
  441. player:get_player_name(), minetest.pos_to_string(pos)))
  442. end,
  443. on_metadata_inventory_take = function(pos, listname, index, stack, player)
  444. minetest.log("action", S("@1 takes stuff from protected chest at @2",
  445. player:get_player_name(), minetest.pos_to_string(pos)))
  446. end,
  447. on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  448. minetest.log("action", S("@1 moves stuff inside protected chest at @2",
  449. player:get_player_name(), minetest.pos_to_string(pos)))
  450. end,
  451. allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  452. if minetest.is_protected(pos, player:get_player_name()) then
  453. return 0
  454. end
  455. return stack:get_count()
  456. end,
  457. allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  458. if minetest.is_protected(pos, player:get_player_name()) then
  459. return 0
  460. end
  461. return stack:get_count()
  462. end,
  463. allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  464. if minetest.is_protected(pos, player:get_player_name()) then
  465. return 0
  466. end
  467. return count
  468. end,
  469. on_rightclick = function(pos, node, clicker)
  470. if minetest.is_protected(pos, clicker:get_player_name()) then
  471. return
  472. end
  473. local meta = minetest.get_meta(pos)
  474. if not meta then
  475. return
  476. end
  477. local spos = pos.x .. "," .. pos.y .. "," ..pos.z
  478. local formspec = "size[8,9]"
  479. .. default.gui_bg
  480. .. default.gui_bg_img
  481. .. default.gui_slots
  482. .. "list[nodemeta:".. spos .. ";main;0,0.3;8,4;]"
  483. .. "button[0,4.5;2,0.25;toup;" .. F(S("To Chest")) .. "]"
  484. .. "field[2.3,4.8;4,0.25;chestname;;"
  485. .. meta:get_string("name") .. "]"
  486. .. "button[6,4.5;2,0.25;todn;" .. F(S("To Inventory")) .. "]"
  487. .. "list[current_player;main;0,5;8,1;]"
  488. .. "list[current_player;main;0,6.08;8,3;8]"
  489. .. "listring[nodemeta:" .. spos .. ";main]"
  490. .. "listring[current_player;main]"
  491. minetest.show_formspec(
  492. clicker:get_player_name(),
  493. "protector:chest_" .. minetest.pos_to_string(pos),
  494. formspec)
  495. end,
  496. on_blast = function() end,
  497. })
  498. -- Protected Chest formspec buttons
  499. minetest.register_on_player_receive_fields(function(player, formname, fields)
  500. if string.sub(formname, 0, string.len("protector:chest_")) ~= "protector:chest_" then
  501. return
  502. end
  503. local pos_s = string.sub(formname,string.len("protector:chest_") + 1)
  504. local pos = minetest.string_to_pos(pos_s)
  505. if minetest.is_protected(pos, player:get_player_name()) then
  506. return
  507. end
  508. local meta = minetest.get_meta(pos) ; if not meta then return end
  509. local chest_inv = meta:get_inventory() ; if not chest_inv then return end
  510. local player_inv = player:get_inventory()
  511. local leftover
  512. if fields.toup then
  513. -- copy contents of players inventory to chest
  514. for i, v in ipairs(player_inv:get_list("main") or {}) do
  515. if chest_inv:room_for_item("main", v) then
  516. leftover = chest_inv:add_item("main", v)
  517. player_inv:remove_item("main", v)
  518. if leftover
  519. and not leftover:is_empty() then
  520. player_inv:add_item("main", v)
  521. end
  522. end
  523. end
  524. elseif fields.todn then
  525. -- copy contents of chest to players inventory
  526. for i, v in ipairs(chest_inv:get_list("main") or {}) do
  527. if player_inv:room_for_item("main", v) then
  528. leftover = player_inv:add_item("main", v)
  529. chest_inv:remove_item("main", v)
  530. if leftover
  531. and not leftover:is_empty() then
  532. chest_inv:add_item("main", v)
  533. end
  534. end
  535. end
  536. elseif fields.chestname then
  537. -- change chest infotext to display name
  538. if fields.chestname ~= "" then
  539. meta:set_string("name", fields.chestname)
  540. meta:set_string("infotext",
  541. S("Protected Chest (@1)", fields.chestname))
  542. else
  543. meta:set_string("infotext", S("Protected Chest"))
  544. end
  545. end
  546. end)
  547. -- Protected Chest recipes
  548. minetest.register_craft({
  549. output = 'protector:chest',
  550. recipe = {
  551. {'group:wood', 'group:wood', 'group:wood'},
  552. {'group:wood', 'default:copper_ingot', 'group:wood'},
  553. {'group:wood', 'group:wood', 'group:wood'},
  554. }
  555. })
  556. minetest.register_craft({
  557. output = 'protector:chest',
  558. recipe = {
  559. {'default:chest', 'default:copper_ingot', ''},
  560. }
  561. })