From d5b3a6f658db4ab12ef102fd4738cf4898e0d5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Thu, 20 Jun 2024 20:02:48 +0700 Subject: [PATCH 01/25] Clean-up ITEMS/mcl_chests code, part 1 Amongst other changes: - mcl_chests.register_chest function has been exposed. The API is still too terrible to call it quits though, I definitely want all these parameters passed to be part of a key-value table. - Added a TODO list at the top of the file. Don't worry, I'll remove it once I'm done. It's more just for my convenience than anything. --- mods/ITEMS/mcl_chests/init.lua | 208 ++++++++++++--------------------- 1 file changed, 76 insertions(+), 132 deletions(-) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index da901da32..74a31639f 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -1,3 +1,17 @@ +-- TODO +-- ==== +-- [ ] Take another full look at this code and clean-up even more. +-- [ ] Expose more functions that are currently local. +-- [ ] Split this giant 1.6k-line file into: +-- - init.lua (dofiles and LBMs) +-- - api.lua (functions) +-- - chests.lua (normal/double and trapped chests) +-- - ender.lua (ender chest registration) +-- - shulkers.lua (self-explanatory) +-- [ ] Take a new look into , maybe it can be fixed here. +-- [ ] File a Pull Request for this branch. +-- ==== + local S = minetest.get_translator(minetest.get_current_modname()) local F = minetest.formspec_escape local C = minetest.colorize @@ -8,8 +22,6 @@ local math = math local sf = string.format -local mod_doc = minetest.get_modpath("doc") - mcl_chests = {} -- Christmas chest setup @@ -32,15 +44,20 @@ if it_is_christmas then end local tiles_chest_ender_small = { "mcl_chests_ender.png" } - local ender_chest_texture = { "mcl_chests_ender.png" } + if it_is_christmas then tiles_chest_ender_small = { "mcl_chests_ender_present.png^mcl_chests_noise.png" } ender_chest_texture = { "mcl_chests_ender_present.png" } end -- Chest Entity -local animate_chests = (minetest.settings:get_bool("animated_chests") ~= false) +-- ============ +-- This is necessary to show the chest as an animated mesh, as Minetest doesn't +-- support assigning animated meshes to nodes directly. We're bypassing this +-- limitation by giving each chest its own entity, and making the chest node +-- itself fully transparent. +local animated_chests = (minetest.settings:get_bool("animated_chests") ~= false) local entity_animations = { shulker = { speed = 50, @@ -99,8 +116,7 @@ minetest.register_entity("mcl_chests:chest", { self.node_name = node_name self.sound_prefix = sound_prefix self.animation_type = animation_type - local obj = self.object - obj:set_properties({ + self.object:set_properties({ textures = textures, mesh = mesh_prefix .. (double and "_double" or "") .. ".b3d", }) @@ -171,8 +187,8 @@ local function create_entity(pos, node_name, textures, param2, double, sound_pre return luaentity end -local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type - , dir, entity_pos) +local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, + animation_type, dir, entity_pos) dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) return find_entity(entity_pos) or create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, @@ -203,14 +219,6 @@ Value: Otherwise: nil ]] local open_chests = {} ---[[local function back_is_blocked(pos, dir) - pos = vector.add(pos, dir) - local def = minetest.registered_nodes[minetest.get_node(pos).name] - pos.y = pos.y + 1 - local def2 = minetest.registered_nodes[minetest.get_node(pos).name] - return not def or def.groups.opaque == 1 or not def2 or def2.groups.opaque == 1 -end]] - -- To be called if a player opened a chest local function player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker) local name = player:get_player_name() @@ -224,10 +232,10 @@ local function player_chest_open(player, pos, node_name, textures, param2, doubl mesh = mesh, shulker = shulker } - if animate_chests then + if animated_chests then local dir = minetest.facedir_to_dir(param2) find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, shulker and "shulker" or "chest", - dir): + dir): open(name) end end @@ -292,7 +300,7 @@ local function player_chest_close(player) if open_chest == nil then return end - if animate_chests then + if animated_chests then find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, open_chest.double, open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest"):close(name) @@ -303,10 +311,9 @@ local function player_chest_close(player) end -- This is a helper function to register both chests and trapped chests. Trapped chests will make use of the additional parameters -local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tiles_table, hidden, mesecons, - on_rightclick_addendum, on_rightclick_addendum_left, on_rightclick_addendum_right, drop, - canonical_basename) - -- START OF register_chest FUNCTION BODY +function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, tiles_table, hidden, mesecons, + on_rightclick_addendum, on_rightclick_addendum_left, + on_rightclick_addendum_right, drop, canonical_basename) if not drop then drop = "mcl_chests:" .. basename else @@ -319,9 +326,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile end local function double_chest_add_item(top_inv, bottom_inv, listname, stack) - if not stack or stack:is_empty() then - return - end + if not stack or stack:is_empty() then return end local name = stack:get_name() @@ -379,7 +384,8 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile local small_name = "mcl_chests:" .. basename .. "_small" local small_textures = tiles_table.small local left_name = "mcl_chests:" .. basename .. "_left" - local left_textures = tiles_table.double + local right_name = "mcl_chests:" .. basename .. "_right" + local double_textures = tiles_table.double minetest.register_node("mcl_chests:" .. basename, { description = desc, @@ -472,26 +478,26 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile -- BEGIN OF LISTRING WORKAROUND inv:set_size("input", 1) -- END OF LISTRING WORKAROUND + + -- Combine into a double chest if neighbouring another small chest if minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "right")).name == - "mcl_chests:" .. canonical_basename .. "_small" then - minetest.swap_node(pos, { name = "mcl_chests:" .. canonical_basename .. "_right", param2 = param2 }) + small_name then + minetest.swap_node(pos, { name = right_name, param2 = param2 }) local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") - minetest.swap_node(p, { name = "mcl_chests:" .. canonical_basename .. "_left", param2 = param2 }) - create_entity(p, "mcl_chests:" .. canonical_basename .. "_left", left_textures, param2, true, - "default_chest", + minetest.swap_node(p, { name = left_name, param2 = param2 }) + create_entity(p, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") elseif minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "left")).name == - "mcl_chests:" .. canonical_basename .. "_small" then - minetest.swap_node(pos, { name = "mcl_chests:" .. canonical_basename .. "_left", param2 = param2 }) - create_entity(pos, "mcl_chests:" .. canonical_basename .. "_left", left_textures, param2, true, - "default_chest", + small_name then + minetest.swap_node(pos, { name = left_name, param2 = param2 }) + create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") - minetest.swap_node(p, { name = "mcl_chests:" .. canonical_basename .. "_right", param2 = param2 }) + minetest.swap_node(p, { name = right_name, param2 = param2 }) else - minetest.swap_node(pos, { name = "mcl_chests:" .. canonical_basename .. "_small", param2 = param2 }) - create_entity(pos, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", - "chest") + minetest.swap_node(pos, { name = small_name, param2 = param2 }) + create_entity(pos, small_name, small_textures, param2, false, "default_chest", + "mcl_chests_chest", "chest") end end, after_place_node = function(pos, placer, itemstack, pointed_thing) @@ -579,7 +585,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile }, tiles = { "blank.png^[resize:16x16" }, use_texture_alpha = "clip", - _chest_entity_textures = left_textures, + _chest_entity_textures = double_textures, _chest_entity_sound = "default_chest", _chest_entity_mesh = "mcl_chests_chest", _chest_entity_animation_type = "chest", @@ -606,7 +612,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile n.name = "mcl_chests:" .. canonical_basename .. "_small" minetest.swap_node(pos, n) end - create_entity(pos, left_name, left_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") + create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") end, after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) @@ -644,17 +650,6 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) return limit_put(stack, inv, other_inv) - --[[if inv:room_for_item("main", stack) then - return -1 - else - - if other_inv:room_for_item("main", stack) then - return -1 - else - return 0 - end - end]] - -- -- END OF LISTRING WORKAROUND else return stack:get_count() @@ -738,7 +733,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile on_rightclick_addendum_left(pos, node, clicker) end - player_chest_open(clicker, pos, left_name, left_textures, node.param2, true, "default_chest", + player_chest_open(clicker, pos, left_name, double_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, mesecons = mesecons, @@ -773,7 +768,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile end, }) - minetest.register_node("mcl_chests:" .. basename .. "_right", { + minetest.register_node(right_name, { drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", @@ -799,8 +794,8 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile local n = minetest.get_node(pos) local param2 = n.param2 local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. canonical_basename .. "_left" then - n.name = "mcl_chests:" .. canonical_basename .. "_small" + if not p or minetest.get_node(p).name ~= left_name then + n.name = small_name minetest.swap_node(pos, n) end end, @@ -817,7 +812,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile local param2 = n.param2 local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. basename .. "_left" then + if not p or minetest.get_node(p).name ~= left_name then return end close_forms(canonical_basename, p) @@ -839,15 +834,6 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) local inv = minetest.get_inventory({ type = "node", pos = pos }) - --[[if other_inv:room_for_item("main", stack) then - return -1 - else - if inv:room_for_item("main", stack) then - return -1 - else - return 0 - end - end--]] return limit_put(stack, other_inv, inv) -- END OF LISTRING WORKAROUND else @@ -931,7 +917,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile on_rightclick_addendum_right(pos, node, clicker) end - player_chest_open(clicker, pos_other, left_name, left_textures, node.param2, true, "default_chest", + player_chest_open(clicker, pos_other, left_name, double_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, mesecons = mesecons, @@ -966,17 +952,15 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile end, }) - if mod_doc then - doc.add_entry_alias("nodes", small_name, "nodes", "mcl_chests:" .. basename .. "_left") - doc.add_entry_alias("nodes", small_name, "nodes", "mcl_chests:" .. basename .. "_right") + if doc then + doc.add_entry_alias("nodes", small_name, "nodes", left_name) + doc.add_entry_alias("nodes", small_name, "nodes", right_name) end - - -- END OF register_chest FUNCTION BODY end local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") -register_chest("chest", +mcl_chests.register_chest("chest", S("Chest"), S("Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with double the capacity by placing two chests next to each other."), chestusage, @@ -987,12 +971,6 @@ register_chest("chest", inv = { "default_chest_top.png", "mcl_chests_chest_bottom.png", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "mcl_chests_chest_back.png", "default_chest_front.png" }, - --[[left = {"default_chest_top_big.png", "default_chest_top_big.png", - "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", - "default_chest_side_big.png^[transformFX", "default_chest_front_big.png"}, - right = {"default_chest_top_big.png^[transformFX", "default_chest_top_big.png^[transformFX", - "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", - "default_chest_side_big.png", "default_chest_front_big.png^[transformFX"},]] -- }, false ) @@ -1002,7 +980,7 @@ local traptiles = { double = tiles_chest_trapped_double, } -register_chest("trapped_chest", +mcl_chests.register_chest("trapped_chest", S("Trapped Chest"), S("A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped chests with double the capacity by placing two trapped chests next to each other."), chestusage, @@ -1049,7 +1027,7 @@ register_chest("trapped_chest", end ) -register_chest("trapped_chest_on", +mcl_chests.register_chest("trapped_chest_on", nil, nil, nil, nil, traptiles, true, { receptor = { @@ -1062,38 +1040,6 @@ register_chest("trapped_chest_on", "trapped_chest" ) ---[[local function close_if_trapped_chest(pos, player) - local node = minetest.get_node(pos) - - if node.name == "mcl_chests:trapped_chest_on_small" then - minetest.swap_node(pos, {name="mcl_chests:trapped_chest_small", param2 = node.param2}) - find_or_create_entity(pos, "mcl_chests:trapped_chest_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small") - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - - player_chest_close(player) - elseif node.name == "mcl_chests:trapped_chest_on_left" then - minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) - find_or_create_entity(pos, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") - minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_right", param2 = node.param2}) - mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) - - player_chest_close(player) - elseif node.name == "mcl_chests:trapped_chest_on_right" then - minetest.swap_node(pos, {name="mcl_chests:trapped_chest_right", param2 = node.param2}) - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") - minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) - find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") - mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) - - player_chest_close(player) - end -end]] - -- Disable chest when it has been closed minetest.register_on_player_receive_fields(function(player, formname, fields) if formname:find("mcl_chests:") == 1 then @@ -1288,6 +1234,8 @@ local shulker_mob_textures = { black = "mobs_mc_shulker_black.png", } local canonical_shulker_color = "violet" +local normal_canonical_name = "mcl_chests:" .. canonical_shulker_color .. "_shulker_box" +local small_canonical_name = normal_canonical_name .. "_small" --WARNING: after formspec v4 update, old shulker boxes will need to be placed again to get the new formspec local function formspec_shulker_box(name) @@ -1325,7 +1273,7 @@ for color, desc in pairs(boxtypes) do local mob_texture = shulker_mob_textures[color] local is_canonical = color == canonical_shulker_color local longdesc, usagehelp, create_entry, entry_name - if mod_doc then + if doc then if is_canonical then longdesc = S( "A shulker box is a portable container which provides 27 inventory slots for any item except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes as well as their contents can be taken as a single item. Shulker boxes come in many different colors.") @@ -1337,9 +1285,10 @@ for color, desc in pairs(boxtypes) do end end - local small_name = "mcl_chests:" .. color .. "_shulker_box_small" + local normal_name = "mcl_chests:" .. color .. "_shulker_box" + local small_name = normal_name .. "_small" - minetest.register_node("mcl_chests:" .. color .. "_shulker_box", { + minetest.register_node(normal_name, { description = desc, _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), _doc_items_create_entry = create_entry, @@ -1439,9 +1388,9 @@ for color, desc in pairs(boxtypes) do drop = "", paramtype = "light", paramtype2 = "facedir", - -- TODO: Make shulker boxes rotatable - -- This doesn't work, it just destroys the inventory: - -- on_place = minetest.rotate_node, + -- TODO: Make shulker boxes rotatable + -- This doesn't work, it just destroys the inventory: + -- on_place = minetest.rotate_node, on_construct = function(pos) local meta = minetest.get_meta(pos) meta:set_string("formspec", formspec_shulker_box(nil)) @@ -1526,23 +1475,19 @@ for color, desc in pairs(boxtypes) do end, }) - if mod_doc and not is_canonical then - doc.add_entry_alias("nodes", "mcl_chests:" .. canonical_shulker_color .. "_shulker_box", "nodes", - "mcl_chests:" .. color .. "_shulker_box") - doc.add_entry_alias("nodes", "mcl_chests:" .. canonical_shulker_color .. "_shulker_box_small", "nodes", - "mcl_chests:" .. color .. "_shulker_box_small") + if doc and not is_canonical then + doc.add_entry_alias("nodes", normal_canonical_name, "nodes", normal_name) + doc.add_entry_alias("nodes", small_canonical_name, "nodes", small_name) end minetest.register_craft({ type = "shapeless", - output = "mcl_chests:" .. color .. "_shulker_box", + output = normal_name, recipe = { "group:shulker_box", "mcl_dye:" .. color }, }) end ---- Returns false if itemstack is a shulker box ----@param itemstack ItemStack ----@return boolean +-- Returns false if itemstack is a shulker box function mcl_chests.is_not_shulker_box(stack) local g = minetest.get_item_group(stack:get_name(), "shulker_box") return g == 0 or g == nil @@ -1616,10 +1561,9 @@ minetest.register_lbm({ end }) +-- Disable active/open trapped chests when loaded because nobody could have them open at loading time. +-- Fixes redstone weirdness. minetest.register_lbm({ - -- Disable active/open trapped chests when loaded because nobody could - -- have them open at loading time. - -- Fixes redstone weirdness. label = "Disable active trapped chests", name = "mcl_chests:reset_trapped_chests", nodenames = { "mcl_chests:trapped_chest_on_small", "mcl_chests:trapped_chest_on_left", From b10bfe27cef25b75d291f93145dabdb1813832df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 21 Jun 2024 14:29:21 +0700 Subject: [PATCH 02/25] Clean-up ITEMS/mcl_chests code, part 2 Chest tile management has been reorganized to use postfixes, some slight formatting fixes applied here and there, and roughly marked down where the new files should (ideally) begin and end. --- mods/ITEMS/mcl_chests/init.lua | 240 +++++++++++++++++++++++---------- 1 file changed, 168 insertions(+), 72 deletions(-) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 74a31639f..f7186eea4 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -1,6 +1,6 @@ -- TODO -- ==== --- [ ] Take another full look at this code and clean-up even more. +-- [x] Take another full look at this code and clean-up even more. -- [ ] Expose more functions that are currently local. -- [ ] Split this giant 1.6k-line file into: -- - init.lua (dofiles and LBMs) @@ -21,35 +21,47 @@ local table = table local math = math local sf = string.format +local sm = string.match mcl_chests = {} +local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos + -- Christmas chest setup local it_is_christmas = mcl_util.is_it_christmas() -local tiles_chest_normal_small = { "mcl_chests_normal.png" } -local tiles_chest_normal_double = { "mcl_chests_normal_double.png" } +local tiles = { -- extensions will be added later + chest_normal_small = { "mcl_chests_normal" }, + chest_normal_double = { "mcl_chests_normal_double" }, + chest_trapped_small = { "mcl_chests_trapped" }, + chest_trapped_double = { "mcl_chests_trapped_double" }, + chest_ender_small = { "mcl_chests_ender" }, + ender_chest_texture = { "mcl_chests_ender" }, +} +local tiles_postfix = ".png" +local tiles_postfix_double = ".png" if it_is_christmas then - tiles_chest_normal_small = { "mcl_chests_normal_present.png^mcl_chests_noise.png" } - tiles_chest_normal_double = { "mcl_chests_normal_double_present.png^mcl_chests_noise_double.png" } + tiles_postfix = "_present.png^mcl_chests_noise.png" + tiles_postfix_double = "_present.png^mcl_chests_noise_double.png" end -local tiles_chest_trapped_small = { "mcl_chests_trapped.png" } -local tiles_chest_trapped_double = { "mcl_chests_trapped_double.png" } - -if it_is_christmas then - tiles_chest_trapped_small = { "mcl_chests_trapped_present.png^mcl_chests_noise.png" } - tiles_chest_trapped_double = { "mcl_chests_trapped_double_present.png^mcl_chests_noise_double.png" } +-- Append the postfixes for each entry +for k,v in pairs(tiles) do + if not sm(k, "double") then + tiles[k] = {v[1] .. tiles_postfix} + else + tiles[k] = {v[1] .. tiles_postfix_double} + end end -local tiles_chest_ender_small = { "mcl_chests_ender.png" } -local ender_chest_texture = { "mcl_chests_ender.png" } -if it_is_christmas then - tiles_chest_ender_small = { "mcl_chests_ender_present.png^mcl_chests_noise.png" } - ender_chest_texture = { "mcl_chests_ender_present.png" } -end + +-- ======= -- +-- api.lua -- +-- ======= -- + + -- Chest Entity -- ============ @@ -179,7 +191,7 @@ local function get_entity_info(pos, param2, double, dir, entity_pos) end local function create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, - entity_pos) + entity_pos) dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) local obj = minetest.add_entity(entity_pos, "mcl_chests:chest") local luaentity = obj:get_luaentity() @@ -196,7 +208,7 @@ local function find_or_create_entity(pos, node_name, textures, param2, double, s end local no_rotate, simple_rotate -if minetest.get_modpath("screwdriver") then +if screwdriver then no_rotate = screwdriver.disallow simple_rotate = function(pos, node, user, mode, new_param2) if screwdriver.rotate_simple(pos, node, user, mode, new_param2) ~= false then @@ -274,20 +286,20 @@ local function chest_update_after_close(pos) mesecon.receptor_off(pos, trapped_chest_mesecons_rules) elseif node.name == "mcl_chests:trapped_chest_on_left" then minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) - find_or_create_entity(pos, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, + find_or_create_entity(pos, "mcl_chests:trapped_chest_left", tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) elseif node.name == "mcl_chests:trapped_chest_on_right" then minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) - find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) end @@ -456,6 +468,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, on_construct = function(pos) local param2 = minetest.get_node(pos).param2 local meta = minetest.get_meta(pos) + --[[ This is a workaround for Minetest issue 5894 . Apparently if we don't do this, large chests initially don't work when @@ -466,8 +479,10 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, meta:set_string("workaround", "ignore_me") meta:set_string("workaround", nil) -- Done to keep metadata clean -- END OF WORKAROUND -- + local inv = meta:get_inventory() inv:set_size("main", 9 * 3) + --[[ The "input" list is *another* workaround (hahahaha!) around the fact that Minetest does not support listrings to put items into an alternative list if the first one happens to be full. See . @@ -480,19 +495,19 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, -- END OF LISTRING WORKAROUND -- Combine into a double chest if neighbouring another small chest - if minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "right")).name == + if minetest.get_node(get_double_container_neighbor_pos(pos, param2, "right")).name == small_name then minetest.swap_node(pos, { name = right_name, param2 = param2 }) - local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") + local p = get_double_container_neighbor_pos(pos, param2, "right") minetest.swap_node(p, { name = left_name, param2 = param2 }) create_entity(p, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") - elseif minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "left")).name == + elseif minetest.get_node(get_double_container_neighbor_pos(pos, param2, "left")).name == small_name then minetest.swap_node(pos, { name = left_name, param2 = param2 }) create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") - local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") + local p = get_double_container_neighbor_pos(pos, param2, "left") minetest.swap_node(p, { name = right_name, param2 = param2 }) else minetest.swap_node(pos, { name = small_name, param2 = param2 }) @@ -607,7 +622,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, on_construct = function(pos) local n = minetest.get_node(pos) local param2 = n.param2 - local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") + local p = get_double_container_neighbor_pos(pos, param2, "left") if not p or minetest.get_node(p).name ~= "mcl_chests:" .. canonical_basename .. "_right" then n.name = "mcl_chests:" .. canonical_basename .. "_small" minetest.swap_node(pos, n) @@ -626,7 +641,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, close_forms(canonical_basename, pos) local param2 = n.param2 - local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") + local p = get_double_container_neighbor_pos(pos, param2, "left") if not p or minetest.get_node(p).name ~= "mcl_chests:" .. basename .. "_right" then return end @@ -644,13 +659,13 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, if minetest.is_protected(pos, name) then minetest.record_protection_violation(pos, name) return 0 - -- BEGIN OF LISTRING WORKAROUND + -- BEGIN OF LISTRING WORKAROUND elseif listname == "input" then local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) return limit_put(stack, inv, other_inv) - -- END OF LISTRING WORKAROUND + -- END OF LISTRING WORKAROUND else return stack:get_count() end @@ -665,7 +680,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, -- BEGIN OF LISTRING WORKAROUND if listname == "input" then local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) inv:set_stack("input", 1, nil) @@ -682,21 +697,24 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, _mcl_hardness = 2.5, on_rightclick = function(pos, node, clicker) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") - local above_def = minetest.registered_nodes[minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name] + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") + local above_def = minetest.registered_nodes[ + minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name + ] local above_def_other = minetest.registered_nodes[ - minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name] + minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name + ] - if not above_def or above_def.groups.opaque == 1 or not above_def_other or above_def_other.groups.opaque == 1 then + if (not above_def or above_def.groups.opaque == 1 or not above_def_other + or above_def_other.groups.opaque == 1) then -- won't open if there is no space from the top return false end local name = minetest.get_meta(pos):get_string("name") - if name == "" then + if name == "" then -- if empty after that ^ name = minetest.get_meta(pos_other):get_string("name") - end - if name == "" then + end if name == "" then -- if STILL empty after that ^ name = S("Large Chest") end @@ -722,6 +740,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, "listring[current_player;main]", sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z), --END OF LISTRING WORKAROUND + "listring[current_player;main]" .. sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), "listring[current_player;main]", @@ -741,12 +760,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() + local stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) if stack_id ~= nil then return inv, "main", stack_id end + local node = minetest.get_node(pos) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") local meta_other = minetest.get_meta(pos_other) local inv_other = meta_other:get_inventory() stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) @@ -755,12 +776,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() + local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) if stack_id ~= nil then return inv, "main", stack_id end + local node = minetest.get_node(pos) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") local meta_other = minetest.get_meta(pos_other) local inv_other = meta_other:get_inventory() stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) @@ -793,7 +816,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, on_construct = function(pos) local n = minetest.get_node(pos) local param2 = n.param2 - local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") + local p = get_double_container_neighbor_pos(pos, param2, "right") if not p or minetest.get_node(p).name ~= left_name then n.name = small_name minetest.swap_node(pos, n) @@ -811,7 +834,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, close_forms(canonical_basename, pos) local param2 = n.param2 - local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") + local p = get_double_container_neighbor_pos(pos, param2, "right") if not p or minetest.get_node(p).name ~= left_name then return end @@ -829,13 +852,13 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, if minetest.is_protected(pos, name) then minetest.record_protection_violation(pos, name) return 0 - -- BEGIN OF LISTRING WORKAROUND + -- BEGIN OF LISTRING WORKAROUND elseif listname == "input" then - local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) local inv = minetest.get_inventory({ type = "node", pos = pos }) return limit_put(stack, other_inv, inv) - -- END OF LISTRING WORKAROUND + -- END OF LISTRING WORKAROUND else return stack:get_count() end @@ -849,7 +872,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, " moves stuff to chest at " .. minetest.pos_to_string(pos)) -- BEGIN OF LISTRING WORKAROUND if listname == "input" then - local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) local inv = minetest.get_inventory({ type = "node", pos = pos }) @@ -867,20 +890,24 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, _mcl_hardness = 2.5, on_rightclick = function(pos, node, clicker) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") - if minetest.registered_nodes[minetest.get_node(vector.offset(pos, 0, 1, 0)).name].groups.opaque == 1 - or - minetest.registered_nodes[minetest.get_node(vector.offset(pos_other, 0, 1, 0)).name].groups.opaque - == 1 then + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") + local above_def = minetest.registered_nodes[ + minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name + ] + local above_def_other = minetest.registered_nodes[ + minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name + ] + + if (not above_def or above_def.groups.opaque == 1 or not above_def_other + or above_def_other.groups.opaque == 1) then -- won't open if there is no space from the top return false end - local name = minetest.get_meta(pos_other):get_string("name") - if name == "" then - name = minetest.get_meta(pos):get_string("name") - end - if name == "" then + local name = minetest.get_meta(pos):get_string("name") + if name == "" then -- if empty after that ^ + name = minetest.get_meta(pos_other):get_string("name") + end if name == "" then -- if STILL empty after that ^ name = S("Large Chest") end @@ -906,6 +933,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, "listring[current_player;main]", sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z), --END OF LISTRING WORKAROUND + "listring[current_player;main]" .. sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z), "listring[current_player;main]", @@ -924,13 +952,15 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, on_rotate = no_rotate, _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) local node = minetest.get_node(pos) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") local meta_other = minetest.get_meta(pos_other) local inv_other = meta_other:get_inventory() + local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) if stack_id ~= nil then return inv_other, "main", stack_id end + local meta = minetest.get_meta(pos) local inv = meta:get_inventory() stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) @@ -938,13 +968,15 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end, _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) local node = minetest.get_node(pos) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") local meta_other = minetest.get_meta(pos_other) local inv_other = meta_other:get_inventory() + local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) if stack_id ~= nil then return inv_other, "main", stack_id end + local meta = minetest.get_meta(pos) local inv = meta:get_inventory() stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) @@ -958,6 +990,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end end + + +-- ========== -- +-- chests.lua -- +-- ========== -- + + + local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") mcl_chests.register_chest("chest", @@ -966,8 +1006,8 @@ mcl_chests.register_chest("chest", chestusage, S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), { - small = tiles_chest_normal_small, - double = tiles_chest_normal_double, + small = tiles.chest_normal_small, + double = tiles.chest_normal_double, inv = { "default_chest_top.png", "mcl_chests_chest_bottom.png", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "mcl_chests_chest_back.png", "default_chest_front.png" }, @@ -976,8 +1016,8 @@ mcl_chests.register_chest("chest", ) local traptiles = { - small = tiles_chest_trapped_small, - double = tiles_chest_trapped_double, + small = tiles.chest_trapped_small, + double = tiles.chest_trapped_double, } mcl_chests.register_chest("trapped_chest", @@ -1005,22 +1045,22 @@ mcl_chests.register_chest("trapped_chest", meta:set_int("players", 1) minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) - find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", tiles_chest_trapped_double, node.param2, true, + find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos, trapped_chest_mesecons_rules) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) end, function(pos, node, clicker) - local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) mesecon.receptor_on(pos, trapped_chest_mesecons_rules) minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) - find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", tiles_chest_trapped_double, node.param2, + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) @@ -1040,6 +1080,14 @@ mcl_chests.register_chest("trapped_chest_on", "trapped_chest" ) + + +-- ================= -- +-- CONTINUE init.lua -- +-- ================= -- + + + -- Disable chest when it has been closed minetest.register_on_player_receive_fields(function(player, formname, fields) if formname:find("mcl_chests:") == 1 then @@ -1053,6 +1101,14 @@ minetest.register_on_leaveplayer(function(player) player_chest_close(player) end) + + +-- =================== -- +-- CONTINUE chests.lua -- +-- =================== -- + + + minetest.register_craft({ output = "mcl_chests:chest", recipe = { @@ -1074,6 +1130,14 @@ minetest.register_craft({ burntime = 15, }) + + +-- ========= -- +-- ender.lua -- +-- ========= -- + + + minetest.register_node("mcl_chests:ender_chest", { description = S("Ender Chest"), _tt_help = S("27 interdimensional inventory slots") .. @@ -1083,7 +1147,7 @@ minetest.register_node("mcl_chests:ender_chest", { _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), drawtype = "mesh", mesh = "mcl_chests_chest.b3d", - tiles = tiles_chest_ender_small, + tiles = tiles.chest_ender_small, use_texture_alpha = "opaque", paramtype = "light", paramtype2 = "facedir", @@ -1127,7 +1191,7 @@ minetest.register_node("mcl_chests:ender_chest_small", { type = "fixed", fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, }, - _chest_entity_textures = ender_chest_texture, + _chest_entity_textures = tiles.ender_chest_texture, _chest_entity_sound = "mcl_chests_enderchest", _chest_entity_mesh = "mcl_chests_chest", _chest_entity_animation_type = "chest", @@ -1143,7 +1207,7 @@ minetest.register_node("mcl_chests:ender_chest_small", { sounds = mcl_sounds.node_sound_stone_defaults(), drop = "mcl_core:obsidian 8", on_construct = function(pos) - create_entity(pos, "mcl_chests:ender_chest_small", ender_chest_texture, minetest.get_node(pos).param2, false, + create_entity(pos, "mcl_chests:ender_chest_small", tiles.ender_chest_texture, minetest.get_node(pos).param2, false, "mcl_chests_enderchest", "mcl_chests_chest", "chest") end, on_rightclick = function(pos, node, clicker) @@ -1153,7 +1217,7 @@ minetest.register_node("mcl_chests:ender_chest_small", { end minetest.show_formspec(clicker:get_player_name(), "mcl_chests:ender_chest_" .. clicker:get_player_name(), formspec_ender_chest) - player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", ender_chest_texture, node.param2, false, + player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", tiles.ender_chest_texture, node.param2, false, "mcl_chests_enderchest", "mcl_chests_chest") end, on_receive_fields = function(pos, formname, fields, sender) @@ -1195,6 +1259,14 @@ minetest.register_craft({ }, }) + + +-- ============ -- +-- shulkers.lua -- +-- ============ -- + + + -- Shulker boxes local boxtypes = { white = S("White Shulker Box"), @@ -1487,12 +1559,28 @@ for color, desc in pairs(boxtypes) do }) end + + +-- ================ -- +-- CONTINUE api.lua -- +-- ================ -- + + + -- Returns false if itemstack is a shulker box function mcl_chests.is_not_shulker_box(stack) local g = minetest.get_item_group(stack:get_name(), "shulker_box") return g == 0 or g == nil end + + +-- ===================== -- +-- CONTINUE shulkers.lua -- +-- ===================== -- + + + minetest.register_craft({ output = "mcl_chests:violet_shulker_box", recipe = { @@ -1523,6 +1611,14 @@ minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv end end) + + +-- ================= -- +-- CONTINUE init.lua -- +-- ================= -- + + + local function select_and_spawn_entity(pos, node) local node_name = node.name local node_def = minetest.registered_nodes[node_name] From e771f0e3ff8b618edaad5787a72a1874dd4d1191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 21 Jun 2024 16:15:39 +0700 Subject: [PATCH 03/25] Split ITEMS/mcl_chests/init.lua (fixes #281) Shulker boxes can now be rotated. The TODO will now be transferred to PR. --- mods/ITEMS/mcl_chests/api.lua | 953 +++++++++++++++++ mods/ITEMS/mcl_chests/chests.lua | 108 ++ mods/ITEMS/mcl_chests/ender.lua | 128 +++ mods/ITEMS/mcl_chests/init.lua | 1593 +--------------------------- mods/ITEMS/mcl_chests/shulkers.lua | 342 ++++++ 5 files changed, 1542 insertions(+), 1582 deletions(-) create mode 100644 mods/ITEMS/mcl_chests/api.lua create mode 100644 mods/ITEMS/mcl_chests/chests.lua create mode 100644 mods/ITEMS/mcl_chests/ender.lua create mode 100644 mods/ITEMS/mcl_chests/shulkers.lua diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua new file mode 100644 index 000000000..6b602b5c7 --- /dev/null +++ b/mods/ITEMS/mcl_chests/api.lua @@ -0,0 +1,953 @@ +local S = minetest.get_translator(minetest.get_current_modname()) +local F = minetest.formspec_escape +local C = minetest.colorize + +local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos + +local string = string +local table = table +local math = math + +local sf = string.format + +-- Chest Entity +-- ============ +-- This is necessary to show the chest as an animated mesh, as Minetest doesn't +-- support assigning animated meshes to nodes directly. We're bypassing this +-- limitation by giving each chest its own entity, and making the chest node +-- itself fully transparent. +local animated_chests = (minetest.settings:get_bool("animated_chests") ~= false) +local entity_animations = { + shulker = { + speed = 50, + open = { x = 45, y = 95 }, + close = { x = 95, y = 145 }, + }, + chest = { + speed = 25, + open = { x = 0, y = 7 }, + close = { x = 13, y = 20 }, + }, +} + +minetest.register_entity("mcl_chests:chest", { + initial_properties = { + visual = "mesh", + pointable = false, + physical = false, + static_save = false, + }, + + set_animation = function(self, animname) + local anim_table = entity_animations[self.animation_type] + local anim = anim_table[animname] + if not anim then return end + self.object:set_animation(anim, anim_table.speed, 0, false) + end, + + open = function(self, playername) + self.players[playername] = true + if not self.is_open then + self:set_animation("open") + minetest.sound_play(self.sound_prefix .. "_open", { pos = self.node_pos, gain = 0.5, max_hear_distance = 16 }, + true) + self.is_open = true + end + end, + + close = function(self, playername) + local playerlist = self.players + playerlist[playername] = nil + if self.is_open then + if next(playerlist) then + return + end + self:set_animation("close") + minetest.sound_play(self.sound_prefix .. "_close", + { pos = self.node_pos, gain = 0.3, max_hear_distance = 16 }, + true) + self.is_open = false + end + end, + + initialize = function(self, node_pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) + self.node_pos = node_pos + self.node_name = node_name + self.sound_prefix = sound_prefix + self.animation_type = animation_type + self.object:set_properties({ + textures = textures, + mesh = mesh_prefix .. (double and "_double" or "") .. ".b3d", + }) + self:set_yaw(dir) + end, + + reinitialize = function(self, node_name) + self.node_name = node_name + end, + + set_yaw = function(self, dir) + self.object:set_yaw(minetest.dir_to_yaw(dir)) + end, + + check = function(self) + local node_pos, node_name = self.node_pos, self.node_name + if not node_pos or not node_name then + return false + end + local node = minetest.get_node(node_pos) + if node.name ~= node_name then + return false + end + return true + end, + + on_activate = function(self) + self.object:set_armor_groups({ immortal = 1 }) + self.players = {} + end, + + on_step = function(self, dtime) + if not self:check() then + self.object:remove() + end + end +}) + +local function get_entity_pos(pos, dir, double) + pos = vector.copy(pos) + if double then + local add, mul, vec, cross = vector.add, vector.multiply, vector.new, vector.cross + pos = add(pos, mul(cross(dir, vec(0, 1, 0)), -0.5)) + end + return pos +end + +local function find_entity(pos) + for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0)) do + local luaentity = obj:get_luaentity() + if luaentity and luaentity.name == "mcl_chests:chest" then + return luaentity + end + end +end + +local function get_entity_info(pos, param2, double, dir, entity_pos) + dir = dir or minetest.facedir_to_dir(param2) + return dir, get_entity_pos(pos, dir, double) +end + +local function create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, + entity_pos) + dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) + local obj = minetest.add_entity(entity_pos, "mcl_chests:chest") + local luaentity = obj:get_luaentity() + luaentity:initialize(pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) + return luaentity +end +mcl_chests.create_entity = create_entity + +local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, + animation_type, dir, entity_pos) + dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) + return find_entity(entity_pos) or + create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, + entity_pos) +end +mcl_chests.find_or_create_entity = find_or_create_entity + +local no_rotate, simple_rotate +if screwdriver then + no_rotate = screwdriver.disallow + simple_rotate = function(pos, node, user, mode, new_param2) + if screwdriver.rotate_simple(pos, node, user, mode, new_param2) ~= false then + local nodename = node.name + local nodedef = minetest.registered_nodes[nodename] + local dir = minetest.facedir_to_dir(new_param2) + find_or_create_entity(pos, nodename, nodedef._chest_entity_textures, new_param2, false, + nodedef._chest_entity_sound, + nodedef._chest_entity_mesh, nodedef._chest_entity_animation_type, dir):set_yaw(dir) + else + return false + end + end +end +mcl_chests.no_rotate, mcl_chests.simple_rotate = no_rotate, simple_rotate + +--[[ List of open chests. +Key: Player name +Value: + If player is using a chest: { pos = } + Otherwise: nil ]] +local open_chests = {} +mcl_chests.open_chests = open_chests + +-- To be called if a player opened a chest +local function player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker) + local name = player:get_player_name() + open_chests[name] = { + pos = pos, + node_name = node_name, + textures = textures, + param2 = param2, + double = double, + sound = sound, + mesh = mesh, + shulker = shulker + } + if animated_chests then + local dir = minetest.facedir_to_dir(param2) + find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, shulker and "shulker" or "chest", + dir): + open(name) + end +end +mcl_chests.player_chest_open = player_chest_open + +-- Simple protection checking functions +local function protection_check_move(pos, from_list, from_index, to_list, to_index, count, player) + local name = player:get_player_name() + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return 0 + else + return count + end +end +mcl_chests.protection_check_move = protection_check_move + +local function protection_check_put_take(pos, listname, index, stack, player) + local name = player:get_player_name() + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return 0 + else + return stack:get_count() + end +end +mcl_chests.protection_check_put_take = protection_check_put_take + +local trapped_chest_mesecons_rules = mesecon.rules.pplate + +-- To be called when a chest is closed (only relevant for trapped chest atm) +local function chest_update_after_close(pos) + local node = minetest.get_node(pos) + + if node.name == "mcl_chests:trapped_chest_on_small" then + minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_small", param2 = node.param2 }) + find_or_create_entity(pos, "mcl_chests:trapped_chest_small", { "mcl_chests_trapped.png" }, node.param2, false, + "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small") + mesecon.receptor_off(pos, trapped_chest_mesecons_rules) + elseif node.name == "mcl_chests:trapped_chest_on_left" then + minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) + find_or_create_entity(pos, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, + "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") + mesecon.receptor_off(pos, trapped_chest_mesecons_rules) + + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") + minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) + mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) + elseif node.name == "mcl_chests:trapped_chest_on_right" then + minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) + mesecon.receptor_off(pos, trapped_chest_mesecons_rules) + + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") + minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, + "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") + mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) + end +end +mcl_chests.chest_update_after_close = chest_update_after_close + +-- To be called if a player closed a chest +local function player_chest_close(player) + local name = player:get_player_name() + local open_chest = open_chests[name] + if open_chest == nil then + return + end + if animated_chests then + find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, + open_chest.double, + open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest"):close(name) + end + chest_update_after_close(open_chest.pos) + + open_chests[name] = nil +end +mcl_chests.player_chest_close = player_chest_close + +-- This is a helper function to register both chests and trapped chests. Trapped chests will make use of the additional parameters +function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, tiles_table, hidden, mesecons, + on_rightclick_addendum, on_rightclick_addendum_left, + on_rightclick_addendum_right, drop, canonical_basename) + if not drop then + drop = "mcl_chests:" .. basename + else + drop = "mcl_chests:" .. drop + end + -- The basename of the "canonical" version of the node, if set (e.g.: trapped_chest_on → trapped_chest). + -- Used to get a shared formspec ID and to swap the node back to the canonical version in on_construct. + if not canonical_basename then + canonical_basename = basename + end + + local function double_chest_add_item(top_inv, bottom_inv, listname, stack) + if not stack or stack:is_empty() then return end + + local name = stack:get_name() + + local function top_off(inv, stack) + for c, chest_stack in ipairs(inv:get_list(listname)) do + if stack:is_empty() then + break + end + + if chest_stack:get_name() == name and chest_stack:get_free_space() > 0 then + stack = chest_stack:add_item(stack) + inv:set_stack(listname, c, chest_stack) + end + end + + return stack + end + + stack = top_off(top_inv, stack) + stack = top_off(bottom_inv, stack) + + if not stack:is_empty() then + stack = top_inv:add_item(listname, stack) + if not stack:is_empty() then + bottom_inv:add_item(listname, stack) + end + end + end + + local drop_items_chest = mcl_util.drop_items_from_meta_container("main") + + local function on_chest_blast(pos) + local node = minetest.get_node(pos) + drop_items_chest(pos, node) + minetest.remove_node(pos) + end + + local function limit_put_list(stack, list) + for _, other in ipairs(list) do + stack = other:add_item(stack) + if stack:is_empty() then + break + end + end + return stack + end + + local function limit_put(stack, inv1, inv2) + local leftover = ItemStack(stack) + leftover = limit_put_list(leftover, inv1:get_list("main")) + leftover = limit_put_list(leftover, inv2:get_list("main")) + return stack:get_count() - leftover:get_count() + end + + local small_name = "mcl_chests:" .. basename .. "_small" + local small_textures = tiles_table.small + local left_name = "mcl_chests:" .. basename .. "_left" + local right_name = "mcl_chests:" .. basename .. "_right" + local double_textures = tiles_table.double + + minetest.register_node("mcl_chests:" .. basename, { + description = desc, + _tt_help = tt_help, + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = usagehelp, + _doc_items_hidden = hidden, + drawtype = "mesh", + mesh = "mcl_chests_chest.b3d", + tiles = small_textures, + use_texture_alpha = "opaque", + paramtype = "light", + paramtype2 = "facedir", + sounds = mcl_sounds.node_sound_wood_defaults(), + groups = { deco_block = 1 }, + on_construct = function(pos, node) + local node = minetest.get_node(pos) + node.name = small_name + minetest.set_node(pos, node) + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) + end, + }) + + local function close_forms(canonical_basename, pos) + local players = minetest.get_connected_players() + for p = 1, #players do + if vector.distance(players[p]:get_pos(), pos) <= 30 then + minetest.close_formspec(players[p]:get_player_name(), + "mcl_chests:" .. canonical_basename .. "_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z) + end + end + end + + minetest.register_node(small_name, { + description = desc, + _tt_help = tt_help, + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = usagehelp, + _doc_items_hidden = hidden, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, + }, + tiles = { "blank.png^[resize:16x16" }, + use_texture_alpha = "clip", + _chest_entity_textures = small_textures, + _chest_entity_sound = "default_chest", + _chest_entity_mesh = "mcl_chests_chest", + _chest_entity_animation_type = "chest", + paramtype = "light", + paramtype2 = "facedir", + drop = drop, + groups = { + handy = 1, + axey = 1, + container = 2, + deco_block = 1, + material_wood = 1, + flammable = -1, + chest_entity = 1, + not_in_creative_inventory = 1 + }, + is_ground_content = false, + sounds = mcl_sounds.node_sound_wood_defaults(), + on_construct = function(pos) + local param2 = minetest.get_node(pos).param2 + local meta = minetest.get_meta(pos) + + --[[ This is a workaround for Minetest issue 5894 + . + Apparently if we don't do this, large chests initially don't work when + placed at chunk borders, and some chests randomly don't work after + placing. ]] + -- FIXME: Remove this workaround when the bug has been fixed. + -- BEGIN OF WORKAROUND -- + meta:set_string("workaround", "ignore_me") + meta:set_string("workaround", nil) -- Done to keep metadata clean + -- END OF WORKAROUND -- + + local inv = meta:get_inventory() + inv:set_size("main", 9 * 3) + + --[[ The "input" list is *another* workaround (hahahaha!) around the fact that Minetest + does not support listrings to put items into an alternative list if the first one + happens to be full. See . + This list is a hidden input-only list and immediately puts items into the appropriate chest. + It is only used for listrings and hoppers. This workaround is not that bad because it only + requires a simple “inventory allows” check for large chests.]] + -- FIXME: Refactor the listrings as soon Minetest supports alternative listrings + -- BEGIN OF LISTRING WORKAROUND + inv:set_size("input", 1) + -- END OF LISTRING WORKAROUND + + -- Combine into a double chest if neighbouring another small chest + if minetest.get_node(get_double_container_neighbor_pos(pos, param2, "right")).name == + small_name then + minetest.swap_node(pos, { name = right_name, param2 = param2 }) + local p = get_double_container_neighbor_pos(pos, param2, "right") + minetest.swap_node(p, { name = left_name, param2 = param2 }) + create_entity(p, left_name, double_textures, param2, true, "default_chest", + "mcl_chests_chest", "chest") + elseif minetest.get_node(get_double_container_neighbor_pos(pos, param2, "left")).name == + small_name then + minetest.swap_node(pos, { name = left_name, param2 = param2 }) + create_entity(pos, left_name, double_textures, param2, true, "default_chest", + "mcl_chests_chest", "chest") + local p = get_double_container_neighbor_pos(pos, param2, "left") + minetest.swap_node(p, { name = right_name, param2 = param2 }) + else + minetest.swap_node(pos, { name = small_name, param2 = param2 }) + create_entity(pos, small_name, small_textures, param2, false, "default_chest", + "mcl_chests_chest", "chest") + end + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) + end, + after_dig_node = drop_items_chest, + on_blast = on_chest_blast, + allow_metadata_inventory_move = protection_check_move, + allow_metadata_inventory_take = protection_check_put_take, + allow_metadata_inventory_put = protection_check_put_take, + on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + minetest.log("action", player:get_player_name() .. + " moves stuff in chest at " .. minetest.pos_to_string(pos)) + end, + on_metadata_inventory_put = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) + -- BEGIN OF LISTRING WORKAROUND + if listname == "input" then + local inv = minetest.get_inventory({ type = "node", pos = pos }) + inv:add_item("main", stack) + end + -- END OF LISTRING WORKAROUND + end, + on_metadata_inventory_take = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " takes stuff from chest at " .. minetest.pos_to_string(pos)) + end, + _mcl_blast_resistance = 2.5, + _mcl_hardness = 2.5, + + on_rightclick = function(pos, node, clicker) + local topnode = minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }) + if topnode and topnode.name and minetest.registered_nodes[topnode.name] then + if minetest.registered_nodes[topnode.name].groups.opaque == 1 then + -- won't open if there is no space from the top + return false + end + end + local name = minetest.get_meta(pos):get_string("name") + if name == "" then + name = S("Chest") + end + + minetest.show_formspec(clicker:get_player_name(), + sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), + table.concat({ + "formspec_version[4]", + "size[11.75,10.425]", + + "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), + sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos.x, pos.y, pos.z), + "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3), + "list[current_player;main;0.375,5.1;9,3;9]", + + mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1), + "list[current_player;main;0.375,9.05;9,1;]", + sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), + "listring[current_player;main]", + }) + ) + + if on_rightclick_addendum then + on_rightclick_addendum(pos, node, clicker) + end + + player_chest_open(clicker, pos, small_name, small_textures, node.param2, false, "default_chest", + "mcl_chests_chest") + end, + + on_destruct = function(pos) + close_forms(canonical_basename, pos) + end, + mesecons = mesecons, + on_rotate = simple_rotate, + }) + + minetest.register_node(left_name, { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { -0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375 }, + }, + tiles = { "blank.png^[resize:16x16" }, + use_texture_alpha = "clip", + _chest_entity_textures = double_textures, + _chest_entity_sound = "default_chest", + _chest_entity_mesh = "mcl_chests_chest", + _chest_entity_animation_type = "chest", + paramtype = "light", + paramtype2 = "facedir", + groups = { + handy = 1, + axey = 1, + container = 2, + not_in_creative_inventory = 1, + material_wood = 1, + flammable = -1, + chest_entity = 1, + double_chest = 1 + }, + drop = drop, + is_ground_content = false, + sounds = mcl_sounds.node_sound_wood_defaults(), + on_construct = function(pos) + local n = minetest.get_node(pos) + local param2 = n.param2 + local p = get_double_container_neighbor_pos(pos, param2, "left") + if not p or minetest.get_node(p).name ~= "mcl_chests:" .. canonical_basename .. "_right" then + n.name = "mcl_chests:" .. canonical_basename .. "_small" + minetest.swap_node(pos, n) + end + create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) + end, + on_destruct = function(pos) + local n = minetest.get_node(pos) + if n.name == small_name then + return + end + + close_forms(canonical_basename, pos) + + local param2 = n.param2 + local p = get_double_container_neighbor_pos(pos, param2, "left") + if not p or minetest.get_node(p).name ~= "mcl_chests:" .. basename .. "_right" then + return + end + close_forms(canonical_basename, p) + + minetest.swap_node(p, { name = small_name, param2 = param2 }) + create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") + end, + after_dig_node = drop_items_chest, + on_blast = on_chest_blast, + allow_metadata_inventory_move = protection_check_move, + allow_metadata_inventory_take = protection_check_put_take, + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + local name = player:get_player_name() + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return 0 + -- BEGIN OF LISTRING WORKAROUND + elseif listname == "input" then + local inv = minetest.get_inventory({ type = "node", pos = pos }) + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + return limit_put(stack, inv, other_inv) + -- END OF LISTRING WORKAROUND + else + return stack:get_count() + end + end, + on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + minetest.log("action", player:get_player_name() .. + " moves stuff in chest at " .. minetest.pos_to_string(pos)) + end, + on_metadata_inventory_put = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) + -- BEGIN OF LISTRING WORKAROUND + if listname == "input" then + local inv = minetest.get_inventory({ type = "node", pos = pos }) + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + + inv:set_stack("input", 1, nil) + + double_chest_add_item(inv, other_inv, "main", stack) + end + -- END OF LISTRING WORKAROUND + end, + on_metadata_inventory_take = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " takes stuff from chest at " .. minetest.pos_to_string(pos)) + end, + _mcl_blast_resistance = 2.5, + _mcl_hardness = 2.5, + + on_rightclick = function(pos, node, clicker) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") + local above_def = minetest.registered_nodes[ + minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name + ] + local above_def_other = minetest.registered_nodes[ + minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name + ] + + if (not above_def or above_def.groups.opaque == 1 or not above_def_other + or above_def_other.groups.opaque == 1) then + -- won't open if there is no space from the top + return false + end + + local name = minetest.get_meta(pos):get_string("name") + if name == "" then -- if empty after that ^ + name = minetest.get_meta(pos_other):get_string("name") + end if name == "" then -- if STILL empty after that ^ + name = S("Large Chest") + end + + minetest.show_formspec(clicker:get_player_name(), + sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), + table.concat({ + "formspec_version[4]", + "size[11.75,14.15]", + + "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), + sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos.x, pos.y, pos.z), + mcl_formspec.get_itemslot_bg_v4(0.375, 4.5, 9, 3), + sf("list[nodemeta:%s,%s,%s;main;0.375,4.5;9,3;]", pos_other.x, pos_other.y, pos_other.z), + "label[0.375,8.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 8.825, 9, 3), + "list[current_player;main;0.375,8.825;9,3;9]", + + mcl_formspec.get_itemslot_bg_v4(0.375, 12.775, 9, 1), + "list[current_player;main;0.375,12.775;9,1;]", + + --BEGIN OF LISTRING WORKAROUND + "listring[current_player;main]", + sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z), + --END OF LISTRING WORKAROUND + + "listring[current_player;main]" .. + sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), + "listring[current_player;main]", + sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z), + }) + ) + + if on_rightclick_addendum_left then + on_rightclick_addendum_left(pos, node, clicker) + end + + player_chest_open(clicker, pos, left_name, double_textures, node.param2, true, "default_chest", + "mcl_chests_chest") + end, + mesecons = mesecons, + on_rotate = no_rotate, + _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + + local stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) + if stack_id ~= nil then + return inv, "main", stack_id + end + + local node = minetest.get_node(pos) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) + return inv_other, "main", stack_id + end, + _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + + local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) + if stack_id ~= nil then + return inv, "main", stack_id + end + + local node = minetest.get_node(pos) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) + return inv_other, "main", stack_id + end, + }) + + minetest.register_node(right_name, { + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + node_box = { + type = "fixed", + fixed = { -0.5, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, + }, + tiles = { "blank.png^[resize:16x16" }, + use_texture_alpha = "clip", + groups = { + handy = 1, + axey = 1, + container = 2, + not_in_creative_inventory = 1, + material_wood = 1, + flammable = -1, + double_chest = 2 + }, + drop = drop, + is_ground_content = false, + sounds = mcl_sounds.node_sound_wood_defaults(), + on_construct = function(pos) + local n = minetest.get_node(pos) + local param2 = n.param2 + local p = get_double_container_neighbor_pos(pos, param2, "right") + if not p or minetest.get_node(p).name ~= left_name then + n.name = small_name + minetest.swap_node(pos, n) + end + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) + end, + on_destruct = function(pos) + local n = minetest.get_node(pos) + if n.name == small_name then + return + end + + close_forms(canonical_basename, pos) + + local param2 = n.param2 + local p = get_double_container_neighbor_pos(pos, param2, "right") + if not p or minetest.get_node(p).name ~= left_name then + return + end + close_forms(canonical_basename, p) + + minetest.swap_node(p, { name = small_name, param2 = param2 }) + create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") + end, + after_dig_node = drop_items_chest, + on_blast = on_chest_blast, + allow_metadata_inventory_move = protection_check_move, + allow_metadata_inventory_take = protection_check_put_take, + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + local name = player:get_player_name() + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return 0 + -- BEGIN OF LISTRING WORKAROUND + elseif listname == "input" then + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + local inv = minetest.get_inventory({ type = "node", pos = pos }) + return limit_put(stack, other_inv, inv) + -- END OF LISTRING WORKAROUND + else + return stack:get_count() + end + end, + on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + minetest.log("action", player:get_player_name() .. + " moves stuff in chest at " .. minetest.pos_to_string(pos)) + end, + on_metadata_inventory_put = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) + -- BEGIN OF LISTRING WORKAROUND + if listname == "input" then + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + local inv = minetest.get_inventory({ type = "node", pos = pos }) + + inv:set_stack("input", 1, nil) + + double_chest_add_item(other_inv, inv, "main", stack) + end + -- END OF LISTRING WORKAROUND + end, + on_metadata_inventory_take = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " takes stuff from chest at " .. minetest.pos_to_string(pos)) + end, + _mcl_blast_resistance = 2.5, + _mcl_hardness = 2.5, + + on_rightclick = function(pos, node, clicker) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") + local above_def = minetest.registered_nodes[ + minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name + ] + local above_def_other = minetest.registered_nodes[ + minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name + ] + + if (not above_def or above_def.groups.opaque == 1 or not above_def_other + or above_def_other.groups.opaque == 1) then + -- won't open if there is no space from the top + return false + end + + local name = minetest.get_meta(pos):get_string("name") + if name == "" then -- if empty after that ^ + name = minetest.get_meta(pos_other):get_string("name") + end if name == "" then -- if STILL empty after that ^ + name = S("Large Chest") + end + + minetest.show_formspec(clicker:get_player_name(), + sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), + table.concat({ + "formspec_version[4]", + "size[11.75,14.15]", + + "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), + sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos_other.x, pos_other.y, pos_other.z), + mcl_formspec.get_itemslot_bg_v4(0.375, 4.5, 9, 3), + sf("list[nodemeta:%s,%s,%s;main;0.375,4.5;9,3;]", pos.x, pos.y, pos.z), + "label[0.375,8.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 8.825, 9, 3), + "list[current_player;main;0.375,8.825;9,3;9]", + + mcl_formspec.get_itemslot_bg_v4(0.375, 12.775, 9, 1), + "list[current_player;main;0.375,12.775;9,1;]", + + --BEGIN OF LISTRING WORKAROUND + "listring[current_player;main]", + sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z), + --END OF LISTRING WORKAROUND + + "listring[current_player;main]" .. + sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z), + "listring[current_player;main]", + sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), + }) + ) + + if on_rightclick_addendum_right then + on_rightclick_addendum_right(pos, node, clicker) + end + + player_chest_open(clicker, pos_other, left_name, double_textures, node.param2, true, "default_chest", + "mcl_chests_chest") + end, + mesecons = mesecons, + on_rotate = no_rotate, + _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) + local node = minetest.get_node(pos) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + + local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) + if stack_id ~= nil then + return inv_other, "main", stack_id + end + + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) + return inv, "main", stack_id + end, + _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) + local node = minetest.get_node(pos) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + + local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) + if stack_id ~= nil then + return inv_other, "main", stack_id + end + + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) + return inv, "main", stack_id + end, + }) + + if doc then + doc.add_entry_alias("nodes", small_name, "nodes", left_name) + doc.add_entry_alias("nodes", small_name, "nodes", right_name) + end +end + +-- Returns false if itemstack is a shulker box +function mcl_chests.is_not_shulker_box(stack) + local g = minetest.get_item_group(stack:get_name(), "shulker_box") + return g == 0 or g == nil +end diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua new file mode 100644 index 000000000..c66f336da --- /dev/null +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -0,0 +1,108 @@ +local S = minetest.get_translator(minetest.get_current_modname()) +local F = minetest.formspec_escape +local C = minetest.colorize +local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos +local trapped_chest_mesecons_rules = mesecon.rules.pplate + +local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") + +mcl_chests.register_chest("chest", + S("Chest"), + S("Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with double the capacity by placing two chests next to each other."), + chestusage, + S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), + { + small = mcl_chests.tiles.chest_normal_small, + double = mcl_chests.tiles.chest_normal_double, + inv = { "default_chest_top.png", "mcl_chests_chest_bottom.png", + "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", + "mcl_chests_chest_back.png", "default_chest_front.png" }, + }, + false +) + +local traptiles = { + small = mcl_chests.tiles.chest_trapped_small, + double = mcl_chests.tiles.chest_trapped_double, +} + +mcl_chests.register_chest("trapped_chest", + S("Trapped Chest"), + S("A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped chests with double the capacity by placing two trapped chests next to each other."), + chestusage, + S("27 inventory slots") .. + "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), + traptiles, + nil, + { + receptor = { + state = mesecon.state.off, + rules = trapped_chest_mesecons_rules, + }, + }, + function(pos, node, clicker) + minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_small", param2 = node.param2 }) + mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" }, node.param2, false, + "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_small") + mesecon.receptor_on(pos, trapped_chest_mesecons_rules) + end, + function(pos, node, clicker) + local meta = minetest.get_meta(pos) + meta:set_int("players", 1) + + minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) + mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, + "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") + mesecon.receptor_on(pos, trapped_chest_mesecons_rules) + + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") + minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) + mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) + end, + function(pos, node, clicker) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") + + minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) + mesecon.receptor_on(pos, trapped_chest_mesecons_rules) + + minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) + mcl_chests.find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", mcl_chests.tiles.chest_trapped_double, node.param2, + true, + "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") + mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) + end +) + +mcl_chests.register_chest("trapped_chest_on", + nil, nil, nil, nil, traptiles, true, + { + receptor = { + state = mesecon.state.on, + rules = trapped_chest_mesecons_rules, + }, + }, + nil, nil, nil, + "trapped_chest", + "trapped_chest" +) + +minetest.register_craft({ + output = "mcl_chests:chest", + recipe = { + { "group:wood", "group:wood", "group:wood" }, + { "group:wood", "", "group:wood" }, + { "group:wood", "group:wood", "group:wood" }, + }, +}) + +minetest.register_craft({ + type = "fuel", + recipe = "mcl_chests:chest", + burntime = 15, +}) + +minetest.register_craft({ + type = "fuel", + recipe = "mcl_chests:trapped_chest", + burntime = 15, +}) diff --git a/mods/ITEMS/mcl_chests/ender.lua b/mods/ITEMS/mcl_chests/ender.lua new file mode 100644 index 000000000..c126563ca --- /dev/null +++ b/mods/ITEMS/mcl_chests/ender.lua @@ -0,0 +1,128 @@ +local S = minetest.get_translator(minetest.get_current_modname()) +local F = minetest.formspec_escape +local C = minetest.colorize + +local longdesc = S([[ + Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This + inventory is the same no matter from which ender chest you access it from. If you put one item into one + ender chest, you will find it in all other ender chests. Each player will only see their own items, but + not the items of other players. +]]) + +minetest.register_node("mcl_chests:ender_chest", { + description = S("Ender Chest"), + _tt_help = S("27 interdimensional inventory slots") .. + "\n" .. S("Put items inside, retrieve them from any ender chest"), + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), + drawtype = "mesh", + mesh = "mcl_chests_chest.b3d", + tiles = mcl_chests.tiles.chest_ender_small, + use_texture_alpha = "opaque", + paramtype = "light", + paramtype2 = "facedir", + groups = { deco_block = 1 }, + sounds = mcl_sounds.node_sound_stone_defaults(), + on_construct = function(pos) + local node = minetest.get_node(pos) + node.name = "mcl_chests:ender_chest_small" + minetest.set_node(pos, node) + end, +}) + +local formspec_ender_chest = table.concat({ + "formspec_version[4]", + "size[11.75,10.425]", + + "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Ender Chest"))) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), + "list[current_player;enderchest;0.375,0.75;9,3;]", + "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3), + "list[current_player;main;0.375,5.1;9,3;9]", + + mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1), + "list[current_player;main;0.375,9.05;9,1;]", + + "listring[current_player;enderchest]", + "listring[current_player;main]", +}) + +minetest.register_node("mcl_chests:ender_chest_small", { + description = S("Ender Chest"), + _tt_help = S("27 interdimensional inventory slots") .. + "\n" .. S("Put items inside, retrieve them from any ender chest"), + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, + }, + _chest_entity_textures = mcl_chests.tiles.ender_chest_texture, + _chest_entity_sound = "mcl_chests_enderchest", + _chest_entity_mesh = "mcl_chests_chest", + _chest_entity_animation_type = "chest", + tiles = { "blank.png^[resize:16x16" }, + use_texture_alpha = "clip", + -- Note: The “container” group is missing here because the ender chest does not + -- have an inventory on its own + groups = { pickaxey = 1, deco_block = 1, material_stone = 1, chest_entity = 1, not_in_creative_inventory = 1 }, + is_ground_content = false, + paramtype = "light", + light_source = 7, + paramtype2 = "facedir", + sounds = mcl_sounds.node_sound_stone_defaults(), + drop = "mcl_core:obsidian 8", + on_construct = function(pos) + mcl_chests.create_entity(pos, "mcl_chests:ender_chest_small", mcl_chests.tiles.ender_chest_texture, + minetest.get_node(pos).param2, false, "mcl_chests_enderchest", "mcl_chests_chest", "chest") + end, + on_rightclick = function(pos, node, clicker) + if minetest.registered_nodes[minetest.get_node(vector.offset(pos, 0, 1, 0)).name].groups.opaque == 1 then + -- won't open if there is no space from the top + return false + end + minetest.show_formspec(clicker:get_player_name(), "mcl_chests:ender_chest_" .. clicker:get_player_name(), + formspec_ender_chest) + mcl_chests.player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", + mcl_chests.tiles.ender_chest_texture, node.param2, false, "mcl_chests_enderchest", + "mcl_chests_chest") + end, + on_receive_fields = function(pos, formname, fields, sender) + if fields.quit then + mcl_chests.player_chest_close(sender) + end + end, + _mcl_blast_resistance = 3000, + _mcl_hardness = 22.5, + _mcl_silk_touch_drop = { "mcl_chests:ender_chest" }, + on_rotate = simple_rotate, +}) + +minetest.register_on_joinplayer(function(player) + local inv = player:get_inventory() + inv:set_size("enderchest", 9 * 3) +end) + +minetest.register_allow_player_inventory_action(function(player, action, inv, info) + if inv:get_location().type == "player" and ( + action == "move" and (info.from_list == "enderchest" or info.to_list == "enderchest") + or action == "put" and info.listname == "enderchest" + or action == "take" and info.listname == "enderchest") then + local def = player:get_wielded_item():get_definition() + local range = (def and def.range or player:get_inventory():get_stack("hand", 1):get_definition().range) + 1 + if not minetest.find_node_near(player:get_pos(), range, "mcl_chests:ender_chest_small", true) then + return 0 + end + end +end) + +minetest.register_craft({ + output = "mcl_chests:ender_chest", + recipe = { + { "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" }, + { "mcl_core:obsidian", "mcl_end:ender_eye", "mcl_core:obsidian" }, + { "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" }, + }, +}) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index f7186eea4..3ca1cf4da 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -1,32 +1,11 @@ --- TODO --- ==== --- [x] Take another full look at this code and clean-up even more. --- [ ] Expose more functions that are currently local. --- [ ] Split this giant 1.6k-line file into: --- - init.lua (dofiles and LBMs) --- - api.lua (functions) --- - chests.lua (normal/double and trapped chests) --- - ender.lua (ender chest registration) --- - shulkers.lua (self-explanatory) --- [ ] Take a new look into , maybe it can be fixed here. --- [ ] File a Pull Request for this branch. --- ==== - -local S = minetest.get_translator(minetest.get_current_modname()) -local F = minetest.formspec_escape -local C = minetest.colorize - local string = string local table = table local math = math -local sf = string.format local sm = string.match mcl_chests = {} -local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos - -- Christmas chest setup local it_is_christmas = mcl_util.is_it_christmas() @@ -55,1036 +34,13 @@ for k,v in pairs(tiles) do end end +mcl_chests.tiles = tiles - --- ======= -- --- api.lua -- --- ======= -- - - - --- Chest Entity --- ============ --- This is necessary to show the chest as an animated mesh, as Minetest doesn't --- support assigning animated meshes to nodes directly. We're bypassing this --- limitation by giving each chest its own entity, and making the chest node --- itself fully transparent. -local animated_chests = (minetest.settings:get_bool("animated_chests") ~= false) -local entity_animations = { - shulker = { - speed = 50, - open = { x = 45, y = 95 }, - close = { x = 95, y = 145 }, - }, - chest = { - speed = 25, - open = { x = 0, y = 7 }, - close = { x = 13, y = 20 }, - }, -} - -minetest.register_entity("mcl_chests:chest", { - initial_properties = { - visual = "mesh", - pointable = false, - physical = false, - static_save = false, - }, - - set_animation = function(self, animname) - local anim_table = entity_animations[self.animation_type] - local anim = anim_table[animname] - if not anim then return end - self.object:set_animation(anim, anim_table.speed, 0, false) - end, - - open = function(self, playername) - self.players[playername] = true - if not self.is_open then - self:set_animation("open") - minetest.sound_play(self.sound_prefix .. "_open", { pos = self.node_pos, gain = 0.5, max_hear_distance = 16 }, - true) - self.is_open = true - end - end, - - close = function(self, playername) - local playerlist = self.players - playerlist[playername] = nil - if self.is_open then - if next(playerlist) then - return - end - self:set_animation("close") - minetest.sound_play(self.sound_prefix .. "_close", - { pos = self.node_pos, gain = 0.3, max_hear_distance = 16 }, - true) - self.is_open = false - end - end, - - initialize = function(self, node_pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) - self.node_pos = node_pos - self.node_name = node_name - self.sound_prefix = sound_prefix - self.animation_type = animation_type - self.object:set_properties({ - textures = textures, - mesh = mesh_prefix .. (double and "_double" or "") .. ".b3d", - }) - self:set_yaw(dir) - end, - - reinitialize = function(self, node_name) - self.node_name = node_name - end, - - set_yaw = function(self, dir) - self.object:set_yaw(minetest.dir_to_yaw(dir)) - end, - - check = function(self) - local node_pos, node_name = self.node_pos, self.node_name - if not node_pos or not node_name then - return false - end - local node = minetest.get_node(node_pos) - if node.name ~= node_name then - return false - end - return true - end, - - on_activate = function(self) - self.object:set_armor_groups({ immortal = 1 }) - self.players = {} - end, - - on_step = function(self, dtime) - if not self:check() then - self.object:remove() - end - end -}) - -local function get_entity_pos(pos, dir, double) - pos = vector.copy(pos) - if double then - local add, mul, vec, cross = vector.add, vector.multiply, vector.new, vector.cross - pos = add(pos, mul(cross(dir, vec(0, 1, 0)), -0.5)) - end - return pos -end - -local function find_entity(pos) - for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0)) do - local luaentity = obj:get_luaentity() - if luaentity and luaentity.name == "mcl_chests:chest" then - return luaentity - end - end -end - -local function get_entity_info(pos, param2, double, dir, entity_pos) - dir = dir or minetest.facedir_to_dir(param2) - return dir, get_entity_pos(pos, dir, double) -end - -local function create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, - entity_pos) - dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) - local obj = minetest.add_entity(entity_pos, "mcl_chests:chest") - local luaentity = obj:get_luaentity() - luaentity:initialize(pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) - return luaentity -end - -local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, - animation_type, dir, entity_pos) - dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) - return find_entity(entity_pos) or - create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, - entity_pos) -end - -local no_rotate, simple_rotate -if screwdriver then - no_rotate = screwdriver.disallow - simple_rotate = function(pos, node, user, mode, new_param2) - if screwdriver.rotate_simple(pos, node, user, mode, new_param2) ~= false then - local nodename = node.name - local nodedef = minetest.registered_nodes[nodename] - local dir = minetest.facedir_to_dir(new_param2) - find_or_create_entity(pos, nodename, nodedef._chest_entity_textures, new_param2, false, - nodedef._chest_entity_sound, - nodedef._chest_entity_mesh, nodedef._chest_entity_animation_type, dir):set_yaw(dir) - else - return false - end - end -end - ---[[ List of open chests. -Key: Player name -Value: - If player is using a chest: { pos = } - Otherwise: nil ]] -local open_chests = {} - --- To be called if a player opened a chest -local function player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker) - local name = player:get_player_name() - open_chests[name] = { - pos = pos, - node_name = node_name, - textures = textures, - param2 = param2, - double = double, - sound = sound, - mesh = mesh, - shulker = shulker - } - if animated_chests then - local dir = minetest.facedir_to_dir(param2) - find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, shulker and "shulker" or "chest", - dir): - open(name) - end -end - --- Simple protection checking functions -local function protection_check_move(pos, from_list, from_index, to_list, to_index, count, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - else - return count - end -end - -local function protection_check_put_take(pos, listname, index, stack, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - else - return stack:get_count() - end -end - -local trapped_chest_mesecons_rules = mesecon.rules.pplate - --- To be called when a chest is closed (only relevant for trapped chest atm) -local function chest_update_after_close(pos) - local node = minetest.get_node(pos) - - if node.name == "mcl_chests:trapped_chest_on_small" then - minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_small", param2 = node.param2 }) - find_or_create_entity(pos, "mcl_chests:trapped_chest_small", { "mcl_chests_trapped.png" }, node.param2, false, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small") - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - elseif node.name == "mcl_chests:trapped_chest_on_left" then - minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) - find_or_create_entity(pos, "mcl_chests:trapped_chest_left", tiles.chest_trapped_double, node.param2, true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) - mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) - elseif node.name == "mcl_chests:trapped_chest_on_right" then - minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) - - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) - find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", tiles.chest_trapped_double, node.param2, true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") - mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) - end -end - --- To be called if a player closed a chest -local function player_chest_close(player) - local name = player:get_player_name() - local open_chest = open_chests[name] - if open_chest == nil then - return - end - if animated_chests then - find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, - open_chest.double, - open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest"):close(name) - end - chest_update_after_close(open_chest.pos) - - open_chests[name] = nil -end - --- This is a helper function to register both chests and trapped chests. Trapped chests will make use of the additional parameters -function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, tiles_table, hidden, mesecons, - on_rightclick_addendum, on_rightclick_addendum_left, - on_rightclick_addendum_right, drop, canonical_basename) - if not drop then - drop = "mcl_chests:" .. basename - else - drop = "mcl_chests:" .. drop - end - -- The basename of the "canonical" version of the node, if set (e.g.: trapped_chest_on → trapped_chest). - -- Used to get a shared formspec ID and to swap the node back to the canonical version in on_construct. - if not canonical_basename then - canonical_basename = basename - end - - local function double_chest_add_item(top_inv, bottom_inv, listname, stack) - if not stack or stack:is_empty() then return end - - local name = stack:get_name() - - local function top_off(inv, stack) - for c, chest_stack in ipairs(inv:get_list(listname)) do - if stack:is_empty() then - break - end - - if chest_stack:get_name() == name and chest_stack:get_free_space() > 0 then - stack = chest_stack:add_item(stack) - inv:set_stack(listname, c, chest_stack) - end - end - - return stack - end - - stack = top_off(top_inv, stack) - stack = top_off(bottom_inv, stack) - - if not stack:is_empty() then - stack = top_inv:add_item(listname, stack) - if not stack:is_empty() then - bottom_inv:add_item(listname, stack) - end - end - end - - local drop_items_chest = mcl_util.drop_items_from_meta_container("main") - - local function on_chest_blast(pos) - local node = minetest.get_node(pos) - drop_items_chest(pos, node) - minetest.remove_node(pos) - end - - local function limit_put_list(stack, list) - for _, other in ipairs(list) do - stack = other:add_item(stack) - if stack:is_empty() then - break - end - end - return stack - end - - local function limit_put(stack, inv1, inv2) - local leftover = ItemStack(stack) - leftover = limit_put_list(leftover, inv1:get_list("main")) - leftover = limit_put_list(leftover, inv2:get_list("main")) - return stack:get_count() - leftover:get_count() - end - - local small_name = "mcl_chests:" .. basename .. "_small" - local small_textures = tiles_table.small - local left_name = "mcl_chests:" .. basename .. "_left" - local right_name = "mcl_chests:" .. basename .. "_right" - local double_textures = tiles_table.double - - minetest.register_node("mcl_chests:" .. basename, { - description = desc, - _tt_help = tt_help, - _doc_items_longdesc = longdesc, - _doc_items_usagehelp = usagehelp, - _doc_items_hidden = hidden, - drawtype = "mesh", - mesh = "mcl_chests_chest.b3d", - tiles = small_textures, - use_texture_alpha = "opaque", - paramtype = "light", - paramtype2 = "facedir", - sounds = mcl_sounds.node_sound_wood_defaults(), - groups = { deco_block = 1 }, - on_construct = function(pos, node) - local node = minetest.get_node(pos) - node.name = small_name - minetest.set_node(pos, node) - end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) - end, - }) - - local function close_forms(canonical_basename, pos) - local players = minetest.get_connected_players() - for p = 1, #players do - if vector.distance(players[p]:get_pos(), pos) <= 30 then - minetest.close_formspec(players[p]:get_player_name(), - "mcl_chests:" .. canonical_basename .. "_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z) - end - end - end - - minetest.register_node(small_name, { - description = desc, - _tt_help = tt_help, - _doc_items_longdesc = longdesc, - _doc_items_usagehelp = usagehelp, - _doc_items_hidden = hidden, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, - }, - tiles = { "blank.png^[resize:16x16" }, - use_texture_alpha = "clip", - _chest_entity_textures = small_textures, - _chest_entity_sound = "default_chest", - _chest_entity_mesh = "mcl_chests_chest", - _chest_entity_animation_type = "chest", - paramtype = "light", - paramtype2 = "facedir", - drop = drop, - groups = { - handy = 1, - axey = 1, - container = 2, - deco_block = 1, - material_wood = 1, - flammable = -1, - chest_entity = 1, - not_in_creative_inventory = 1 - }, - is_ground_content = false, - sounds = mcl_sounds.node_sound_wood_defaults(), - on_construct = function(pos) - local param2 = minetest.get_node(pos).param2 - local meta = minetest.get_meta(pos) - - --[[ This is a workaround for Minetest issue 5894 - . - Apparently if we don't do this, large chests initially don't work when - placed at chunk borders, and some chests randomly don't work after - placing. ]] - -- FIXME: Remove this workaround when the bug has been fixed. - -- BEGIN OF WORKAROUND -- - meta:set_string("workaround", "ignore_me") - meta:set_string("workaround", nil) -- Done to keep metadata clean - -- END OF WORKAROUND -- - - local inv = meta:get_inventory() - inv:set_size("main", 9 * 3) - - --[[ The "input" list is *another* workaround (hahahaha!) around the fact that Minetest - does not support listrings to put items into an alternative list if the first one - happens to be full. See . - This list is a hidden input-only list and immediately puts items into the appropriate chest. - It is only used for listrings and hoppers. This workaround is not that bad because it only - requires a simple “inventory allows” check for large chests.]] - -- FIXME: Refactor the listrings as soon Minetest supports alternative listrings - -- BEGIN OF LISTRING WORKAROUND - inv:set_size("input", 1) - -- END OF LISTRING WORKAROUND - - -- Combine into a double chest if neighbouring another small chest - if minetest.get_node(get_double_container_neighbor_pos(pos, param2, "right")).name == - small_name then - minetest.swap_node(pos, { name = right_name, param2 = param2 }) - local p = get_double_container_neighbor_pos(pos, param2, "right") - minetest.swap_node(p, { name = left_name, param2 = param2 }) - create_entity(p, left_name, double_textures, param2, true, "default_chest", - "mcl_chests_chest", "chest") - elseif minetest.get_node(get_double_container_neighbor_pos(pos, param2, "left")).name == - small_name then - minetest.swap_node(pos, { name = left_name, param2 = param2 }) - create_entity(pos, left_name, double_textures, param2, true, "default_chest", - "mcl_chests_chest", "chest") - local p = get_double_container_neighbor_pos(pos, param2, "left") - minetest.swap_node(p, { name = right_name, param2 = param2 }) - else - minetest.swap_node(pos, { name = small_name, param2 = param2 }) - create_entity(pos, small_name, small_textures, param2, false, "default_chest", - "mcl_chests_chest", "chest") - end - end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) - end, - after_dig_node = drop_items_chest, - on_blast = on_chest_blast, - allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = protection_check_put_take, - on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - minetest.log("action", player:get_player_name() .. - " moves stuff in chest at " .. minetest.pos_to_string(pos)) - end, - on_metadata_inventory_put = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " moves stuff to chest at " .. minetest.pos_to_string(pos)) - -- BEGIN OF LISTRING WORKAROUND - if listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - inv:add_item("main", stack) - end - -- END OF LISTRING WORKAROUND - end, - on_metadata_inventory_take = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " takes stuff from chest at " .. minetest.pos_to_string(pos)) - end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 2.5, - - on_rightclick = function(pos, node, clicker) - local topnode = minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }) - if topnode and topnode.name and minetest.registered_nodes[topnode.name] then - if minetest.registered_nodes[topnode.name].groups.opaque == 1 then - -- won't open if there is no space from the top - return false - end - end - local name = minetest.get_meta(pos):get_string("name") - if name == "" then - name = S("Chest") - end - - minetest.show_formspec(clicker:get_player_name(), - sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), - table.concat({ - "formspec_version[4]", - "size[11.75,10.425]", - - "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), - sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos.x, pos.y, pos.z), - "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3), - "list[current_player;main;0.375,5.1;9,3;9]", - - mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1), - "list[current_player;main;0.375,9.05;9,1;]", - sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), - "listring[current_player;main]", - }) - ) - - if on_rightclick_addendum then - on_rightclick_addendum(pos, node, clicker) - end - - player_chest_open(clicker, pos, small_name, small_textures, node.param2, false, "default_chest", - "mcl_chests_chest") - end, - - on_destruct = function(pos) - close_forms(canonical_basename, pos) - end, - mesecons = mesecons, - on_rotate = simple_rotate, - }) - - minetest.register_node(left_name, { - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { -0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375 }, - }, - tiles = { "blank.png^[resize:16x16" }, - use_texture_alpha = "clip", - _chest_entity_textures = double_textures, - _chest_entity_sound = "default_chest", - _chest_entity_mesh = "mcl_chests_chest", - _chest_entity_animation_type = "chest", - paramtype = "light", - paramtype2 = "facedir", - groups = { - handy = 1, - axey = 1, - container = 2, - not_in_creative_inventory = 1, - material_wood = 1, - flammable = -1, - chest_entity = 1, - double_chest = 1 - }, - drop = drop, - is_ground_content = false, - sounds = mcl_sounds.node_sound_wood_defaults(), - on_construct = function(pos) - local n = minetest.get_node(pos) - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "left") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. canonical_basename .. "_right" then - n.name = "mcl_chests:" .. canonical_basename .. "_small" - minetest.swap_node(pos, n) - end - create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") - end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) - end, - on_destruct = function(pos) - local n = minetest.get_node(pos) - if n.name == small_name then - return - end - - close_forms(canonical_basename, pos) - - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "left") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. basename .. "_right" then - return - end - close_forms(canonical_basename, p) - - minetest.swap_node(p, { name = small_name, param2 = param2 }) - create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") - end, - after_dig_node = drop_items_chest, - on_blast = on_chest_blast, - allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - -- BEGIN OF LISTRING WORKAROUND - elseif listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - return limit_put(stack, inv, other_inv) - -- END OF LISTRING WORKAROUND - else - return stack:get_count() - end - end, - on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - minetest.log("action", player:get_player_name() .. - " moves stuff in chest at " .. minetest.pos_to_string(pos)) - end, - on_metadata_inventory_put = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " moves stuff to chest at " .. minetest.pos_to_string(pos)) - -- BEGIN OF LISTRING WORKAROUND - if listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - - inv:set_stack("input", 1, nil) - - double_chest_add_item(inv, other_inv, "main", stack) - end - -- END OF LISTRING WORKAROUND - end, - on_metadata_inventory_take = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " takes stuff from chest at " .. minetest.pos_to_string(pos)) - end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 2.5, - - on_rightclick = function(pos, node, clicker) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - local above_def = minetest.registered_nodes[ - minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name - ] - local above_def_other = minetest.registered_nodes[ - minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name - ] - - if (not above_def or above_def.groups.opaque == 1 or not above_def_other - or above_def_other.groups.opaque == 1) then - -- won't open if there is no space from the top - return false - end - - local name = minetest.get_meta(pos):get_string("name") - if name == "" then -- if empty after that ^ - name = minetest.get_meta(pos_other):get_string("name") - end if name == "" then -- if STILL empty after that ^ - name = S("Large Chest") - end - - minetest.show_formspec(clicker:get_player_name(), - sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), - table.concat({ - "formspec_version[4]", - "size[11.75,14.15]", - - "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), - sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos.x, pos.y, pos.z), - mcl_formspec.get_itemslot_bg_v4(0.375, 4.5, 9, 3), - sf("list[nodemeta:%s,%s,%s;main;0.375,4.5;9,3;]", pos_other.x, pos_other.y, pos_other.z), - "label[0.375,8.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 8.825, 9, 3), - "list[current_player;main;0.375,8.825;9,3;9]", - - mcl_formspec.get_itemslot_bg_v4(0.375, 12.775, 9, 1), - "list[current_player;main;0.375,12.775;9,1;]", - - --BEGIN OF LISTRING WORKAROUND - "listring[current_player;main]", - sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z), - --END OF LISTRING WORKAROUND - - "listring[current_player;main]" .. - sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), - "listring[current_player;main]", - sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z), - }) - ) - - if on_rightclick_addendum_left then - on_rightclick_addendum_left(pos, node, clicker) - end - - player_chest_open(clicker, pos, left_name, double_textures, node.param2, true, "default_chest", - "mcl_chests_chest") - end, - mesecons = mesecons, - on_rotate = no_rotate, - _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - - local stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) - if stack_id ~= nil then - return inv, "main", stack_id - end - - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) - return inv_other, "main", stack_id - end, - _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - - local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) - if stack_id ~= nil then - return inv, "main", stack_id - end - - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) - return inv_other, "main", stack_id - end, - }) - - minetest.register_node(right_name, { - drawtype = "nodebox", - paramtype = "light", - paramtype2 = "facedir", - node_box = { - type = "fixed", - fixed = { -0.5, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, - }, - tiles = { "blank.png^[resize:16x16" }, - use_texture_alpha = "clip", - groups = { - handy = 1, - axey = 1, - container = 2, - not_in_creative_inventory = 1, - material_wood = 1, - flammable = -1, - double_chest = 2 - }, - drop = drop, - is_ground_content = false, - sounds = mcl_sounds.node_sound_wood_defaults(), - on_construct = function(pos) - local n = minetest.get_node(pos) - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "right") - if not p or minetest.get_node(p).name ~= left_name then - n.name = small_name - minetest.swap_node(pos, n) - end - end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) - end, - on_destruct = function(pos) - local n = minetest.get_node(pos) - if n.name == small_name then - return - end - - close_forms(canonical_basename, pos) - - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "right") - if not p or minetest.get_node(p).name ~= left_name then - return - end - close_forms(canonical_basename, p) - - minetest.swap_node(p, { name = small_name, param2 = param2 }) - create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") - end, - after_dig_node = drop_items_chest, - on_blast = on_chest_blast, - allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - -- BEGIN OF LISTRING WORKAROUND - elseif listname == "input" then - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - local inv = minetest.get_inventory({ type = "node", pos = pos }) - return limit_put(stack, other_inv, inv) - -- END OF LISTRING WORKAROUND - else - return stack:get_count() - end - end, - on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - minetest.log("action", player:get_player_name() .. - " moves stuff in chest at " .. minetest.pos_to_string(pos)) - end, - on_metadata_inventory_put = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " moves stuff to chest at " .. minetest.pos_to_string(pos)) - -- BEGIN OF LISTRING WORKAROUND - if listname == "input" then - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - local inv = minetest.get_inventory({ type = "node", pos = pos }) - - inv:set_stack("input", 1, nil) - - double_chest_add_item(other_inv, inv, "main", stack) - end - -- END OF LISTRING WORKAROUND - end, - on_metadata_inventory_take = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " takes stuff from chest at " .. minetest.pos_to_string(pos)) - end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 2.5, - - on_rightclick = function(pos, node, clicker) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - local above_def = minetest.registered_nodes[ - minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name - ] - local above_def_other = minetest.registered_nodes[ - minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name - ] - - if (not above_def or above_def.groups.opaque == 1 or not above_def_other - or above_def_other.groups.opaque == 1) then - -- won't open if there is no space from the top - return false - end - - local name = minetest.get_meta(pos):get_string("name") - if name == "" then -- if empty after that ^ - name = minetest.get_meta(pos_other):get_string("name") - end if name == "" then -- if STILL empty after that ^ - name = S("Large Chest") - end - - minetest.show_formspec(clicker:get_player_name(), - sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), - table.concat({ - "formspec_version[4]", - "size[11.75,14.15]", - - "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), - sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos_other.x, pos_other.y, pos_other.z), - mcl_formspec.get_itemslot_bg_v4(0.375, 4.5, 9, 3), - sf("list[nodemeta:%s,%s,%s;main;0.375,4.5;9,3;]", pos.x, pos.y, pos.z), - "label[0.375,8.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 8.825, 9, 3), - "list[current_player;main;0.375,8.825;9,3;9]", - - mcl_formspec.get_itemslot_bg_v4(0.375, 12.775, 9, 1), - "list[current_player;main;0.375,12.775;9,1;]", - - --BEGIN OF LISTRING WORKAROUND - "listring[current_player;main]", - sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z), - --END OF LISTRING WORKAROUND - - "listring[current_player;main]" .. - sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z), - "listring[current_player;main]", - sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z), - }) - ) - - if on_rightclick_addendum_right then - on_rightclick_addendum_right(pos, node, clicker) - end - - player_chest_open(clicker, pos_other, left_name, double_textures, node.param2, true, "default_chest", - "mcl_chests_chest") - end, - mesecons = mesecons, - on_rotate = no_rotate, - _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - - local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) - if stack_id ~= nil then - return inv_other, "main", stack_id - end - - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) - return inv, "main", stack_id - end, - _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - - local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) - if stack_id ~= nil then - return inv_other, "main", stack_id - end - - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) - return inv, "main", stack_id - end, - }) - - if doc then - doc.add_entry_alias("nodes", small_name, "nodes", left_name) - doc.add_entry_alias("nodes", small_name, "nodes", right_name) - end -end - - - --- ========== -- --- chests.lua -- --- ========== -- - - - -local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") - -mcl_chests.register_chest("chest", - S("Chest"), - S("Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with double the capacity by placing two chests next to each other."), - chestusage, - S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), - { - small = tiles.chest_normal_small, - double = tiles.chest_normal_double, - inv = { "default_chest_top.png", "mcl_chests_chest_bottom.png", - "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", - "mcl_chests_chest_back.png", "default_chest_front.png" }, - }, - false -) - -local traptiles = { - small = tiles.chest_trapped_small, - double = tiles.chest_trapped_double, -} - -mcl_chests.register_chest("trapped_chest", - S("Trapped Chest"), - S("A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped chests with double the capacity by placing two trapped chests next to each other."), - chestusage, - S("27 inventory slots") .. - "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), - traptiles, - nil, - { - receptor = { - state = mesecon.state.off, - rules = trapped_chest_mesecons_rules, - }, - }, - function(pos, node, clicker) - minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_small", param2 = node.param2 }) - find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" }, node.param2, false, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_small") - mesecon.receptor_on(pos, trapped_chest_mesecons_rules) - end, - function(pos, node, clicker) - local meta = minetest.get_meta(pos) - meta:set_int("players", 1) - - minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) - find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", tiles.chest_trapped_double, node.param2, true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") - mesecon.receptor_on(pos, trapped_chest_mesecons_rules) - - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) - mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) - end, - function(pos, node, clicker) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - - minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) - mesecon.receptor_on(pos, trapped_chest_mesecons_rules) - - minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) - find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", tiles.chest_trapped_double, node.param2, - true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") - mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) - end -) - -mcl_chests.register_chest("trapped_chest_on", - nil, nil, nil, nil, traptiles, true, - { - receptor = { - state = mesecon.state.on, - rules = trapped_chest_mesecons_rules, - }, - }, - nil, nil, nil, - "trapped_chest", - "trapped_chest" -) - - - --- ================= -- --- CONTINUE init.lua -- --- ================= -- +local modpath = minetest.get_modpath("mcl_chests") +dofile(modpath .. "/api.lua") +dofile(modpath .. "/chests.lua") +dofile(modpath .. "/ender.lua") +dofile(modpath .. "/shulkers.lua") @@ -1092,538 +48,22 @@ mcl_chests.register_chest("trapped_chest_on", minetest.register_on_player_receive_fields(function(player, formname, fields) if formname:find("mcl_chests:") == 1 then if fields.quit then - player_chest_close(player) + mcl_chests.player_chest_close(player) end end end) minetest.register_on_leaveplayer(function(player) - player_chest_close(player) + mcl_chests.player_chest_close(player) end) --- =================== -- --- CONTINUE chests.lua -- --- =================== -- - - - -minetest.register_craft({ - output = "mcl_chests:chest", - recipe = { - { "group:wood", "group:wood", "group:wood" }, - { "group:wood", "", "group:wood" }, - { "group:wood", "group:wood", "group:wood" }, - }, -}) - -minetest.register_craft({ - type = "fuel", - recipe = "mcl_chests:chest", - burntime = 15, -}) - -minetest.register_craft({ - type = "fuel", - recipe = "mcl_chests:trapped_chest", - burntime = 15, -}) - - - --- ========= -- --- ender.lua -- --- ========= -- - - - -minetest.register_node("mcl_chests:ender_chest", { - description = S("Ender Chest"), - _tt_help = S("27 interdimensional inventory slots") .. - "\n" .. S("Put items inside, retrieve them from any ender chest"), - _doc_items_longdesc = S( - "Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This inventory is the same no matter from which ender chest you access it from. If you put one item into one ender chest, you will find it in all other ender chests. Each player will only see their own items, but not the items of other players."), - _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), - drawtype = "mesh", - mesh = "mcl_chests_chest.b3d", - tiles = tiles.chest_ender_small, - use_texture_alpha = "opaque", - paramtype = "light", - paramtype2 = "facedir", - groups = { deco_block = 1 }, - sounds = mcl_sounds.node_sound_stone_defaults(), - on_construct = function(pos) - local node = minetest.get_node(pos) - node.name = "mcl_chests:ender_chest_small" - minetest.set_node(pos, node) - end, -}) - -local formspec_ender_chest = table.concat({ - "formspec_version[4]", - "size[11.75,10.425]", - - "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Ender Chest"))) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), - "list[current_player;enderchest;0.375,0.75;9,3;]", - "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3), - "list[current_player;main;0.375,5.1;9,3;9]", - - mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1), - "list[current_player;main;0.375,9.05;9,1;]", - - "listring[current_player;enderchest]", - "listring[current_player;main]", -}) - - -minetest.register_node("mcl_chests:ender_chest_small", { - description = S("Ender Chest"), - _tt_help = S("27 interdimensional inventory slots") .. - "\n" .. S("Put items inside, retrieve them from any ender chest"), - _doc_items_longdesc = S( - "Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This inventory is the same no matter from which ender chest you access it from. If you put one item into one ender chest, you will find it in all other ender chests. Each player will only see their own items, but not the items of other players."), - _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 }, - }, - _chest_entity_textures = tiles.ender_chest_texture, - _chest_entity_sound = "mcl_chests_enderchest", - _chest_entity_mesh = "mcl_chests_chest", - _chest_entity_animation_type = "chest", - tiles = { "blank.png^[resize:16x16" }, - use_texture_alpha = "clip", - -- Note: The “container” group is missing here because the ender chest does not - -- have an inventory on its own - groups = { pickaxey = 1, deco_block = 1, material_stone = 1, chest_entity = 1, not_in_creative_inventory = 1 }, - is_ground_content = false, - paramtype = "light", - light_source = 7, - paramtype2 = "facedir", - sounds = mcl_sounds.node_sound_stone_defaults(), - drop = "mcl_core:obsidian 8", - on_construct = function(pos) - create_entity(pos, "mcl_chests:ender_chest_small", tiles.ender_chest_texture, minetest.get_node(pos).param2, false, - "mcl_chests_enderchest", "mcl_chests_chest", "chest") - end, - on_rightclick = function(pos, node, clicker) - if minetest.registered_nodes[minetest.get_node(vector.offset(pos, 0, 1, 0)).name].groups.opaque == 1 then - -- won't open if there is no space from the top - return false - end - minetest.show_formspec(clicker:get_player_name(), "mcl_chests:ender_chest_" .. clicker:get_player_name(), - formspec_ender_chest) - player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", tiles.ender_chest_texture, node.param2, false, - "mcl_chests_enderchest", "mcl_chests_chest") - end, - on_receive_fields = function(pos, formname, fields, sender) - if fields.quit then - player_chest_close(sender) - end - end, - _mcl_blast_resistance = 3000, - _mcl_hardness = 22.5, - _mcl_silk_touch_drop = { "mcl_chests:ender_chest" }, - on_rotate = simple_rotate, -}) - -minetest.register_on_joinplayer(function(player) - local inv = player:get_inventory() - inv:set_size("enderchest", 9 * 3) -end) - -minetest.register_allow_player_inventory_action(function(player, action, inv, info) - if inv:get_location().type == "player" and ( - action == "move" and (info.from_list == "enderchest" or info.to_list == "enderchest") - or action == "put" and info.listname == "enderchest" - or action == "take" and info.listname == "enderchest" - ) then - local def = player:get_wielded_item():get_definition() - local range = (def and def.range or player:get_inventory():get_stack("hand", 1):get_definition().range) + 1 - if not minetest.find_node_near(player:get_pos(), range, "mcl_chests:ender_chest_small", true) then - return 0 - end - end -end) - -minetest.register_craft({ - output = "mcl_chests:ender_chest", - recipe = { - { "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" }, - { "mcl_core:obsidian", "mcl_end:ender_eye", "mcl_core:obsidian" }, - { "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" }, - }, -}) - - - --- ============ -- --- shulkers.lua -- --- ============ -- - - - --- Shulker boxes -local boxtypes = { - white = S("White Shulker Box"), - grey = S("Light Grey Shulker Box"), - orange = S("Orange Shulker Box"), - cyan = S("Cyan Shulker Box"), - magenta = S("Magenta Shulker Box"), - violet = S("Purple Shulker Box"), - lightblue = S("Light Blue Shulker Box"), - blue = S("Blue Shulker Box"), - yellow = S("Yellow Shulker Box"), - brown = S("Brown Shulker Box"), - green = S("Lime Shulker Box"), - dark_green = S("Green Shulker Box"), - pink = S("Pink Shulker Box"), - red = S("Red Shulker Box"), - dark_grey = S("Grey Shulker Box"), - black = S("Black Shulker Box"), -} - -local shulker_mob_textures = { - white = "mobs_mc_shulker_white.png", - grey = "mobs_mc_shulker_silver.png", - orange = "mobs_mc_shulker_orange.png", - cyan = "mobs_mc_shulker_cyan.png", - magenta = "mobs_mc_shulker_magenta.png", - violet = "mobs_mc_shulker_purple.png", - lightblue = "mobs_mc_shulker_light_blue.png", - blue = "mobs_mc_shulker_blue.png", - yellow = "mobs_mc_shulker_yellow.png", - brown = "mobs_mc_shulker_brown.png", - green = "mobs_mc_shulker_lime.png", - dark_green = "mobs_mc_shulker_green.png", - pink = "mobs_mc_shulker_pink.png", - red = "mobs_mc_shulker_red.png", - dark_grey = "mobs_mc_shulker_gray.png", - black = "mobs_mc_shulker_black.png", -} -local canonical_shulker_color = "violet" -local normal_canonical_name = "mcl_chests:" .. canonical_shulker_color .. "_shulker_box" -local small_canonical_name = normal_canonical_name .. "_small" - ---WARNING: after formspec v4 update, old shulker boxes will need to be placed again to get the new formspec -local function formspec_shulker_box(name) - if not name or name == "" then - name = S("Shulker Box") - end - - return table.concat({ - "formspec_version[4]", - "size[11.75,10.425]", - - "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), - "list[context;main;0.375,0.75;9,3;]", - "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", - mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3), - "list[current_player;main;0.375,5.1;9,3;9]", - - mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1), - "list[current_player;main;0.375,9.05;9,1;]", - - "listring[context;main]", - "listring[current_player;main]", - }) -end - -local function set_shulkerbox_meta(nmeta, imeta) - local name = imeta:get_string("name") - nmeta:set_string("description", imeta:get_string("description")) - nmeta:set_string("name", name) - nmeta:set_string("formspec", formspec_shulker_box(name)) -end - -for color, desc in pairs(boxtypes) do - local mob_texture = shulker_mob_textures[color] - local is_canonical = color == canonical_shulker_color - local longdesc, usagehelp, create_entry, entry_name - if doc then - if is_canonical then - longdesc = S( - "A shulker box is a portable container which provides 27 inventory slots for any item except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes as well as their contents can be taken as a single item. Shulker boxes come in many different colors.") - usagehelp = S( - "To access the inventory of a shulker box, place and right-click it. To take a shulker box and its contents with you, just break and collect it, the items will not fall out. Place the shulker box again to be able to retrieve its contents.") - entry_name = S("Shulker Box") - else - create_entry = false - end - end - - local normal_name = "mcl_chests:" .. color .. "_shulker_box" - local small_name = normal_name .. "_small" - - minetest.register_node(normal_name, { - description = desc, - _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), - _doc_items_create_entry = create_entry, - _doc_items_entry_name = entry_name, - _doc_items_longdesc = longdesc, - _doc_items_usagehelp = usagehelp, - tiles = { mob_texture }, - use_texture_alpha = "opaque", - drawtype = "mesh", - mesh = "mcl_chests_shulker.b3d", - groups = { - handy = 1, - pickaxey = 1, - container = 2, - deco_block = 1, - dig_by_piston = 1, - shulker_box = 1, - old_shulker_box_node = 1 - }, - is_ground_content = false, - sounds = mcl_sounds.node_sound_stone_defaults(), - stack_max = 1, - drop = "", - paramtype = "light", - paramtype2 = "facedir", - on_construct = function(pos) - local node = minetest.get_node(pos) - node.name = small_name - minetest.set_node(pos, node) - end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - local nmeta = minetest.get_meta(pos) - local imetadata = itemstack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) - local ninv = nmeta:get_inventory() - ninv:set_list("main", iinv_main) - ninv:set_size("main", 9 * 3) - set_shulkerbox_meta(nmeta, itemstack:get_meta()) - - if minetest.is_creative_enabled(placer:get_player_name()) then - if not ninv:is_empty("main") then - return nil - else - return itemstack - end - else - return nil - end - end, - _on_dispense = function(stack, pos, droppos, dropnode, dropdir) - -- Place shulker box as node - if minetest.registered_nodes[dropnode.name].buildable_to then - minetest.set_node(droppos, { name = small_name, param2 = minetest.dir_to_facedir(dropdir) }) - local ninv = minetest.get_inventory({ type = "node", pos = droppos }) - local imetadata = stack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) - ninv:set_list("main", iinv_main) - ninv:set_size("main", 9 * 3) - set_shulkerbox_meta(minetest.get_meta(droppos), stack:get_meta()) - stack:take_item() - end - return stack - end, - }) - - minetest.register_node(small_name, { - description = desc, - _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), - _doc_items_create_entry = create_entry, - _doc_items_entry_name = entry_name, - _doc_items_longdesc = longdesc, - _doc_items_usagehelp = usagehelp, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { -0.48, -0.5, -0.48, 0.48, 0.489, 0.48 }, - }, - tiles = { "blank.png^[resize:16x16" }, - use_texture_alpha = "clip", - _chest_entity_textures = { mob_texture }, - _chest_entity_sound = "mcl_chests_shulker", - _chest_entity_mesh = "mcl_chests_shulker", - _chest_entity_animation_type = "shulker", - groups = { - handy = 1, - pickaxey = 1, - container = 2, - deco_block = 1, - dig_by_piston = 1, - shulker_box = 1, - chest_entity = 1, - not_in_creative_inventory = 1 - }, - is_ground_content = false, - sounds = mcl_sounds.node_sound_stone_defaults(), - stack_max = 1, - drop = "", - paramtype = "light", - paramtype2 = "facedir", - -- TODO: Make shulker boxes rotatable - -- This doesn't work, it just destroys the inventory: - -- on_place = minetest.rotate_node, - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", formspec_shulker_box(nil)) - local inv = meta:get_inventory() - inv:set_size("main", 9 * 3) - create_entity(pos, small_name, { mob_texture }, minetest.get_node(pos).param2, false, "mcl_chests_shulker", - "mcl_chests_shulker", "shulker") - end, - after_place_node = function(pos, placer, itemstack, pointed_thing) - local nmeta = minetest.get_meta(pos) - local imetadata = itemstack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) - local ninv = nmeta:get_inventory() - ninv:set_list("main", iinv_main) - ninv:set_size("main", 9 * 3) - set_shulkerbox_meta(nmeta, itemstack:get_meta()) - - if minetest.is_creative_enabled(placer:get_player_name()) then - if not ninv:is_empty("main") then - return nil - else - return itemstack - end - else - return nil - end - end, - on_rightclick = function(pos, node, clicker) - player_chest_open(clicker, pos, small_name, { mob_texture }, node.param2, false, "mcl_chests_shulker", - "mcl_chests_shulker", true) - end, - on_receive_fields = function(pos, formname, fields, sender) - if fields.quit then - player_chest_close(sender) - end - end, - on_destruct = function(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - local items = {} - for i = 1, inv:get_size("main") do - local stack = inv:get_stack("main", i) - items[i] = stack:to_string() - end - local data = minetest.serialize(items) - local boxitem = ItemStack("mcl_chests:" .. color .. "_shulker_box") - local boxitem_meta = boxitem:get_meta() - boxitem_meta:set_string("description", meta:get_string("description")) - boxitem_meta:set_string("name", meta:get_string("name")) - boxitem:set_metadata(data) - - if minetest.is_creative_enabled("") then - if not inv:is_empty("main") then - minetest.add_item(pos, boxitem) - end - else - minetest.add_item(pos, boxitem) - end - end, - allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - end - -- Do not allow to place shulker boxes into shulker boxes - local group = minetest.get_item_group(stack:get_name(), "shulker_box") - if group == 0 or group == nil then - return stack:get_count() - else - return 0 - end - end, - _mcl_blast_resistance = 6, - _mcl_hardness = 2, - _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main", mcl_chests.is_not_shulker_box, 1) - end, - }) - - if doc and not is_canonical then - doc.add_entry_alias("nodes", normal_canonical_name, "nodes", normal_name) - doc.add_entry_alias("nodes", small_canonical_name, "nodes", small_name) - end - - minetest.register_craft({ - type = "shapeless", - output = normal_name, - recipe = { "group:shulker_box", "mcl_dye:" .. color }, - }) -end - - - --- ================ -- --- CONTINUE api.lua -- --- ================ -- - - - --- Returns false if itemstack is a shulker box -function mcl_chests.is_not_shulker_box(stack) - local g = minetest.get_item_group(stack:get_name(), "shulker_box") - return g == 0 or g == nil -end - - - --- ===================== -- --- CONTINUE shulkers.lua -- --- ===================== -- - - - -minetest.register_craft({ - output = "mcl_chests:violet_shulker_box", - recipe = { - { "mcl_mobitems:shulker_shell" }, - { "mcl_chests:chest" }, - { "mcl_mobitems:shulker_shell" }, - }, -}) - --- Save metadata of shulker box when used in crafting -minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv) - if minetest.get_item_group(itemstack:get_name(), "shulker_box") ~= 1 then - return - end - local original - for i = 1, #old_craft_grid do - local item = old_craft_grid[i]:get_name() - if minetest.get_item_group(item, "shulker_box") == 1 then - original = old_craft_grid[i] - break - end - end - if original then - local ometa = original:get_meta():to_table() - local nmeta = itemstack:get_meta() - nmeta:from_table(ometa) - return itemstack - end -end) - - - --- ================= -- --- CONTINUE init.lua -- --- ================= -- - - - local function select_and_spawn_entity(pos, node) local node_name = node.name local node_def = minetest.registered_nodes[node_name] local double_chest = minetest.get_item_group(node_name, "double_chest") > 0 - find_or_create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest, + mcl_chests.find_or_create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest, node_def._chest_entity_sound, node_def._chest_entity_mesh, node_def._chest_entity_animation_type) end @@ -1649,7 +89,7 @@ minetest.register_lbm({ select_and_spawn_entity(pos, node) if node_name == "mcl_chests:trapped_chest_on" then minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos)) - chest_update_after_close(pos) + mcl_chests.chest_update_after_close(pos) elseif node_name == "mcl_chests:ender_chest" then local meta = minetest.get_meta(pos) meta:set_string("formspec", formspec_ender_chest) @@ -1667,18 +107,7 @@ minetest.register_lbm({ run_at_every_load = true, action = function(pos, node) minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos)) - chest_update_after_close(pos) - end, -}) - -minetest.register_lbm({ - label = "Update shulker box formspecs (0.72.0)", - name = "mcl_chests:update_shulker_box_formspecs_0_72_0", - nodenames = { "group:shulker_box" }, - run_at_every_load = false, - action = function(pos, node) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", formspec_shulker_box(meta:get_string("name"))) + mcl_chests.chest_update_after_close(pos) end, }) diff --git a/mods/ITEMS/mcl_chests/shulkers.lua b/mods/ITEMS/mcl_chests/shulkers.lua new file mode 100644 index 000000000..0323c6fc9 --- /dev/null +++ b/mods/ITEMS/mcl_chests/shulkers.lua @@ -0,0 +1,342 @@ +local S = minetest.get_translator(minetest.get_current_modname()) +local F = minetest.formspec_escape +local C = minetest.colorize + +-- Shulker boxes +local boxtypes = { + white = S("White Shulker Box"), + grey = S("Light Grey Shulker Box"), + orange = S("Orange Shulker Box"), + cyan = S("Cyan Shulker Box"), + magenta = S("Magenta Shulker Box"), + violet = S("Purple Shulker Box"), + lightblue = S("Light Blue Shulker Box"), + blue = S("Blue Shulker Box"), + yellow = S("Yellow Shulker Box"), + brown = S("Brown Shulker Box"), + green = S("Lime Shulker Box"), + dark_green = S("Green Shulker Box"), + pink = S("Pink Shulker Box"), + red = S("Red Shulker Box"), + dark_grey = S("Grey Shulker Box"), + black = S("Black Shulker Box"), +} + +local shulker_mob_textures = { + white = "mobs_mc_shulker_white.png", + grey = "mobs_mc_shulker_silver.png", + orange = "mobs_mc_shulker_orange.png", + cyan = "mobs_mc_shulker_cyan.png", + magenta = "mobs_mc_shulker_magenta.png", + violet = "mobs_mc_shulker_purple.png", + lightblue = "mobs_mc_shulker_light_blue.png", + blue = "mobs_mc_shulker_blue.png", + yellow = "mobs_mc_shulker_yellow.png", + brown = "mobs_mc_shulker_brown.png", + green = "mobs_mc_shulker_lime.png", + dark_green = "mobs_mc_shulker_green.png", + pink = "mobs_mc_shulker_pink.png", + red = "mobs_mc_shulker_red.png", + dark_grey = "mobs_mc_shulker_gray.png", + black = "mobs_mc_shulker_black.png", +} + +local canonical_shulker_color = "violet" +local normal_canonical_name = "mcl_chests:" .. canonical_shulker_color .. "_shulker_box" +local small_canonical_name = normal_canonical_name .. "_small" + +--WARNING: after formspec v4 update, old shulker boxes will need to be placed again to get the new formspec +local function formspec_shulker_box(name) + if not name or name == "" then + name = S("Shulker Box") + end + + return table.concat({ + "formspec_version[4]", + "size[11.75,10.425]", + + "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3), + "list[context;main;0.375,0.75;9,3;]", + "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]", + mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3), + "list[current_player;main;0.375,5.1;9,3;9]", + + mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1), + "list[current_player;main;0.375,9.05;9,1;]", + + "listring[context;main]", + "listring[current_player;main]", + }) +end + +local function set_shulkerbox_meta(nmeta, imeta) + local name = imeta:get_string("name") + nmeta:set_string("description", imeta:get_string("description")) + nmeta:set_string("name", name) + nmeta:set_string("formspec", formspec_shulker_box(name)) +end + +for color, desc in pairs(boxtypes) do + local mob_texture = shulker_mob_textures[color] + local is_canonical = color == canonical_shulker_color + local longdesc, usagehelp, create_entry, entry_name + if doc then + if is_canonical then + longdesc = S([[ + A shulker box is a portable container which provides 27 inventory slots for any item + except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes + as well as their contents can be taken as a single item. Shulker boxes come in many + different colors. + ]]) + usagehelp = S([[ + To access the inventory of a shulker box, place and right-click it. To take a shulker + box and its contents with you, just break and collect it, the items will not fall out. + Place the shulker box again to be able to retrieve its contents. + ]]) + entry_name = S("Shulker Box") + else + create_entry = false + end + end + + local normal_name = "mcl_chests:" .. color .. "_shulker_box" + local small_name = normal_name .. "_small" + + minetest.register_node(normal_name, { + description = desc, + _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), + _doc_items_create_entry = create_entry, + _doc_items_entry_name = entry_name, + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = usagehelp, + tiles = { mob_texture }, + use_texture_alpha = "opaque", + drawtype = "mesh", + mesh = "mcl_chests_shulker.b3d", + groups = { + handy = 1, + pickaxey = 1, + container = 2, + deco_block = 1, + dig_by_piston = 1, + shulker_box = 1, + old_shulker_box_node = 1 + }, + is_ground_content = false, + sounds = mcl_sounds.node_sound_stone_defaults(), + stack_max = 1, + drop = "", + paramtype = "light", + paramtype2 = "facedir", + on_construct = function(pos) + local node = minetest.get_node(pos) + node.name = small_name + minetest.set_node(pos, node) + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + local nmeta = minetest.get_meta(pos) + local imetadata = itemstack:get_metadata() + local iinv_main = minetest.deserialize(imetadata) + local ninv = nmeta:get_inventory() + ninv:set_list("main", iinv_main) + ninv:set_size("main", 9 * 3) + set_shulkerbox_meta(nmeta, itemstack:get_meta()) + + if minetest.is_creative_enabled(placer:get_player_name()) then + if not ninv:is_empty("main") then + return nil + else + return itemstack + end + else + return nil + end + end, + _on_dispense = function(stack, pos, droppos, dropnode, dropdir) + -- Place shulker box as node + if minetest.registered_nodes[dropnode.name].buildable_to then + minetest.set_node(droppos, { name = small_name, param2 = minetest.dir_to_facedir(dropdir) }) + local ninv = minetest.get_inventory({ type = "node", pos = droppos }) + local imetadata = stack:get_metadata() + local iinv_main = minetest.deserialize(imetadata) + ninv:set_list("main", iinv_main) + ninv:set_size("main", 9 * 3) + set_shulkerbox_meta(minetest.get_meta(droppos), stack:get_meta()) + stack:take_item() + end + return stack + end, + }) + + minetest.register_node(small_name, { + description = desc, + _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), + _doc_items_create_entry = create_entry, + _doc_items_entry_name = entry_name, + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = usagehelp, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { -0.48, -0.5, -0.48, 0.48, 0.489, 0.48 }, + }, + tiles = { "blank.png^[resize:16x16" }, + use_texture_alpha = "clip", + _chest_entity_textures = { mob_texture }, + _chest_entity_sound = "mcl_chests_shulker", + _chest_entity_mesh = "mcl_chests_shulker", + _chest_entity_animation_type = "shulker", + groups = { + handy = 1, + pickaxey = 1, + container = 2, + deco_block = 1, + dig_by_piston = 1, + shulker_box = 1, + chest_entity = 1, + not_in_creative_inventory = 1 + }, + is_ground_content = false, + sounds = mcl_sounds.node_sound_stone_defaults(), + stack_max = 1, + drop = "", + paramtype = "light", + paramtype2 = "facedir", + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", formspec_shulker_box(nil)) + local inv = meta:get_inventory() + inv:set_size("main", 9 * 3) + mcl_chests.create_entity(pos, small_name, { mob_texture }, minetest.get_node(pos).param2, false, + "mcl_chests_shulker", "mcl_chests_shulker", "shulker") + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + local nmeta = minetest.get_meta(pos) + local imetadata = itemstack:get_metadata() + local iinv_main = minetest.deserialize(imetadata) + local ninv = nmeta:get_inventory() + ninv:set_list("main", iinv_main) + ninv:set_size("main", 9 * 3) + set_shulkerbox_meta(nmeta, itemstack:get_meta()) + + if minetest.is_creative_enabled(placer:get_player_name()) then + if not ninv:is_empty("main") then + return nil + else + return itemstack + end + else + return nil + end + end, + on_rightclick = function(pos, node, clicker) + mcl_chests.player_chest_open(clicker, pos, small_name, { mob_texture }, node.param2, false, + "mcl_chests_shulker", "mcl_chests_shulker", true) + end, + on_receive_fields = function(pos, formname, fields, sender) + if fields.quit then + mcl_chests.player_chest_close(sender) + end + end, + on_destruct = function(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local items = {} + for i = 1, inv:get_size("main") do + local stack = inv:get_stack("main", i) + items[i] = stack:to_string() + end + local data = minetest.serialize(items) + local boxitem = ItemStack("mcl_chests:" .. color .. "_shulker_box") + local boxitem_meta = boxitem:get_meta() + boxitem_meta:set_string("description", meta:get_string("description")) + boxitem_meta:set_string("name", meta:get_string("name")) + boxitem:set_metadata(data) + + if minetest.is_creative_enabled("") then + if not inv:is_empty("main") then + minetest.add_item(pos, boxitem) + end + else + minetest.add_item(pos, boxitem) + end + end, + allow_metadata_inventory_move = mcl_chests.protection_check_move, + allow_metadata_inventory_take = mcl_chests.protection_check_put_take, + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + local name = player:get_player_name() + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return 0 + end + -- Do not allow to place shulker boxes into shulker boxes + local group = minetest.get_item_group(stack:get_name(), "shulker_box") + if group == 0 or group == nil then + return stack:get_count() + else + return 0 + end + end, + on_rotate = mcl_chests.simple_rotate, + _mcl_blast_resistance = 6, + _mcl_hardness = 2, + _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + return inv, "main", + mcl_util.select_stack(hop_inv, hop_list, inv, "main", mcl_chests.is_not_shulker_box, 1) + end, + }) + + if doc and not is_canonical then + doc.add_entry_alias("nodes", normal_canonical_name, "nodes", normal_name) + doc.add_entry_alias("nodes", small_canonical_name, "nodes", small_name) + end + + minetest.register_craft({ + type = "shapeless", + output = normal_name, + recipe = { "group:shulker_box", "mcl_dye:" .. color }, + }) +end + +minetest.register_craft({ + output = "mcl_chests:violet_shulker_box", + recipe = { + { "mcl_mobitems:shulker_shell" }, + { "mcl_chests:chest" }, + { "mcl_mobitems:shulker_shell" }, + }, +}) + +-- Save metadata of shulker box when used in crafting +minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv) + if minetest.get_item_group(itemstack:get_name(), "shulker_box") ~= 1 then return end + + local original + for i = 1, #old_craft_grid do + local item = old_craft_grid[i]:get_name() + if minetest.get_item_group(item, "shulker_box") == 1 then + original = old_craft_grid[i] + break + end + end + if original then + local ometa = original:get_meta():to_table() + local nmeta = itemstack:get_meta() + nmeta:from_table(ometa) + return itemstack + end +end) + +minetest.register_lbm({ + label = "Update shulker box formspecs (0.72.0)", + name = "mcl_chests:update_shulker_box_formspecs_0_72_0", + nodenames = { "group:shulker_box" }, + run_at_every_load = false, + action = function(pos, node) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", formspec_shulker_box(meta:get_string("name"))) + end, +}) From d6d64d8837b3078b6043b521d65b673ff8b11726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 21 Jun 2024 18:43:12 +0700 Subject: [PATCH 04/25] Fix long doc strings --- mods/ITEMS/mcl_chests/chests.lua | 27 ++++++++++++++++++--------- mods/ITEMS/mcl_chests/ender.lua | 12 ++++++------ mods/ITEMS/mcl_chests/shulkers.lua | 22 +++++++++++----------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index c66f336da..8ec67e133 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -8,7 +8,10 @@ local chestusage = S("To access its inventory, rightclick it. When broken, the i mcl_chests.register_chest("chest", S("Chest"), - S("Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with double the capacity by placing two chests next to each other."), + S( + "Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with " .. + "double the capacity by placing two chests next to each other." + ), chestusage, S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), { @@ -28,7 +31,11 @@ local traptiles = { mcl_chests.register_chest("trapped_chest", S("Trapped Chest"), - S("A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped chests with double the capacity by placing two trapped chests next to each other."), + S( + "A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone " .. + "signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped " .. + "chests with double the capacity by placing two trapped chests next to each other." + ), chestusage, S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), @@ -42,8 +49,9 @@ mcl_chests.register_chest("trapped_chest", }, function(pos, node, clicker) minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_small", param2 = node.param2 }) - mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" }, node.param2, false, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_small") + mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" }, + node.param2, false, "default_chest", "mcl_chests_chest", "chest") + :reinitialize("mcl_chests:trapped_chest_on_small") mesecon.receptor_on(pos, trapped_chest_mesecons_rules) end, function(pos, node, clicker) @@ -51,8 +59,9 @@ mcl_chests.register_chest("trapped_chest", meta:set_int("players", 1) minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) - mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") + mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", + mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", + "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos, trapped_chest_mesecons_rules) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") @@ -66,9 +75,9 @@ mcl_chests.register_chest("trapped_chest", mesecon.receptor_on(pos, trapped_chest_mesecons_rules) minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) - mcl_chests.find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", mcl_chests.tiles.chest_trapped_double, node.param2, - true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") + mcl_chests.find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", + mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", + "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) end ) diff --git a/mods/ITEMS/mcl_chests/ender.lua b/mods/ITEMS/mcl_chests/ender.lua index c126563ca..f41ec950a 100644 --- a/mods/ITEMS/mcl_chests/ender.lua +++ b/mods/ITEMS/mcl_chests/ender.lua @@ -2,12 +2,12 @@ local S = minetest.get_translator(minetest.get_current_modname()) local F = minetest.formspec_escape local C = minetest.colorize -local longdesc = S([[ - Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This - inventory is the same no matter from which ender chest you access it from. If you put one item into one - ender chest, you will find it in all other ender chests. Each player will only see their own items, but - not the items of other players. -]]) +local longdesc = S( + "Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This " .. + "inventory is the same no matter from which ender chest you access it from. If you put one item into one " .. + "ender chest, you will find it in all other ender chests. Each player will only see their own items, but " .. + "not the items of other players." +) minetest.register_node("mcl_chests:ender_chest", { description = S("Ender Chest"), diff --git a/mods/ITEMS/mcl_chests/shulkers.lua b/mods/ITEMS/mcl_chests/shulkers.lua index 0323c6fc9..a80ea7ae1 100644 --- a/mods/ITEMS/mcl_chests/shulkers.lua +++ b/mods/ITEMS/mcl_chests/shulkers.lua @@ -83,17 +83,17 @@ for color, desc in pairs(boxtypes) do local longdesc, usagehelp, create_entry, entry_name if doc then if is_canonical then - longdesc = S([[ - A shulker box is a portable container which provides 27 inventory slots for any item - except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes - as well as their contents can be taken as a single item. Shulker boxes come in many - different colors. - ]]) - usagehelp = S([[ - To access the inventory of a shulker box, place and right-click it. To take a shulker - box and its contents with you, just break and collect it, the items will not fall out. - Place the shulker box again to be able to retrieve its contents. - ]]) + longdesc = S( + "A shulker box is a portable container which provides 27 inventory slots for any item " .. + "except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes " .. + "as well as their contents can be taken as a single item. Shulker boxes come in many " .. + "different colors." + ) + usagehelp = S( + "To access the inventory of a shulker box, place and right-click it. To take a shulker " .. + "box and its contents with you, just break and collect it, the items will not fall out. " .. + "Place the shulker box again to be able to retrieve its contents." + ) entry_name = S("Shulker Box") else create_entry = false From 1d77017ca9a418d6b1d2b34911659e19f7de2769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 21 Jun 2024 20:22:26 +0700 Subject: [PATCH 05/25] Refactor mcl_chests.register_chest and clean-up --- mods/ITEMS/mcl_chests/api.lua | 257 ++++++++++++++++--------------- mods/ITEMS/mcl_chests/chests.lua | 69 +++++---- 2 files changed, 170 insertions(+), 156 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 6b602b5c7..0723dc1f2 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -227,8 +227,6 @@ local function protection_check_put_take(pos, listname, index, stack, player) end mcl_chests.protection_check_put_take = protection_check_put_take -local trapped_chest_mesecons_rules = mesecon.rules.pplate - -- To be called when a chest is closed (only relevant for trapped chest atm) local function chest_update_after_close(pos) local node = minetest.get_node(pos) @@ -237,25 +235,26 @@ local function chest_update_after_close(pos) minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_small", param2 = node.param2 }) find_or_create_entity(pos, "mcl_chests:trapped_chest_small", { "mcl_chests_trapped.png" }, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small") - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos, mesecon.rules.pplate) elseif node.name == "mcl_chests:trapped_chest_on_left" then minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) find_or_create_entity(pos, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos, mesecon.rules.pplate) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) - mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos_other, mesecon.rules.pplate) elseif node.name == "mcl_chests:trapped_chest_on_right" then minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 }) - mesecon.receptor_off(pos, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos, mesecon.rules.pplate) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 }) - find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, - "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") - mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double, + node.param2, true, "default_chest", "mcl_chests_chest", "chest") + :reinitialize("mcl_chests:trapped_chest_left") + mesecon.receptor_off(pos_other, mesecon.rules.pplate) end end mcl_chests.chest_update_after_close = chest_update_after_close @@ -269,8 +268,8 @@ local function player_chest_close(player) end if animated_chests then find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, - open_chest.double, - open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest"):close(name) + open_chest.double, open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest") + :close(name) end chest_update_after_close(open_chest.pos) @@ -278,96 +277,108 @@ local function player_chest_close(player) end mcl_chests.player_chest_close = player_chest_close --- This is a helper function to register both chests and trapped chests. Trapped chests will make use of the additional parameters -function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, tiles_table, hidden, mesecons, - on_rightclick_addendum, on_rightclick_addendum_left, - on_rightclick_addendum_right, drop, canonical_basename) - if not drop then - drop = "mcl_chests:" .. basename +local function double_chest_add_item(top_inv, bottom_inv, listname, stack) + if not stack or stack:is_empty() then return end + + local name = stack:get_name() + + local function top_off(inv, stack) + for c, chest_stack in ipairs(inv:get_list(listname)) do + if stack:is_empty() then + break + end + + if chest_stack:get_name() == name and chest_stack:get_free_space() > 0 then + stack = chest_stack:add_item(stack) + inv:set_stack(listname, c, chest_stack) + end + end + + return stack + end + + stack = top_off(top_inv, stack) + stack = top_off(bottom_inv, stack) + + if not stack:is_empty() then + stack = top_inv:add_item(listname, stack) + if not stack:is_empty() then + bottom_inv:add_item(listname, stack) + end + end +end + +local function on_chest_blast(pos) + local node = minetest.get_node(pos) + drop_items_chest(pos, node) + minetest.remove_node(pos) +end + +local function limit_put_list(stack, list) + for _, other in ipairs(list) do + stack = other:add_item(stack) + if stack:is_empty() then + break + end + end + return stack +end + +local function limit_put(stack, inv1, inv2) + local leftover = ItemStack(stack) + leftover = limit_put_list(leftover, inv1:get_list("main")) + leftover = limit_put_list(leftover, inv2:get_list("main")) + return stack:get_count() - leftover:get_count() +end + +local function close_forms(canonical_basename, pos) + local players = minetest.get_connected_players() + for p = 1, #players do + if vector.distance(players[p]:get_pos(), pos) <= 30 then + minetest.close_formspec(players[p]:get_player_name(), + "mcl_chests:" .. canonical_basename .. "_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z) + end + end +end + +-- This is a helper function to register regular chests (both small and double variants). +-- Some parameters here are only utilized by trapped chests. +function mcl_chests.register_chest(basename, d) + -- If this passes without crash, we know for a fact that d = {...} + assert((d and type(d) == "table"), "Second argument to mcl_chests.register_chest must be a table") + + if not d.drop then + d.drop = "mcl_chests:" .. basename else - drop = "mcl_chests:" .. drop + d.drop = "mcl_chests:" .. d.drop end -- The basename of the "canonical" version of the node, if set (e.g.: trapped_chest_on → trapped_chest). -- Used to get a shared formspec ID and to swap the node back to the canonical version in on_construct. - if not canonical_basename then - canonical_basename = basename - end - - local function double_chest_add_item(top_inv, bottom_inv, listname, stack) - if not stack or stack:is_empty() then return end - - local name = stack:get_name() - - local function top_off(inv, stack) - for c, chest_stack in ipairs(inv:get_list(listname)) do - if stack:is_empty() then - break - end - - if chest_stack:get_name() == name and chest_stack:get_free_space() > 0 then - stack = chest_stack:add_item(stack) - inv:set_stack(listname, c, chest_stack) - end - end - - return stack - end - - stack = top_off(top_inv, stack) - stack = top_off(bottom_inv, stack) - - if not stack:is_empty() then - stack = top_inv:add_item(listname, stack) - if not stack:is_empty() then - bottom_inv:add_item(listname, stack) - end - end + if not d.canonical_basename then + d.canonical_basename = basename end local drop_items_chest = mcl_util.drop_items_from_meta_container("main") - local function on_chest_blast(pos) - local node = minetest.get_node(pos) - drop_items_chest(pos, node) - minetest.remove_node(pos) - end - - local function limit_put_list(stack, list) - for _, other in ipairs(list) do - stack = other:add_item(stack) - if stack:is_empty() then - break - end - end - return stack - end - - local function limit_put(stack, inv1, inv2) - local leftover = ItemStack(stack) - leftover = limit_put_list(leftover, inv1:get_list("main")) - leftover = limit_put_list(leftover, inv2:get_list("main")) - return stack:get_count() - leftover:get_count() - end - local small_name = "mcl_chests:" .. basename .. "_small" - local small_textures = tiles_table.small + local small_textures = d.tiles.small local left_name = "mcl_chests:" .. basename .. "_left" local right_name = "mcl_chests:" .. basename .. "_right" - local double_textures = tiles_table.double + local double_textures = d.tiles.double minetest.register_node("mcl_chests:" .. basename, { - description = desc, - _tt_help = tt_help, - _doc_items_longdesc = longdesc, - _doc_items_usagehelp = usagehelp, - _doc_items_hidden = hidden, + description = d.desc, + _tt_help = d.tt_help, + _doc_items_longdesc = d.longdesc, + _doc_items_usagehelp = d.usagehelp, + _doc_items_hidden = d.hidden, drawtype = "mesh", mesh = "mcl_chests_chest.b3d", tiles = small_textures, use_texture_alpha = "opaque", paramtype = "light", paramtype2 = "facedir", - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = d.sounds, groups = { deco_block = 1 }, on_construct = function(pos, node) local node = minetest.get_node(pos) @@ -379,22 +390,12 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end, }) - local function close_forms(canonical_basename, pos) - local players = minetest.get_connected_players() - for p = 1, #players do - if vector.distance(players[p]:get_pos(), pos) <= 30 then - minetest.close_formspec(players[p]:get_player_name(), - "mcl_chests:" .. canonical_basename .. "_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z) - end - end - end - minetest.register_node(small_name, { - description = desc, - _tt_help = tt_help, - _doc_items_longdesc = longdesc, - _doc_items_usagehelp = usagehelp, - _doc_items_hidden = hidden, + description = d.desc, + _tt_help = d.tt_help, + _doc_items_longdesc = d.longdesc, + _doc_items_usagehelp = d.usagehelp, + _doc_items_hidden = d.hidden, drawtype = "nodebox", node_box = { type = "fixed", @@ -408,7 +409,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, _chest_entity_animation_type = "chest", paramtype = "light", paramtype2 = "facedir", - drop = drop, + drop = d.drop, groups = { handy = 1, axey = 1, @@ -420,7 +421,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, not_in_creative_inventory = 1 }, is_ground_content = false, - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = d.sounds, on_construct = function(pos) local param2 = minetest.get_node(pos).param2 local meta = minetest.get_meta(pos) @@ -497,8 +498,8 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, minetest.log("action", player:get_player_name() .. " takes stuff from chest at " .. minetest.pos_to_string(pos)) end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 2.5, + _mcl_blast_resistance = d.hardness, + _mcl_hardness = d.hardness, on_rightclick = function(pos, node, clicker) local topnode = minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }) @@ -514,7 +515,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end minetest.show_formspec(clicker:get_player_name(), - sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), + sf("mcl_chests:%s_%s_%s_%s", d.canonical_basename, pos.x, pos.y, pos.z), table.concat({ "formspec_version[4]", "size[11.75,10.425]", @@ -533,8 +534,8 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, }) ) - if on_rightclick_addendum then - on_rightclick_addendum(pos, node, clicker) + if d.on_rightclick then + d.on_rightclick(pos, node, clicker) end player_chest_open(clicker, pos, small_name, small_textures, node.param2, false, "default_chest", @@ -542,9 +543,9 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end, on_destruct = function(pos) - close_forms(canonical_basename, pos) + close_forms(d.canonical_basename, pos) end, - mesecons = mesecons, + mesecons = d.mesecons, on_rotate = simple_rotate, }) @@ -572,15 +573,15 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, chest_entity = 1, double_chest = 1 }, - drop = drop, + drop = d.drop, is_ground_content = false, - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = d.sounds, on_construct = function(pos) local n = minetest.get_node(pos) local param2 = n.param2 local p = get_double_container_neighbor_pos(pos, param2, "left") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. canonical_basename .. "_right" then - n.name = "mcl_chests:" .. canonical_basename .. "_small" + if not p or minetest.get_node(p).name ~= "mcl_chests:" .. d.canonical_basename .. "_right" then + n.name = "mcl_chests:" .. d.canonical_basename .. "_small" minetest.swap_node(pos, n) end create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") @@ -594,14 +595,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, return end - close_forms(canonical_basename, pos) + close_forms(d.canonical_basename, pos) local param2 = n.param2 local p = get_double_container_neighbor_pos(pos, param2, "left") if not p or minetest.get_node(p).name ~= "mcl_chests:" .. basename .. "_right" then return end - close_forms(canonical_basename, p) + close_forms(d.canonical_basename, p) minetest.swap_node(p, { name = small_name, param2 = param2 }) create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") @@ -649,8 +650,8 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, minetest.log("action", player:get_player_name() .. " takes stuff from chest at " .. minetest.pos_to_string(pos)) end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 2.5, + _mcl_blast_resistance = d.hardness, + _mcl_hardness = d.hardness, on_rightclick = function(pos, node, clicker) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") @@ -675,7 +676,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end minetest.show_formspec(clicker:get_player_name(), - sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), + sf("mcl_chests:%s_%s_%s_%s", d.canonical_basename, pos.x, pos.y, pos.z), table.concat({ "formspec_version[4]", "size[11.75,14.15]", @@ -704,14 +705,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, }) ) - if on_rightclick_addendum_left then - on_rightclick_addendum_left(pos, node, clicker) + if d.on_rightclick_left then + d.on_rightclick_left(pos, node, clicker) end player_chest_open(clicker, pos, left_name, double_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, - mesecons = mesecons, + mesecons = d.mesecons, on_rotate = no_rotate, _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) local meta = minetest.get_meta(pos) @@ -766,9 +767,9 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, flammable = -1, double_chest = 2 }, - drop = drop, + drop = d.drop, is_ground_content = false, - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = d.sounds, on_construct = function(pos) local n = minetest.get_node(pos) local param2 = n.param2 @@ -787,14 +788,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, return end - close_forms(canonical_basename, pos) + close_forms(d.canonical_basename, pos) local param2 = n.param2 local p = get_double_container_neighbor_pos(pos, param2, "right") if not p or minetest.get_node(p).name ~= left_name then return end - close_forms(canonical_basename, p) + close_forms(d.canonical_basename, p) minetest.swap_node(p, { name = small_name, param2 = param2 }) create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") @@ -842,8 +843,8 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, minetest.log("action", player:get_player_name() .. " takes stuff from chest at " .. minetest.pos_to_string(pos)) end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 2.5, + _mcl_blast_resistance = d.hardness, + _mcl_hardness = d.hardness, on_rightclick = function(pos, node, clicker) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") @@ -868,7 +869,7 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, end minetest.show_formspec(clicker:get_player_name(), - sf("mcl_chests:%s_%s_%s_%s", canonical_basename, pos.x, pos.y, pos.z), + sf("mcl_chests:%s_%s_%s_%s", d.canonical_basename, pos.x, pos.y, pos.z), table.concat({ "formspec_version[4]", "size[11.75,14.15]", @@ -897,14 +898,14 @@ function mcl_chests.register_chest(basename, desc, longdesc, usagehelp, tt_help, }) ) - if on_rightclick_addendum_right then - on_rightclick_addendum_right(pos, node, clicker) + if d.on_rightclick_right then + d.on_rightclick_right(pos, node, clicker) end player_chest_open(clicker, pos_other, left_name, double_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, - mesecons = mesecons, + mesecons = d.mesecons, on_rotate = no_rotate, _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) local node = minetest.get_node(pos) diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 8ec67e133..0215dc825 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -6,55 +6,59 @@ local trapped_chest_mesecons_rules = mesecon.rules.pplate local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") -mcl_chests.register_chest("chest", - S("Chest"), - S( +mcl_chests.register_chest("chest", { + desc = S("Chest"), + longdesc = S( "Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with " .. "double the capacity by placing two chests next to each other." ), - chestusage, - S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), - { + usagehelp = chestusage, + tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), + tiles = { small = mcl_chests.tiles.chest_normal_small, double = mcl_chests.tiles.chest_normal_double, inv = { "default_chest_top.png", "mcl_chests_chest_bottom.png", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "mcl_chests_chest_back.png", "default_chest_front.png" }, }, - false -) + sounds = mcl_sounds.node_sound_wood_defaults(), + hardness = 2.5, + hidden = false, +}) local traptiles = { small = mcl_chests.tiles.chest_trapped_small, double = mcl_chests.tiles.chest_trapped_double, } -mcl_chests.register_chest("trapped_chest", - S("Trapped Chest"), - S( +mcl_chests.register_chest("trapped_chest", { + desc = S("Trapped Chest"), + longdesc = S( "A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone " .. "signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped " .. "chests with double the capacity by placing two trapped chests next to each other." ), - chestusage, - S("27 inventory slots") .. - "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), - traptiles, - nil, - { + usagehelp = chestusage, + tt_help = S("27 inventory slots") .. + "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), + tiles = traptiles, + sounds = mcl_sounds.node_sound_wood_defaults(), + hardness = 2.5, + hidden = false, + mesecons = { receptor = { state = mesecon.state.off, rules = trapped_chest_mesecons_rules, }, }, - function(pos, node, clicker) + on_rightclick = function(pos, node, clicker) minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_small", param2 = node.param2 }) mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" }, node.param2, false, "default_chest", "mcl_chests_chest", "chest") :reinitialize("mcl_chests:trapped_chest_on_small") mesecon.receptor_on(pos, trapped_chest_mesecons_rules) end, - function(pos, node, clicker) + on_rightclick_left = function(pos, node, clicker) local meta = minetest.get_meta(pos) meta:set_int("players", 1) @@ -68,7 +72,7 @@ mcl_chests.register_chest("trapped_chest", minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) end, - function(pos, node, clicker) + on_rightclick_right = function(pos, node, clicker) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) @@ -80,20 +84,29 @@ mcl_chests.register_chest("trapped_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) end -) +}) -mcl_chests.register_chest("trapped_chest_on", - nil, nil, nil, nil, traptiles, true, - { +mcl_chests.register_chest("trapped_chest_on", { + desc = nil, + longdesc = nil, + usagehelp = nil, + tt_help = nil, + tiles = traptiles, + sounds = mcl_sounds.node_sound_wood_defaults(), + hardness = 2.5, + hidden = true, + mesecons = { receptor = { state = mesecon.state.on, rules = trapped_chest_mesecons_rules, }, }, - nil, nil, nil, - "trapped_chest", - "trapped_chest" -) + on_rightclick = nil, + on_rightclick_left = nil, + on_rightclick_right = nil, + drop = "trapped_chest", + canonical_basename = "trapped_chest" +}) minetest.register_craft({ output = "mcl_chests:chest", From 709b73295c3b6be3e093f53923accaba8df56c9e Mon Sep 17 00:00:00 2001 From: cora Date: Fri, 23 Feb 2024 16:45:43 +0100 Subject: [PATCH 06/25] Fix meta:set_string being called with nil --- mods/ITEMS/mcl_chests/api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 0723dc1f2..91069c743 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -434,7 +434,7 @@ function mcl_chests.register_chest(basename, d) -- FIXME: Remove this workaround when the bug has been fixed. -- BEGIN OF WORKAROUND -- meta:set_string("workaround", "ignore_me") - meta:set_string("workaround", nil) -- Done to keep metadata clean + meta:set_string("workaround", "") -- Done to keep metadata clean -- END OF WORKAROUND -- local inv = meta:get_inventory() From d0d96007099578057c4097bebfbc587d61457280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20R=C3=BChle?= Date: Sat, 2 Mar 2024 14:58:49 +0100 Subject: [PATCH 07/25] Fix invisible chests Pass chest entity initialization data to on_activate as staticdata so initialization is atomic, preventing premature deletion of chest entity by concurrent server steps. --- mods/ITEMS/mcl_chests/api.lua | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 91069c743..c98050934 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -75,11 +75,14 @@ minetest.register_entity("mcl_chests:chest", { self.node_name = node_name self.sound_prefix = sound_prefix self.animation_type = animation_type - self.object:set_properties({ + local obj = self.object + obj:set_armor_groups({ immortal = 1 }) + obj:set_properties({ textures = textures, mesh = mesh_prefix .. (double and "_double" or "") .. ".b3d", }) self:set_yaw(dir) + self.players = {} end, reinitialize = function(self, node_name) @@ -102,9 +105,12 @@ minetest.register_entity("mcl_chests:chest", { return true end, - on_activate = function(self) - self.object:set_armor_groups({ immortal = 1 }) - self.players = {} + on_activate = function(self, initialization_data) + if initialization_data:find("^return") then + self:initialize(unpack(minetest.deserialize(initialization_data))) + else + minetest.log("warning", debug.traceback("[mcl_chests] on_activate called without initialization_data")) + end end, on_step = function(self, dtime) @@ -140,10 +146,14 @@ end local function create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos) dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) - local obj = minetest.add_entity(entity_pos, "mcl_chests:chest") - local luaentity = obj:get_luaentity() - luaentity:initialize(pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) - return luaentity + local initialization_data = minetest.serialize({pos, node_name, textures, dir, double, sound_prefix, + mesh_prefix, animation_type}) + local obj = minetest.add_entity(entity_pos, "mcl_chests:chest", initialization_data) + if obj and obj:get_pos() then + return obj:get_luaentity() + else + minetest.log("warning", "[mcl_chests] Failed to create entity at " .. (entity_pos and minetest.pos_to_string(entity_pos, 1) or "nil")) + end end mcl_chests.create_entity = create_entity From 7a5ee4e6e28133acac7d4ca402e8bf304191caf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20R=C3=BChle?= Date: Sun, 3 Mar 2024 22:34:29 +0100 Subject: [PATCH 08/25] Better detection of properly serialized data Remove entity if initialization data is missing. Downgrade message in that case to a warning. --- mods/ITEMS/mcl_chests/api.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index c98050934..42e0a901d 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -106,10 +106,12 @@ minetest.register_entity("mcl_chests:chest", { end, on_activate = function(self, initialization_data) - if initialization_data:find("^return") then + if initialization_data and initialization_data:find("\"###mcl_chests:chest###\"") then self:initialize(unpack(minetest.deserialize(initialization_data))) else - minetest.log("warning", debug.traceback("[mcl_chests] on_activate called without initialization_data")) + minetest.log("warning", + "[mcl_chests] on_activate called without proper initialization_data ... removing entity") + self.object:remove() end end, @@ -147,7 +149,7 @@ local function create_entity(pos, node_name, textures, param2, double, sound_pre entity_pos) dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) local initialization_data = minetest.serialize({pos, node_name, textures, dir, double, sound_prefix, - mesh_prefix, animation_type}) + mesh_prefix, animation_type, "###mcl_chests:chest###"}) local obj = minetest.add_entity(entity_pos, "mcl_chests:chest", initialization_data) if obj and obj:get_pos() then return obj:get_luaentity() From 16dd8694a66f6dfda40fd959e1659b20d0babe30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Sat, 22 Jun 2024 16:35:52 +0700 Subject: [PATCH 09/25] Refactor mcl_chests.register_chest AGAIN + cleanup Now you can define custom groups for the chests, as shown in the trapped chests example (now they show up under "Mechanisms" tab in creative!). I'm a bit suspicious of the new return-wrapped functions, as in *they might break under some circumstances I didn't observe*. It will require some heavy testing to make sure nothing crashes in the future. Also, `on_rightclick` for double chests is something I really want to return-wrap as well, but failed to do so. :shrug: --- mods/ITEMS/mcl_chests/api.lua | 531 +++++++++++++++---------------- mods/ITEMS/mcl_chests/chests.lua | 1 + mods/ITEMS/mcl_chests/init.lua | 15 +- 3 files changed, 262 insertions(+), 285 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 42e0a901d..0eb313c25 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -10,11 +10,23 @@ local math = math local sf = string.format +-- Recursively merge tables with eachother +local function table_merge(tbl, ...) + local t = table.copy(tbl) + for k,v in pairs(...) do + if type(t[k]) == "table" and type(v) == "table" then + table_merge(t[k], v) + else + t[k] = v + end + end + return t +end + -- Chest Entity -- ============ --- This is necessary to show the chest as an animated mesh, as Minetest doesn't --- support assigning animated meshes to nodes directly. We're bypassing this --- limitation by giving each chest its own entity, and making the chest node +-- This is necessary to show the chest as an animated mesh, as Minetest doesn't support assigning animated meshes to +-- nodes directly. We're bypassing this limitation by giving each chest its own entity, and making the chest node -- itself fully transparent. local animated_chests = (minetest.settings:get_bool("animated_chests") ~= false) local entity_animations = { @@ -154,7 +166,8 @@ local function create_entity(pos, node_name, textures, param2, double, sound_pre if obj and obj:get_pos() then return obj:get_luaentity() else - minetest.log("warning", "[mcl_chests] Failed to create entity at " .. (entity_pos and minetest.pos_to_string(entity_pos, 1) or "nil")) + minetest.log("warning", "[mcl_chests] Failed to create entity at " .. + (entity_pos and minetest.pos_to_string(entity_pos, 1) or "nil")) end end mcl_chests.create_entity = create_entity @@ -209,9 +222,8 @@ local function player_chest_open(player, pos, node_name, textures, param2, doubl } if animated_chests then local dir = minetest.facedir_to_dir(param2) - find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, shulker and "shulker" or "chest", - dir): - open(name) + find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, + shulker and "shulker" or "chest", dir):open(name) end end mcl_chests.player_chest_open = player_chest_open @@ -228,7 +240,7 @@ local function protection_check_move(pos, from_list, from_index, to_list, to_ind end mcl_chests.protection_check_move = protection_check_move -local function protection_check_put_take(pos, listname, index, stack, player) +local function protection_check_take(pos, listname, index, stack, player) local name = player:get_player_name() if minetest.is_protected(pos, name) then minetest.record_protection_violation(pos, name) @@ -237,7 +249,29 @@ local function protection_check_put_take(pos, listname, index, stack, player) return stack:get_count() end end -mcl_chests.protection_check_put_take = protection_check_put_take +mcl_chests.protection_check_put_take = protection_check_take + +-- Logging functions +local function log_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) +end + +local function log_inventory_put(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) + -- BEGIN OF LISTRING WORKAROUND + if listname == "input" then + local inv = minetest.get_inventory({ type = "node", pos = pos }) + inv:add_item("main", stack) + end + -- END OF LISTRING WORKAROUND +end + +local function log_inventory_take(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " takes stuff from chest at " .. minetest.pos_to_string(pos)) +end -- To be called when a chest is closed (only relevant for trapped chest atm) local function chest_update_after_close(pos) @@ -337,7 +371,7 @@ local function limit_put_list(stack, list) end local function limit_put(stack, inv1, inv2) - local leftover = ItemStack(stack) + local leftover = ItemStack(staick) leftover = limit_put_list(leftover, inv1:get_list("main")) leftover = limit_put_list(leftover, inv2:get_list("main")) return stack:get_count() - leftover:get_count() @@ -353,6 +387,108 @@ local function close_forms(canonical_basename, pos) end end +-- Functions used in double chest registration code +-- ================================================ +-- The `return function` wrapping is necessary to avoid stacking up parameters. +-- `side` is either "left" or "right". +local function hopper_pull_double(side) return function(pos, hop_pos, hop_inv, hop_list) + local node = minetest.get_node(pos) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + + local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) + if stack_id ~= nil then + return inv_other, "main", stack_id + end + + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) + return inv, "main", stack_id +end end + +local function hopper_push_double(side) return function(pos, hop_pos, hop_inv, hop_list) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + + local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) + if stack_id ~= nil then + return inv, "main", stack_id + end + + local node = minetest.get_node(pos) + local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) + return inv_other, "main", stack_id +end end + +local function construct_double_chest(side, names) return function(pos) + local n = minetest.get_node(pos) + local param2 = n.param2 + local p = get_double_container_neighbor_pos(pos, param2, side) + -- Turn into a small chest if the neighbor is gone + if not p or minetest.get_node(p).name ~= names[side].cr then + n.name = names.small.a + minetest.swap_node(pos, n) + end +end end + +local function destruct_double_chest(side, names, canonical_basename, small_textures) return function(pos) + local n = minetest.get_node(pos) + if n.name == names.small.a then + return + end + + close_forms(canonical_basename, pos) + + local param2 = n.param2 + local p = get_double_container_neighbor_pos(pos, param2, side) + if not p or minetest.get_node(p).name ~= names[side].r then + return + end + close_forms(canonical_basename, p) + + minetest.swap_node(p, { name = names.small.a, param2 = param2 }) + create_entity(p, names.small.a, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") +end end + +-- Small chests use `protection_check_take` for both put and take actions. +local function protection_check_put(side) return function(pos, listname, index, stack, player) + local name = player:get_player_name() + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return 0 + -- BEGIN OF LISTRING WORKAROUND + elseif listname == "input" then + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + local inv = minetest.get_inventory({ type = "node", pos = pos }) + return limit_put(stack, other_inv, inv) + -- END OF LISTRING WORKAROUND + else + return stack:get_count() + end +end end + +local function log_inventory_put_double(side) return function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) + -- BEGIN OF LISTRING WORKAROUND + if listname == "input" then + local inv = minetest.get_inventory({ type = "node", pos = pos }) + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + + inv:set_stack("input", 1, nil) + + double_chest_add_item(inv, other_inv, "main", stack) + end + -- END OF LISTRING WORKAROUND +end end + -- This is a helper function to register regular chests (both small and double variants). -- Some parameters here are only utilized by trapped chests. function mcl_chests.register_chest(basename, d) @@ -364,20 +500,75 @@ function mcl_chests.register_chest(basename, d) else d.drop = "mcl_chests:" .. d.drop end + + local drop_items_chest = mcl_util.drop_items_from_meta_container("main") + + if not d.groups then d.groups = {} end + + local on_rightclick_side = { + left = d.on_rightclick_left, + right = d.on_rightclick_right + } + -- The basename of the "canonical" version of the node, if set (e.g.: trapped_chest_on → trapped_chest). -- Used to get a shared formspec ID and to swap the node back to the canonical version in on_construct. if not d.canonical_basename then d.canonical_basename = basename end - local drop_items_chest = mcl_util.drop_items_from_meta_container("main") + -- Names table + -- =========== + -- Accessed through names["kind"].x (names.kind.x), where x can be: + -- a = "actual" + -- c = canonical + -- r = reverse (only for double chests) + -- cr = canonical, reverse (only for double chests) + local names = { + small = { + a = "mcl_chests:" .. basename .. "_small", + c = "mcl_chests:" .. d.canonical_basename .. "_small", + }, + left = { + a = "mcl_chests:" .. basename .. "_left", + c = "mcl_chests:" .. d.canonical_basename .. "_left", + }, + right = { + a = "mcl_chests:" .. basename .. "_right", + c = "mcl_chests:" .. d.canonical_basename .. "_right", + }, + } + names.left.r = names.right.a + names.right.r = names.left.a + names.left.cr = names.right.c + names.right.cr = names.left.c - local small_name = "mcl_chests:" .. basename .. "_small" local small_textures = d.tiles.small - local left_name = "mcl_chests:" .. basename .. "_left" - local right_name = "mcl_chests:" .. basename .. "_right" local double_textures = d.tiles.double + -- Construct groups + local groups_inv = table_merge({ deco_block = 1 }, d.groups) + local groups_small = table_merge(groups_inv, { + handy = 1, + axey = 1, + container = 2, + deco_block = 1, + material_wood = 1, + flammable = -1, + chest_entity = 1, + not_in_creative_inventory = 1 + }, d.groups) + local groups_left = table_merge(groups_small, { + double_chest = 1 + }, d.groups) + local groups_right = table_merge(groups_small, { + -- In a double chest, the entity is assigned to the left side, but not the right one. + chest_entity = 0, + double_chest = 2 + }, d.groups) + + + + -- Register minetest.register_node("mcl_chests:" .. basename, { description = d.desc, _tt_help = d.tt_help, @@ -391,10 +582,10 @@ function mcl_chests.register_chest(basename, d) paramtype = "light", paramtype2 = "facedir", sounds = d.sounds, - groups = { deco_block = 1 }, + groups = groups_inv, on_construct = function(pos, node) local node = minetest.get_node(pos) - node.name = small_name + node.name = names.small.a minetest.set_node(pos, node) end, after_place_node = function(pos, placer, itemstack, pointed_thing) @@ -402,7 +593,7 @@ function mcl_chests.register_chest(basename, d) end, }) - minetest.register_node(small_name, { + minetest.register_node(names.small.a, { description = d.desc, _tt_help = d.tt_help, _doc_items_longdesc = d.longdesc, @@ -422,16 +613,7 @@ function mcl_chests.register_chest(basename, d) paramtype = "light", paramtype2 = "facedir", drop = d.drop, - groups = { - handy = 1, - axey = 1, - container = 2, - deco_block = 1, - material_wood = 1, - flammable = -1, - chest_entity = 1, - not_in_creative_inventory = 1 - }, + groups = groups_small, is_ground_content = false, sounds = d.sounds, on_construct = function(pos) @@ -465,22 +647,22 @@ function mcl_chests.register_chest(basename, d) -- Combine into a double chest if neighbouring another small chest if minetest.get_node(get_double_container_neighbor_pos(pos, param2, "right")).name == - small_name then - minetest.swap_node(pos, { name = right_name, param2 = param2 }) + names.small.a then + minetest.swap_node(pos, { name = names.right.a, param2 = param2 }) local p = get_double_container_neighbor_pos(pos, param2, "right") - minetest.swap_node(p, { name = left_name, param2 = param2 }) - create_entity(p, left_name, double_textures, param2, true, "default_chest", + minetest.swap_node(p, { name = names.left.a, param2 = param2 }) + create_entity(p, names.left.a, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") elseif minetest.get_node(get_double_container_neighbor_pos(pos, param2, "left")).name == - small_name then - minetest.swap_node(pos, { name = left_name, param2 = param2 }) - create_entity(pos, left_name, double_textures, param2, true, "default_chest", + names.small.a then + minetest.swap_node(pos, { name = names.left.a, param2 = param2 }) + create_entity(pos, names.left.a, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") local p = get_double_container_neighbor_pos(pos, param2, "left") - minetest.swap_node(p, { name = right_name, param2 = param2 }) + minetest.swap_node(p, { name = names.right.a, param2 = param2 }) else - minetest.swap_node(pos, { name = small_name, param2 = param2 }) - create_entity(pos, small_name, small_textures, param2, false, "default_chest", + minetest.swap_node(pos, { name = names.small.a, param2 = param2 }) + create_entity(pos, names.small.a, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") end end, @@ -490,26 +672,11 @@ function mcl_chests.register_chest(basename, d) after_dig_node = drop_items_chest, on_blast = on_chest_blast, allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = protection_check_put_take, - on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - minetest.log("action", player:get_player_name() .. - " moves stuff in chest at " .. minetest.pos_to_string(pos)) - end, - on_metadata_inventory_put = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " moves stuff to chest at " .. minetest.pos_to_string(pos)) - -- BEGIN OF LISTRING WORKAROUND - if listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - inv:add_item("main", stack) - end - -- END OF LISTRING WORKAROUND - end, - on_metadata_inventory_take = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " takes stuff from chest at " .. minetest.pos_to_string(pos)) - end, + allow_metadata_inventory_take = protection_check_take, + allow_metadata_inventory_put = protection_check_take, + on_metadata_inventory_move = log_inventory_move, + on_metadata_inventory_put = log_inventory_put, + on_metadata_inventory_take = log_inventory_take, _mcl_blast_resistance = d.hardness, _mcl_hardness = d.hardness, @@ -550,7 +717,7 @@ function mcl_chests.register_chest(basename, d) d.on_rightclick(pos, node, clicker) end - player_chest_open(clicker, pos, small_name, small_textures, node.param2, false, "default_chest", + player_chest_open(clicker, pos, names.small.a, small_textures, node.param2, false, "default_chest", "mcl_chests_chest") end, @@ -561,7 +728,7 @@ function mcl_chests.register_chest(basename, d) on_rotate = simple_rotate, }) - minetest.register_node(left_name, { + minetest.register_node(names.left.a, { drawtype = "nodebox", node_box = { type = "fixed", @@ -575,93 +742,23 @@ function mcl_chests.register_chest(basename, d) _chest_entity_animation_type = "chest", paramtype = "light", paramtype2 = "facedir", - groups = { - handy = 1, - axey = 1, - container = 2, - not_in_creative_inventory = 1, - material_wood = 1, - flammable = -1, - chest_entity = 1, - double_chest = 1 - }, + groups = groups_left, drop = d.drop, is_ground_content = false, sounds = d.sounds, - on_construct = function(pos) - local n = minetest.get_node(pos) - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "left") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. d.canonical_basename .. "_right" then - n.name = "mcl_chests:" .. d.canonical_basename .. "_small" - minetest.swap_node(pos, n) - end - create_entity(pos, left_name, double_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") - end, + on_construct = construct_double_chest("left", names), after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) end, - on_destruct = function(pos) - local n = minetest.get_node(pos) - if n.name == small_name then - return - end - - close_forms(d.canonical_basename, pos) - - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "left") - if not p or minetest.get_node(p).name ~= "mcl_chests:" .. basename .. "_right" then - return - end - close_forms(d.canonical_basename, p) - - minetest.swap_node(p, { name = small_name, param2 = param2 }) - create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") - end, + on_destruct = destruct_double_chest("left", names, d.canonical_basename, small_textures), after_dig_node = drop_items_chest, on_blast = on_chest_blast, allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - -- BEGIN OF LISTRING WORKAROUND - elseif listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - return limit_put(stack, inv, other_inv) - -- END OF LISTRING WORKAROUND - else - return stack:get_count() - end - end, - on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - minetest.log("action", player:get_player_name() .. - " moves stuff in chest at " .. minetest.pos_to_string(pos)) - end, - on_metadata_inventory_put = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " moves stuff to chest at " .. minetest.pos_to_string(pos)) - -- BEGIN OF LISTRING WORKAROUND - if listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - - inv:set_stack("input", 1, nil) - - double_chest_add_item(inv, other_inv, "main", stack) - end - -- END OF LISTRING WORKAROUND - end, - on_metadata_inventory_take = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " takes stuff from chest at " .. minetest.pos_to_string(pos)) - end, + allow_metadata_inventory_take = protection_check_take, + allow_metadata_inventory_put = protection_check_put("left"), + on_metadata_inventory_move = log_inventory_move, + on_metadata_inventory_put = log_inventory_put_double("left"), + on_metadata_inventory_take = log_inventory_take, _mcl_blast_resistance = d.hardness, _mcl_hardness = d.hardness, @@ -721,46 +818,16 @@ function mcl_chests.register_chest(basename, d) d.on_rightclick_left(pos, node, clicker) end - player_chest_open(clicker, pos, left_name, double_textures, node.param2, true, "default_chest", + player_chest_open(clicker, pos, names.left.a, double_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, mesecons = d.mesecons, on_rotate = no_rotate, - _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - - local stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) - if stack_id ~= nil then - return inv, "main", stack_id - end - - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) - return inv_other, "main", stack_id - end, - _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - - local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) - if stack_id ~= nil then - return inv, "main", stack_id - end - - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) - return inv_other, "main", stack_id - end, + _mcl_hoppers_on_try_pull = hopper_pull_double("left"), + _mcl_hoppers_on_try_push = hopper_push_double("left"), }) - minetest.register_node(right_name, { + minetest.register_node(names.right.a, { drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", @@ -770,91 +837,23 @@ function mcl_chests.register_chest(basename, d) }, tiles = { "blank.png^[resize:16x16" }, use_texture_alpha = "clip", - groups = { - handy = 1, - axey = 1, - container = 2, - not_in_creative_inventory = 1, - material_wood = 1, - flammable = -1, - double_chest = 2 - }, + groups = groups_right, drop = d.drop, is_ground_content = false, sounds = d.sounds, - on_construct = function(pos) - local n = minetest.get_node(pos) - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "right") - if not p or minetest.get_node(p).name ~= left_name then - n.name = small_name - minetest.swap_node(pos, n) - end - end, + on_construct = construct_double_chest("right", names), after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) end, - on_destruct = function(pos) - local n = minetest.get_node(pos) - if n.name == small_name then - return - end - - close_forms(d.canonical_basename, pos) - - local param2 = n.param2 - local p = get_double_container_neighbor_pos(pos, param2, "right") - if not p or minetest.get_node(p).name ~= left_name then - return - end - close_forms(d.canonical_basename, p) - - minetest.swap_node(p, { name = small_name, param2 = param2 }) - create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") - end, + on_destruct = destruct_double_chest("right", names, d.canonical_basename, small_textures), after_dig_node = drop_items_chest, on_blast = on_chest_blast, allow_metadata_inventory_move = protection_check_move, - allow_metadata_inventory_take = protection_check_put_take, - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local name = player:get_player_name() - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return 0 - -- BEGIN OF LISTRING WORKAROUND - elseif listname == "input" then - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - local inv = minetest.get_inventory({ type = "node", pos = pos }) - return limit_put(stack, other_inv, inv) - -- END OF LISTRING WORKAROUND - else - return stack:get_count() - end - end, - on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - minetest.log("action", player:get_player_name() .. - " moves stuff in chest at " .. minetest.pos_to_string(pos)) - end, - on_metadata_inventory_put = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " moves stuff to chest at " .. minetest.pos_to_string(pos)) - -- BEGIN OF LISTRING WORKAROUND - if listname == "input" then - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - local inv = minetest.get_inventory({ type = "node", pos = pos }) - - inv:set_stack("input", 1, nil) - - double_chest_add_item(other_inv, inv, "main", stack) - end - -- END OF LISTRING WORKAROUND - end, - on_metadata_inventory_take = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name() .. - " takes stuff from chest at " .. minetest.pos_to_string(pos)) - end, + allow_metadata_inventory_take = protection_check_take, + allow_metadata_inventory_put = protection_check_put("right"), + on_metadata_inventory_move = log_inventory_move, + on_metadata_inventory_put = log_inventory_put_double("right"), + on_metadata_inventory_take = log_inventory_take, _mcl_blast_resistance = d.hardness, _mcl_hardness = d.hardness, @@ -919,43 +918,13 @@ function mcl_chests.register_chest(basename, d) end, mesecons = d.mesecons, on_rotate = no_rotate, - _mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list) - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - - local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) - if stack_id ~= nil then - return inv_other, "main", stack_id - end - - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) - return inv, "main", stack_id - end, - _mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list) - local node = minetest.get_node(pos) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - - local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) - if stack_id ~= nil then - return inv_other, "main", stack_id - end - - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) - return inv, "main", stack_id - end, + _mcl_hoppers_on_try_pull = hopper_pull_double("right"), + _mcl_hoppers_on_try_push = hopper_push_double("right"), }) if doc then - doc.add_entry_alias("nodes", small_name, "nodes", left_name) - doc.add_entry_alias("nodes", small_name, "nodes", right_name) + doc.add_entry_alias("nodes", names.small.a, "nodes", names.left.a) + doc.add_entry_alias("nodes", names.small.a, "nodes", names.right.a) end end diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 0215dc825..11e0e2942 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -42,6 +42,7 @@ mcl_chests.register_chest("trapped_chest", { tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), tiles = traptiles, + groups = { mesecon = 2 }, sounds = mcl_sounds.node_sound_wood_defaults(), hardness = 2.5, hidden = false, diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 3ca1cf4da..741a3a523 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -78,9 +78,13 @@ minetest.register_lbm({ minetest.register_lbm({ label = "Replace old chest nodes", name = "mcl_chests:replace_old", - nodenames = { "mcl_chests:chest", "mcl_chests:trapped_chest", "mcl_chests:trapped_chest_on", + nodenames = { + "mcl_chests:chest", + "mcl_chests:trapped_chest", + "mcl_chests:trapped_chest_on", "mcl_chests:ender_chest", - "group:old_shulker_box_node" }, + "group:old_shulker_box_node" + }, run_at_every_load = true, action = function(pos, node) local node_name = node.name @@ -102,8 +106,11 @@ minetest.register_lbm({ minetest.register_lbm({ label = "Disable active trapped chests", name = "mcl_chests:reset_trapped_chests", - nodenames = { "mcl_chests:trapped_chest_on_small", "mcl_chests:trapped_chest_on_left", - "mcl_chests:trapped_chest_on_right" }, + nodenames = { + "mcl_chests:trapped_chest_on_small", + "mcl_chests:trapped_chest_on_left", + "mcl_chests:trapped_chest_on_right" + }, run_at_every_load = true, action = function(pos, node) minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos)) From b4b5bf8391fbce6091d46f2f911bc339559f2269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Sat, 22 Jun 2024 17:05:08 +0700 Subject: [PATCH 10/25] =?UTF-8?q?Move=20some=20groups=20(api.lua=20?= =?UTF-8?q?=E2=86=92=20chests.lua)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mods/ITEMS/mcl_chests/api.lua | 4 ---- mods/ITEMS/mcl_chests/chests.lua | 21 ++++++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 0eb313c25..fb6142f85 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -548,12 +548,8 @@ function mcl_chests.register_chest(basename, d) -- Construct groups local groups_inv = table_merge({ deco_block = 1 }, d.groups) local groups_small = table_merge(groups_inv, { - handy = 1, - axey = 1, container = 2, deco_block = 1, - material_wood = 1, - flammable = -1, chest_entity = 1, not_in_creative_inventory = 1 }, d.groups) diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 11e0e2942..e0dc3b57a 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -21,6 +21,12 @@ mcl_chests.register_chest("chest", { "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "mcl_chests_chest_back.png", "default_chest_front.png" }, }, + groups = { + handy = 1, + axey = 1, + material_wood = 1, + flammable = -1, + }, sounds = mcl_sounds.node_sound_wood_defaults(), hardness = 2.5, hidden = false, @@ -42,7 +48,13 @@ mcl_chests.register_chest("trapped_chest", { tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"), tiles = traptiles, - groups = { mesecon = 2 }, + groups = { + handy = 1, + axey = 1, + material_wood = 1, + flammable = -1, + mesecon = 2, + }, sounds = mcl_sounds.node_sound_wood_defaults(), hardness = 2.5, hidden = false, @@ -93,6 +105,13 @@ mcl_chests.register_chest("trapped_chest_on", { usagehelp = nil, tt_help = nil, tiles = traptiles, + groups = { + handy = 1, + axey = 1, + material_wood = 1, + flammable = -1, + mesecon = 2, + }, sounds = mcl_sounds.node_sound_wood_defaults(), hardness = 2.5, hidden = true, From 6bbb6b8decbaafb93632e99ed3e3729e852249fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Sat, 22 Jun 2024 21:37:32 +0700 Subject: [PATCH 11/25] Add title field for mcl_chests.register_chest --- mods/ITEMS/mcl_chests/api.lua | 11 +++++--- mods/ITEMS/mcl_chests/chests.lua | 8 ++++++ mods/ITEMS/mcl_chests/example.lua | 44 +++++++++++++++++++++++++++++++ mods/ITEMS/mcl_chests/init.lua | 1 + 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 mods/ITEMS/mcl_chests/example.lua diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index fb6142f85..aaea8eb21 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -495,6 +495,11 @@ function mcl_chests.register_chest(basename, d) -- If this passes without crash, we know for a fact that d = {...} assert((d and type(d) == "table"), "Second argument to mcl_chests.register_chest must be a table") + -- Fallback for when there is no `title` field + if not d.title then d.title = {} end + d.title.small = d.title.small or d.desc + d.title.double = d.title.double or ("Large " .. d.title.small) + if not d.drop then d.drop = "mcl_chests:" .. basename else @@ -686,7 +691,7 @@ function mcl_chests.register_chest(basename, d) end local name = minetest.get_meta(pos):get_string("name") if name == "" then - name = S("Chest") + name = d.title.small end minetest.show_formspec(clicker:get_player_name(), @@ -777,7 +782,7 @@ function mcl_chests.register_chest(basename, d) if name == "" then -- if empty after that ^ name = minetest.get_meta(pos_other):get_string("name") end if name == "" then -- if STILL empty after that ^ - name = S("Large Chest") + name = d.title.double end minetest.show_formspec(clicker:get_player_name(), @@ -872,7 +877,7 @@ function mcl_chests.register_chest(basename, d) if name == "" then -- if empty after that ^ name = minetest.get_meta(pos_other):get_string("name") end if name == "" then -- if STILL empty after that ^ - name = S("Large Chest") + name = d.title.double end minetest.show_formspec(clicker:get_player_name(), diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index e0dc3b57a..935872707 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -39,6 +39,10 @@ local traptiles = { mcl_chests.register_chest("trapped_chest", { desc = S("Trapped Chest"), + title = { + small = S("Chest"), + double = S("Large Chest") + }, longdesc = S( "A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone " .. "signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped " .. @@ -101,6 +105,10 @@ mcl_chests.register_chest("trapped_chest", { mcl_chests.register_chest("trapped_chest_on", { desc = nil, + title = { + small = S("Chest"), + double = S("Large Chest") + }, longdesc = nil, usagehelp = nil, tt_help = nil, diff --git a/mods/ITEMS/mcl_chests/example.lua b/mods/ITEMS/mcl_chests/example.lua new file mode 100644 index 000000000..135432ff1 --- /dev/null +++ b/mods/ITEMS/mcl_chests/example.lua @@ -0,0 +1,44 @@ +local S = minetest.get_translator(minetest.get_current_modname()) +local F = minetest.formspec_escape +local C = minetest.colorize +local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos +local trapped_chest_mesecons_rules = mesecon.rules.pplate + +mcl_chests.register_chest("stone_chest", { + desc = S("Stone Chest"), + large_desc = S("Large Stone Chest"), + longdesc = S( + "Stone Chests are containers which provide 27 inventory slots. Stone Chests can be turned into" .. + "large stone chests with double the capacity by placing two stone chests next to each other." + ), + usagehelp = S("To access its inventory, rightclick it. When broken, the items will drop out."), + tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large stone chest"), + tiles = { + small = { mcl_chests.tiles.chest_normal_small[1] .. "^[hsl:-15:-80:-20" }, + double = { mcl_chests.tiles.chest_normal_double[1] .. "^[hsl:-15:-80:-20" }, + inv = { "default_chest_top.png^[hsl:-15:-80:-20", + "mcl_chests_chest_bottom.png^[hsl:-15:-80:-20", + "mcl_chests_chest_right.png^[hsl:-15:-80:-20", + "mcl_chests_chest_left.png^[hsl:-15:-80:-20", + "mcl_chests_chest_back.png^[hsl:-15:-80:-20", + "default_chest_front.png^[hsl:-15:-80:-20" + }, + }, + groups = { + pickaxey = 1, + stone = 1, + material_stone = 1, + }, + sounds = mcl_sounds.node_sound_stone_defaults(), + hardness = 4.0, + hidden = false, +}) + +minetest.register_craft({ + output = "mcl_chests:stone_chest", + recipe = { + { "mcl_core:stone", "mcl_core:stone", "mcl_core:stone" }, + { "mcl_core:stone", "", "mcl_core:stone" }, + { "mcl_core:stone", "mcl_core:stone", "mcl_core:stone" }, + }, +}) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 741a3a523..bea6b9496 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -41,6 +41,7 @@ dofile(modpath .. "/api.lua") dofile(modpath .. "/chests.lua") dofile(modpath .. "/ender.lua") dofile(modpath .. "/shulkers.lua") +--dofile(modpath .. "/example.lua") From a28e55160f2cc8c462c01ae6245372406bf57dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Sun, 23 Jun 2024 21:50:03 +0700 Subject: [PATCH 12/25] Make the chest opening/closing sound customizable + fix double chests --- mods/ITEMS/mcl_chests/api.lua | 63 +++++++++++++++++++++---------- mods/ITEMS/mcl_chests/chests.lua | 6 +-- mods/ITEMS/mcl_chests/example.lua | 6 ++- 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index aaea8eb21..0018c2f74 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -463,10 +463,14 @@ local function protection_check_put(side) return function(pos, listname, index, return 0 -- BEGIN OF LISTRING WORKAROUND elseif listname == "input" then + local inv = minetest.get_inventory({ type = "node", pos = pos }) local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - local inv = minetest.get_inventory({ type = "node", pos = pos }) - return limit_put(stack, other_inv, inv) + if side == "left" then + return limit_put(stack, inv, other_inv) + else + return limit_put(stack, other_inv, inv) + end -- END OF LISTRING WORKAROUND else return stack:get_count() @@ -484,7 +488,11 @@ local function log_inventory_put_double(side) return function(pos, listname, ind inv:set_stack("input", 1, nil) - double_chest_add_item(inv, other_inv, "main", stack) + if side == "left" then + double_chest_add_item(inv, other_inv, "main", stack) + else + double_chest_add_item(other_inv, inv, "main", stack) + end end -- END OF LISTRING WORKAROUND end end @@ -510,10 +518,24 @@ function mcl_chests.register_chest(basename, d) if not d.groups then d.groups = {} end - local on_rightclick_side = { - left = d.on_rightclick_left, - right = d.on_rightclick_right - } + if not d.on_rightclick_left then + d.on_rightclick_left = d.on_rightclick + end + if not d.on_rightclick_right then + d.on_rightclick_right = d.on_rightclick + end + --[[local on_rightclick_side = { + left = d.on_rightclick_left or d.on_rightclick, + right = d.on_rightclick_right or d.on_rightclick, + }]] + + if not d.sounds or type(d.sounds) ~= "table" then + d.sounds = { nil, "default_chest" } + end + + if not d.sounds[2] then + d.sounds[2] = "default_chest" + end -- The basename of the "canonical" version of the node, if set (e.g.: trapped_chest_on → trapped_chest). -- Used to get a shared formspec ID and to swap the node back to the canonical version in on_construct. @@ -569,7 +591,8 @@ function mcl_chests.register_chest(basename, d) - -- Register + -- Dummy inventory node + -- Will turn into names.small.a when placed down minetest.register_node("mcl_chests:" .. basename, { description = d.desc, _tt_help = d.tt_help, @@ -582,7 +605,7 @@ function mcl_chests.register_chest(basename, d) use_texture_alpha = "opaque", paramtype = "light", paramtype2 = "facedir", - sounds = d.sounds, + sounds = d.sounds[1], groups = groups_inv, on_construct = function(pos, node) local node = minetest.get_node(pos) @@ -608,7 +631,7 @@ function mcl_chests.register_chest(basename, d) tiles = { "blank.png^[resize:16x16" }, use_texture_alpha = "clip", _chest_entity_textures = small_textures, - _chest_entity_sound = "default_chest", + _chest_entity_sound = d.sounds[2], _chest_entity_mesh = "mcl_chests_chest", _chest_entity_animation_type = "chest", paramtype = "light", @@ -616,7 +639,7 @@ function mcl_chests.register_chest(basename, d) drop = d.drop, groups = groups_small, is_ground_content = false, - sounds = d.sounds, + sounds = d.sounds[1], on_construct = function(pos) local param2 = minetest.get_node(pos).param2 local meta = minetest.get_meta(pos) @@ -652,18 +675,18 @@ function mcl_chests.register_chest(basename, d) minetest.swap_node(pos, { name = names.right.a, param2 = param2 }) local p = get_double_container_neighbor_pos(pos, param2, "right") minetest.swap_node(p, { name = names.left.a, param2 = param2 }) - create_entity(p, names.left.a, double_textures, param2, true, "default_chest", + create_entity(p, names.left.a, double_textures, param2, true, d.sounds[2], "mcl_chests_chest", "chest") elseif minetest.get_node(get_double_container_neighbor_pos(pos, param2, "left")).name == names.small.a then minetest.swap_node(pos, { name = names.left.a, param2 = param2 }) - create_entity(pos, names.left.a, double_textures, param2, true, "default_chest", + create_entity(pos, names.left.a, double_textures, param2, true, d.sounds[2], "mcl_chests_chest", "chest") local p = get_double_container_neighbor_pos(pos, param2, "left") minetest.swap_node(p, { name = names.right.a, param2 = param2 }) else minetest.swap_node(pos, { name = names.small.a, param2 = param2 }) - create_entity(pos, names.small.a, small_textures, param2, false, "default_chest", + create_entity(pos, names.small.a, small_textures, param2, false, d.sounds[2], "mcl_chests_chest", "chest") end end, @@ -718,7 +741,7 @@ function mcl_chests.register_chest(basename, d) d.on_rightclick(pos, node, clicker) end - player_chest_open(clicker, pos, names.small.a, small_textures, node.param2, false, "default_chest", + player_chest_open(clicker, pos, names.small.a, small_textures, node.param2, false, d.sounds[2], "mcl_chests_chest") end, @@ -738,7 +761,7 @@ function mcl_chests.register_chest(basename, d) tiles = { "blank.png^[resize:16x16" }, use_texture_alpha = "clip", _chest_entity_textures = double_textures, - _chest_entity_sound = "default_chest", + _chest_entity_sound = d.sounds[2], _chest_entity_mesh = "mcl_chests_chest", _chest_entity_animation_type = "chest", paramtype = "light", @@ -746,7 +769,7 @@ function mcl_chests.register_chest(basename, d) groups = groups_left, drop = d.drop, is_ground_content = false, - sounds = d.sounds, + sounds = d.sounds[1], on_construct = construct_double_chest("left", names), after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) @@ -819,7 +842,7 @@ function mcl_chests.register_chest(basename, d) d.on_rightclick_left(pos, node, clicker) end - player_chest_open(clicker, pos, names.left.a, double_textures, node.param2, true, "default_chest", + player_chest_open(clicker, pos, names.left.a, double_textures, node.param2, true, d.sounds[2], "mcl_chests_chest") end, mesecons = d.mesecons, @@ -841,7 +864,7 @@ function mcl_chests.register_chest(basename, d) groups = groups_right, drop = d.drop, is_ground_content = false, - sounds = d.sounds, + sounds = d.sounds[1], on_construct = construct_double_chest("right", names), after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) @@ -914,7 +937,7 @@ function mcl_chests.register_chest(basename, d) d.on_rightclick_right(pos, node, clicker) end - player_chest_open(clicker, pos_other, left_name, double_textures, node.param2, true, "default_chest", + player_chest_open(clicker, pos_other, names.left.a, double_textures, node.param2, true, d.sounds[2], "mcl_chests_chest") end, mesecons = d.mesecons, diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 935872707..95a6a765a 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -27,7 +27,7 @@ mcl_chests.register_chest("chest", { material_wood = 1, flammable = -1, }, - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = { mcl_sounds.node_sound_wood_defaults() }, hardness = 2.5, hidden = false, }) @@ -59,7 +59,7 @@ mcl_chests.register_chest("trapped_chest", { flammable = -1, mesecon = 2, }, - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = { mcl_sounds.node_sound_wood_defaults() }, hardness = 2.5, hidden = false, mesecons = { @@ -120,7 +120,7 @@ mcl_chests.register_chest("trapped_chest_on", { flammable = -1, mesecon = 2, }, - sounds = mcl_sounds.node_sound_wood_defaults(), + sounds = { mcl_sounds.node_sound_wood_defaults() }, hardness = 2.5, hidden = true, mesecons = { diff --git a/mods/ITEMS/mcl_chests/example.lua b/mods/ITEMS/mcl_chests/example.lua index 135432ff1..eb9e15849 100644 --- a/mods/ITEMS/mcl_chests/example.lua +++ b/mods/ITEMS/mcl_chests/example.lua @@ -29,9 +29,13 @@ mcl_chests.register_chest("stone_chest", { stone = 1, material_stone = 1, }, - sounds = mcl_sounds.node_sound_stone_defaults(), + sounds = { mcl_sounds.node_sound_stone_defaults() }, hardness = 4.0, hidden = false, + -- It bites! + on_rightclick = function(pos, node, clicker) + mcl_util.deal_damage(clicker, 2) + end, }) minetest.register_craft({ From a66c35a9ea1058aa4d82b2b46f3b60b7ecb2b920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Mon, 24 Jun 2024 10:32:20 +0700 Subject: [PATCH 13/25] Fix double chests once more (hoppers this time) --- mods/ITEMS/mcl_chests/api.lua | 58 +++++++++++++++++++++---------- mods/ITEMS/mcl_chests/example.lua | 5 ++- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 0018c2f74..0de631afe 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -393,36 +393,56 @@ end -- `side` is either "left" or "right". local function hopper_pull_double(side) return function(pos, hop_pos, hop_inv, hop_list) local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) local meta_other = minetest.get_meta(pos_other) local inv_other = meta_other:get_inventory() - local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list) - if stack_id ~= nil then - return inv_other, "main", stack_id + local ret_inv1, ret_inv2 + if side == "left" then + ret_inv1 = inv + ret_inv2 = inv_other + else + ret_inv1 = inv_other + ret_inv2 = inv end - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list) - return inv, "main", stack_id + local stack_id = mcl_util.select_stack(ret_inv1, "main", hop_inv, hop_list) + if stack_id ~= nil then + return ret_inv1, "main", stack_id + end + + stack_id = mcl_util.select_stack(ret_inv2, "main", hop_inv, hop_list) + return ret_inv2, "main", stack_id end end local function hopper_push_double(side) return function(pos, hop_pos, hop_inv, hop_list) + local node = minetest.get_node(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() - local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main", nil, 1) - if stack_id ~= nil then - return inv, "main", stack_id - end - - local node = minetest.get_node(pos) local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) local meta_other = minetest.get_meta(pos_other) local inv_other = meta_other:get_inventory() - stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main", nil, 1) - return inv_other, "main", stack_id + + local ret_inv1, ret_inv2 + if side == "left" then + ret_inv1 = inv + ret_inv2 = inv_other + else + ret_inv1 = inv_other + ret_inv2 = inv + end + + local stack_id = mcl_util.select_stack(hop_inv, hop_list, ret_inv1, "main", nil, 1) + if stack_id ~= nil then + return ret_inv1, "main", stack_id + end + + stack_id = mcl_util.select_stack(hop_inv, hop_list, ret_inv2, "main", nil, 1) + return ret_inv2, "main", stack_id end end local function construct_double_chest(side, names) return function(pos) @@ -436,7 +456,7 @@ local function construct_double_chest(side, names) return function(pos) end end end -local function destruct_double_chest(side, names, canonical_basename, small_textures) return function(pos) +local function destruct_double_chest(side, names, canonical_basename, small_textures, sound_prefix) return function(pos) local n = minetest.get_node(pos) if n.name == names.small.a then return @@ -452,7 +472,7 @@ local function destruct_double_chest(side, names, canonical_basename, small_text close_forms(canonical_basename, p) minetest.swap_node(p, { name = names.small.a, param2 = param2 }) - create_entity(p, names.small.a, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") + create_entity(p, names.small.a, small_textures, param2, false, sound_prefix, "mcl_chests_chest", "chest") end end -- Small chests use `protection_check_take` for both put and take actions. @@ -774,7 +794,7 @@ function mcl_chests.register_chest(basename, d) after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) end, - on_destruct = destruct_double_chest("left", names, d.canonical_basename, small_textures), + on_destruct = destruct_double_chest("left", names, d.canonical_basename, small_textures, d.sounds[2]), after_dig_node = drop_items_chest, on_blast = on_chest_blast, allow_metadata_inventory_move = protection_check_move, @@ -869,7 +889,7 @@ function mcl_chests.register_chest(basename, d) after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) end, - on_destruct = destruct_double_chest("right", names, d.canonical_basename, small_textures), + on_destruct = destruct_double_chest("right", names, d.canonical_basename, small_textures, d.sounds[2]), after_dig_node = drop_items_chest, on_blast = on_chest_blast, allow_metadata_inventory_move = protection_check_move, diff --git a/mods/ITEMS/mcl_chests/example.lua b/mods/ITEMS/mcl_chests/example.lua index eb9e15849..2fc1e377e 100644 --- a/mods/ITEMS/mcl_chests/example.lua +++ b/mods/ITEMS/mcl_chests/example.lua @@ -29,7 +29,10 @@ mcl_chests.register_chest("stone_chest", { stone = 1, material_stone = 1, }, - sounds = { mcl_sounds.node_sound_stone_defaults() }, + sounds = { + mcl_sounds.node_sound_stone_defaults(), + "mcl_chests_enderchest" + }, hardness = 4.0, hidden = false, -- It bites! From 49b6d099850d29f205585a3dc1cad72f5c3b83e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Mon, 24 Jun 2024 12:42:03 +0700 Subject: [PATCH 14/25] Add documentation (README.md, API.md) --- mods/ITEMS/mcl_chests/API.md | 209 ++++++++++++++++++++++++++++++ mods/ITEMS/mcl_chests/README.md | 19 +++ mods/ITEMS/mcl_chests/chests.lua | 15 +-- mods/ITEMS/mcl_chests/example.lua | 5 +- 4 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 mods/ITEMS/mcl_chests/API.md create mode 100644 mods/ITEMS/mcl_chests/README.md diff --git a/mods/ITEMS/mcl_chests/API.md b/mods/ITEMS/mcl_chests/API.md new file mode 100644 index 000000000..4eba2ee51 --- /dev/null +++ b/mods/ITEMS/mcl_chests/API.md @@ -0,0 +1,209 @@ +# `mcl_chests` API + +## `mcl_chests.register_chest(basename, definition)` + +This function allows for simple chest registration, used by both regular and +trapped chests. + +* `basename` is a string that will be concatenated to form full nodenames for + chests, for example `"mcl_chests:basename_small"`. +* `definition` is a key-value table, with following fields: + +```lua +{ + desc = S("Stone Chest"), + -- Equivalent to `description` field of Item/Node definition. + -- Will be shown as chest's name in the inventory. + + title = { + small = S("Stone Chest") -- the same as `desc` if not specified + double = S("Large Stone Chest") -- defaults to `"Large " .. desc` + } + -- These will be shown when opening the chest (in formspecs). + + longdesc = S( + "Stone Chests are containers which provide 27 inventory slots. Stone Chests can be turned into" .. + "large stone chests with double the capacity by placing two stone chests next to each other." + ), + usagehelp = S("To access its inventory, rightclick it. When broken, the items will drop out."), + tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large stone chest"), + -- Equivalent to `_doc_items_longdesc`, `_doc_items_usagehelp` and + -- `_tt_help` fields of Item/Node definition. Shown in the tooltip and wiki. + + tiles = { + small = { "vl_stone_chests_small.png" }, + double = { "vl_stone_chests_double.png" }, + inv = { + "vl_stone_chests_top.png", + "vl_stone_chests_bottom.png", + "vl_stone_chests_right.png", + "vl_stone_chests_left.png", + "vl_stone_chests_back.png", + "vl_stone_chests_front.png" + }, + }, + -- `small` and `double` fields contain the textures that will be applied to + -- chest entities. + -- `inv` field contains table of textures (6 in total, for each cube side), + -- that will be used to render the chest "node" in the inventory. + + groups = { + pickaxey = 1, + stone = 1, + material_stone = 1, + }, + -- Equivalent to `groups` field of Item/Node definition. There is some table + -- merging occuring internally, but it is purely for entity rendering. + + sounds = { + mcl_sounds.node_sound_stone_defaults(), -- defaults to `nil` + "vl_stone_chests_sound" -- defaults to `"default_chest"` + }, + -- First value is equivalent to `sounds` field of Item/Node definition. + -- Second value is a sound prefix, from which the actual sounds will be + -- concatenated (e.g. `vl_stone_chests_sound_open.ogg`). See `api.lua`. + + hardness = 4.0, + -- Equivalent to `_mcl_blast_resistance` and `_mcl_hardness` fields of + -- Item/Node definition. They are always equal for chests. + + hidden = false, + -- Equivalent to `_doc_items_hidden` field of Item/Node definition. + + mesecons = { + receptor = { + state = mesecon.state.on, + rules = mesecon.rules.pplate, + }, + }, + -- Equivalent to `mesecons` field of Item/Node definition. + + on_rightclick = function(pos, node, clicker) + mcl_util.deal_damage(clicker, 2) + end, + -- If provided, will be executed at the end of the actual `on_rightclick` + -- function of the chest node. + -- If `on_rightclick_left` or `on_rightclick_right` are not provided, this + -- will also be what is executed for left and right double chest nodes, + -- respectively. + + drop = "chest", + -- If provided, the chest will not drop itself, but the item of the chest + -- with that basename. + + canonical_basename = "chest", + -- If provided, the chest will turn into chest with that basename in + -- `on_construct`. +} +``` + +For usage examples, see `chests.lua` and `example.lua`. + + +## `mcl_chests.create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)` + +This function creates a chest entity based on parameters: + +* `pos` is the position vector. +* `node_name` is a string used in initialization data for the entity. +* `textures` is the entity textures. +* `param2` is a node param2, which then will be converted to entity direction. +* `double` is a boolean value for whether the chest is double or not. +* `sound_prefix` is a string, from which the actual sounds for the entity will + be concatenated. +* `mesh_prefix` is the same thing as `sound_prefix`, but for meshes. +* `animation_type` is a string that will be used in `set_animation` method of + chest entity. +* `dir` and `entity_pos` are number and vector values used to get entity info. + +Returned value is either a luaentity, or `nil` if failed (in which case a +warning message gets written into the console). + + +## `find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)` + +This function finds an existing entity, or creates one if failed. Parameters: + +* `pos` is the position vector. +* `node_name` is a string used in initialization data for the entity. +* `textures` is the entity textures. +* `param2` is a node param2, which then will be converted to entity direction. +* `double` is a boolean value for whether the chest is double or not. +* `sound_prefix` is a string, from which the actual sounds for the entity will + be concatenated. +* `mesh_prefix` is the same thing as `sound_prefix`, but for meshes. +* `animation_type` is a string that will be used in `set_animation` method of + chest entity. +* `dir` and `entity_pos` are number and vector values used to get entity info. + +Returned value is either a luaentity, or `nil` if failed (in which case a +warning message gets written into the console). + + +## `mcl_chests.no_rotate` + +This function is equivalent to `screwdriver.disallow` and is used when a chest +can't be rotated, and is applied in `on_rotate` field of Node definition. + + +## `mcl_chests.simple_rotate(pos, node, user, mode, new_param2)` + +This function allows for simple rotation with the entity being affected as well, +and is applied in `on_rotate` field of Node definition. + + +## `mcl_chests.open_chests` + +This table contains all currently open chests, indexed by player name. + +`nil` if player is not using a chest, and `{ pos = }` +otherwise (where position is a vector value). + + +## `mcl_chests.protection_check_move(pos, from_list, from_index, to_list, to_index, count, player)` + +This function is called in `allow_metadata_inventory_move` field of Node +definition. + +## `mcl_chests.protection_check_put_take(pos, listname, index, stack, player)` + +This function is called in `allow_metadata_inventory_put` and +`allow_metadata_inventory_take` fields of Node definition. + + +## `mcl_chests.player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker)` + +This function opens a chest based on parameters: + +* `player` is an ObjectRef. +* `pos` is the position vector. +* `node_name` is a string used in initialization data for the entity. +* `textures` is the entity textures. +* `param2` is a node param2, which then will be converted to entity direction. +* `double` is a boolean value for whether the chest is double or not. +* `sound` is a prefix string, from which the actual sounds for the entity will + be concatenated. +* `mesh` is the same thing as `sound`, but for meshes. +* `shulker` is a boolean value for whether the chest is a shulker or not. + + +## `mcl_chests.player_chest_close(player)` + +This function has to be called when a player closes a chest. + +* `player` is an ObjectRef. + + +## `mcl_chests.chest_update_after_close(pos)` + +This function is called when a chest is closed by `player_chest_close`. + +* `pos` is the chest's position vector. + + +## `mcl_chests.is_not_shulker_box(stack)` + +This function checks for whether `stack` is a shulker box, and returns `false` +if it is. Used internally to disallow putting shulker boxes into shulker boxes. + +* `stack` is an ItemStack. diff --git a/mods/ITEMS/mcl_chests/README.md b/mods/ITEMS/mcl_chests/README.md new file mode 100644 index 000000000..001eae2c2 --- /dev/null +++ b/mods/ITEMS/mcl_chests/README.md @@ -0,0 +1,19 @@ +# `mcl_chests` + +This mod adds normal and large chests, trapped chests, ender chests and +shulkers, providing an API for mods to register their own chests. + +The API is documented in `API.md`. + + +## License of source code + +Copyright (C) 2011-2012 celeron55, Perttu Ahola \ +Copyright (C) 2024 rudzik8, Mikita Wiśniewski + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +http://www.gnu.org/licenses/lgpl-2.1.html diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 95a6a765a..895b2994f 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -2,7 +2,6 @@ local S = minetest.get_translator(minetest.get_current_modname()) local F = minetest.formspec_escape local C = minetest.colorize local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos -local trapped_chest_mesecons_rules = mesecon.rules.pplate local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") @@ -65,7 +64,7 @@ mcl_chests.register_chest("trapped_chest", { mesecons = { receptor = { state = mesecon.state.off, - rules = trapped_chest_mesecons_rules, + rules = mesecon.rules.pplate, }, }, on_rightclick = function(pos, node, clicker) @@ -73,7 +72,7 @@ mcl_chests.register_chest("trapped_chest", { mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" }, node.param2, false, "default_chest", "mcl_chests_chest", "chest") :reinitialize("mcl_chests:trapped_chest_on_small") - mesecon.receptor_on(pos, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos, mesecon.rules.pplate) end, on_rightclick_left = function(pos, node, clicker) local meta = minetest.get_meta(pos) @@ -83,23 +82,23 @@ mcl_chests.register_chest("trapped_chest", { mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") - mesecon.receptor_on(pos, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos, mesecon.rules.pplate) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left") minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) - mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos_other, mesecon.rules.pplate) end, on_rightclick_right = function(pos, node, clicker) local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 }) - mesecon.receptor_on(pos, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos, mesecon.rules.pplate) minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 }) mcl_chests.find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") - mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos_other, mesecon.rules.pplate) end }) @@ -126,7 +125,7 @@ mcl_chests.register_chest("trapped_chest_on", { mesecons = { receptor = { state = mesecon.state.on, - rules = trapped_chest_mesecons_rules, + rules = mesecon.rules.pplate, }, }, on_rightclick = nil, diff --git a/mods/ITEMS/mcl_chests/example.lua b/mods/ITEMS/mcl_chests/example.lua index 2fc1e377e..0ccd460d8 100644 --- a/mods/ITEMS/mcl_chests/example.lua +++ b/mods/ITEMS/mcl_chests/example.lua @@ -6,7 +6,10 @@ local trapped_chest_mesecons_rules = mesecon.rules.pplate mcl_chests.register_chest("stone_chest", { desc = S("Stone Chest"), - large_desc = S("Large Stone Chest"), + title = { + small = S("Stone Chest"), + double = S("Large Stone Chest") + }, longdesc = S( "Stone Chests are containers which provide 27 inventory slots. Stone Chests can be turned into" .. "large stone chests with double the capacity by placing two stone chests next to each other." From 76cff76d912d8e04725fb5b0028834387e2078bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Mon, 24 Jun 2024 12:56:34 +0700 Subject: [PATCH 15/25] Add an introduction text to API.md --- mods/ITEMS/mcl_chests/API.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mods/ITEMS/mcl_chests/API.md b/mods/ITEMS/mcl_chests/API.md index 4eba2ee51..9d28b3de6 100644 --- a/mods/ITEMS/mcl_chests/API.md +++ b/mods/ITEMS/mcl_chests/API.md @@ -1,5 +1,16 @@ # `mcl_chests` API +When reading through this documentation, please keep in mind that the chest +animations are achieved by giving each chest node an entity, as Minetest (as of +5.8.1) doesn't support giving nodes animated meshes, only static ones. + +Because of that, a lot of parameters passed through the exposed functions are +be related to nodes and entities. + +Please refer to [Minetest documentation](http://api.minetest.net/) and the code +comments in `api.lua`. + + ## `mcl_chests.register_chest(basename, definition)` This function allows for simple chest registration, used by both regular and From ac05f8bad6345f87b89b93c07c6d7fdba341e318 Mon Sep 17 00:00:00 2001 From: cora Date: Mon, 24 Jun 2024 08:34:33 +0200 Subject: [PATCH 16/25] Remove unused variables in chests example.lua --- mods/ITEMS/mcl_chests/example.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mods/ITEMS/mcl_chests/example.lua b/mods/ITEMS/mcl_chests/example.lua index 0ccd460d8..87662f116 100644 --- a/mods/ITEMS/mcl_chests/example.lua +++ b/mods/ITEMS/mcl_chests/example.lua @@ -1,8 +1,4 @@ local S = minetest.get_translator(minetest.get_current_modname()) -local F = minetest.formspec_escape -local C = minetest.colorize -local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos -local trapped_chest_mesecons_rules = mesecon.rules.pplate mcl_chests.register_chest("stone_chest", { desc = S("Stone Chest"), From 209b24a2fb1c3c1db5844b4f95b22f2d7cc3878a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Mon, 24 Jun 2024 20:19:43 +0700 Subject: [PATCH 17/25] Move LBMs out of init.lua and fix API.md --- mods/ITEMS/mcl_chests/API.md | 15 +++++++++++- mods/ITEMS/mcl_chests/api.lua | 9 ++++++++ mods/ITEMS/mcl_chests/chests.lua | 17 ++++++++++++++ mods/ITEMS/mcl_chests/ender.lua | 10 ++++++++ mods/ITEMS/mcl_chests/init.lua | 39 +------------------------------- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/mods/ITEMS/mcl_chests/API.md b/mods/ITEMS/mcl_chests/API.md index 9d28b3de6..680d9374a 100644 --- a/mods/ITEMS/mcl_chests/API.md +++ b/mods/ITEMS/mcl_chests/API.md @@ -18,7 +18,7 @@ trapped chests. * `basename` is a string that will be concatenated to form full nodenames for chests, for example `"mcl_chests:basename_small"`. -* `definition` is a key-value table, with following fields: +* `definition` is a key-value table, with the following fields: ```lua { @@ -151,6 +151,18 @@ Returned value is either a luaentity, or `nil` if failed (in which case a warning message gets written into the console). +## `mcl_chests.select_and_spawn_entity(pos, node)` + +This function is a simple wrapper for `mcl_chests.find_or_create_entity`, +getting most of the fields from node definition. + +* `pos` is the position vector. +* `node` is a NodeRef. + +Returned value is either a luaentity, or `nil` if failed (in which case a +warning message gets written into the console). + + ## `mcl_chests.no_rotate` This function is equivalent to `screwdriver.disallow` and is used when a chest @@ -176,6 +188,7 @@ otherwise (where position is a vector value). This function is called in `allow_metadata_inventory_move` field of Node definition. + ## `mcl_chests.protection_check_put_take(pos, listname, index, stack, player)` This function is called in `allow_metadata_inventory_put` and diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 0de631afe..0dd9b39bd 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -181,6 +181,15 @@ local function find_or_create_entity(pos, node_name, textures, param2, double, s end mcl_chests.find_or_create_entity = find_or_create_entity +local function select_and_spawn_entity(pos, node) + local node_name = node.name + local node_def = minetest.registered_nodes[node_name] + local double_chest = minetest.get_item_group(node_name, "double_chest") > 0 + find_or_create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest, + node_def._chest_entity_sound, node_def._chest_entity_mesh, node_def._chest_entity_animation_type) +end +mcl_chests.select_and_spawn_entity = select_and_spawn_entity + local no_rotate, simple_rotate if screwdriver then no_rotate = screwdriver.disallow diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 895b2994f..5771c3b87 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -155,3 +155,20 @@ minetest.register_craft({ recipe = "mcl_chests:trapped_chest", burntime = 15, }) + +-- Disable active/open trapped chests when loaded because nobody could have them open at loading time. +-- Fixes redstone weirdness. +minetest.register_lbm({ + label = "Disable active trapped chests", + name = "mcl_chests:reset_trapped_chests", + nodenames = { + "mcl_chests:trapped_chest_on_small", + "mcl_chests:trapped_chest_on_left", + "mcl_chests:trapped_chest_on_right" + }, + run_at_every_load = true, + action = function(pos, node) + minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos)) + mcl_chests.chest_update_after_close(pos) + end, +}) diff --git a/mods/ITEMS/mcl_chests/ender.lua b/mods/ITEMS/mcl_chests/ender.lua index f41ec950a..e65c5e722 100644 --- a/mods/ITEMS/mcl_chests/ender.lua +++ b/mods/ITEMS/mcl_chests/ender.lua @@ -126,3 +126,13 @@ minetest.register_craft({ { "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" }, }, }) + +minetest.register_lbm({ + label = "Upgrade old ender chest formspec", + name = "mcl_chests:replace_old_ender_form", + nodenames = { "mcl_chests:ender_chest_small" }, + run_at_every_load = false, + action = function(pos, node) + minetest.get_meta(pos):set_string("formspec", "") + end, +}) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index bea6b9496..0a780a660 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -58,22 +58,12 @@ minetest.register_on_leaveplayer(function(player) mcl_chests.player_chest_close(player) end) - - -local function select_and_spawn_entity(pos, node) - local node_name = node.name - local node_def = minetest.registered_nodes[node_name] - local double_chest = minetest.get_item_group(node_name, "double_chest") > 0 - mcl_chests.find_or_create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest, - node_def._chest_entity_sound, node_def._chest_entity_mesh, node_def._chest_entity_animation_type) -end - minetest.register_lbm({ label = "Spawn Chest entities", name = "mcl_chests:spawn_chest_entities", nodenames = { "group:chest_entity" }, run_at_every_load = true, - action = select_and_spawn_entity, + action = mcl_chests.select_and_spawn_entity, }) minetest.register_lbm({ @@ -101,30 +91,3 @@ minetest.register_lbm({ end end }) - --- Disable active/open trapped chests when loaded because nobody could have them open at loading time. --- Fixes redstone weirdness. -minetest.register_lbm({ - label = "Disable active trapped chests", - name = "mcl_chests:reset_trapped_chests", - nodenames = { - "mcl_chests:trapped_chest_on_small", - "mcl_chests:trapped_chest_on_left", - "mcl_chests:trapped_chest_on_right" - }, - run_at_every_load = true, - action = function(pos, node) - minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos)) - mcl_chests.chest_update_after_close(pos) - end, -}) - -minetest.register_lbm({ - label = "Upgrade old ender chest formspec", - name = "mcl_chests:replace_old_ender_form", - nodenames = { "mcl_chests:ender_chest_small" }, - run_at_every_load = false, - action = function(pos, node) - minetest.get_meta(pos):set_string("formspec", "") - end, -}) From 70e903b7165ea828203bc5fe8c4e4b9030e3430d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Tue, 25 Jun 2024 09:28:33 +0700 Subject: [PATCH 18/25] Simplify double inventory inv logic --- mods/ITEMS/mcl_chests/api.lua | 102 +++++++++++++--------------------- 1 file changed, 39 insertions(+), 63 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 0dd9b39bd..dbdb1f185 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -379,10 +379,10 @@ local function limit_put_list(stack, list) return stack end -local function limit_put(stack, inv1, inv2) +local function limit_put(stack, top_inv, bottom_inv) local leftover = ItemStack(staick) - leftover = limit_put_list(leftover, inv1:get_list("main")) - leftover = limit_put_list(leftover, inv2:get_list("main")) + leftover = limit_put_list(leftover, top_inv:get_list("main")) + leftover = limit_put_list(leftover, bottom_inv:get_list("main")) return stack:get_count() - leftover:get_count() end @@ -396,62 +396,52 @@ local function close_forms(canonical_basename, pos) end end +local function get_chest_inventories(pos, side) + local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + + local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) + local meta_other = minetest.get_meta(pos_other) + local inv_other = meta_other:get_inventory() + + local top_inv, bottom_inv + if side == "left" then + top_inv = inv + bottom_inv = inv_other + else + top_inv = inv_other + bottom_inv = inv + end + return top_inv, bottom_inv +end + -- Functions used in double chest registration code --- ================================================ +--------------------------------------------------- -- The `return function` wrapping is necessary to avoid stacking up parameters. -- `side` is either "left" or "right". local function hopper_pull_double(side) return function(pos, hop_pos, hop_inv, hop_list) - local node = minetest.get_node(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() + local top_inv, bottom_inv = get_chest_inventories(pos, side) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - - local ret_inv1, ret_inv2 - if side == "left" then - ret_inv1 = inv - ret_inv2 = inv_other - else - ret_inv1 = inv_other - ret_inv2 = inv - end - - local stack_id = mcl_util.select_stack(ret_inv1, "main", hop_inv, hop_list) + local stack_id = mcl_util.select_stack(top_inv, "main", hop_inv, hop_list) if stack_id ~= nil then - return ret_inv1, "main", stack_id + return top_inv, "main", stack_id end - stack_id = mcl_util.select_stack(ret_inv2, "main", hop_inv, hop_list) - return ret_inv2, "main", stack_id + stack_id = mcl_util.select_stack(bottom_inv, "main", hop_inv, hop_list) + return bottom_inv, "main", stack_id end end local function hopper_push_double(side) return function(pos, hop_pos, hop_inv, hop_list) - local node = minetest.get_node(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() + local top_inv, bottom_inv = get_chest_inventories(pos, side) - local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() - - local ret_inv1, ret_inv2 - if side == "left" then - ret_inv1 = inv - ret_inv2 = inv_other - else - ret_inv1 = inv_other - ret_inv2 = inv - end - - local stack_id = mcl_util.select_stack(hop_inv, hop_list, ret_inv1, "main", nil, 1) + local stack_id = mcl_util.select_stack(hop_inv, hop_list, top_inv, "main", nil, 1) if stack_id ~= nil then - return ret_inv1, "main", stack_id + return top_inv, "main", stack_id end - stack_id = mcl_util.select_stack(hop_inv, hop_list, ret_inv2, "main", nil, 1) - return ret_inv2, "main", stack_id + stack_id = mcl_util.select_stack(hop_inv, hop_list, bottom_inv, "main", nil, 1) + return bottom_inv, "main", stack_id end end local function construct_double_chest(side, names) return function(pos) @@ -492,14 +482,8 @@ local function protection_check_put(side) return function(pos, listname, index, return 0 -- BEGIN OF LISTRING WORKAROUND elseif listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - if side == "left" then - return limit_put(stack, inv, other_inv) - else - return limit_put(stack, other_inv, inv) - end + local top_inv, bottom_inv = get_chest_inventories(pos, side) + return limit_put(stack, top_inv, bottom_inv) -- END OF LISTRING WORKAROUND else return stack:get_count() @@ -511,17 +495,9 @@ local function log_inventory_put_double(side) return function(pos, listname, ind " moves stuff to chest at " .. minetest.pos_to_string(pos)) -- BEGIN OF LISTRING WORKAROUND if listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) - - inv:set_stack("input", 1, nil) - - if side == "left" then - double_chest_add_item(inv, other_inv, "main", stack) - else - double_chest_add_item(other_inv, inv, "main", stack) - end + local top_inv, bottom_inv = get_chest_inventories(pos, side) + top_inv:set_stack("input", 1, nil) + double_chest_add_item(top_inv, bottom_inv, "main", stack) end -- END OF LISTRING WORKAROUND end end From c5bc6ff1890ec8a77c07751cff7f7a1f814793db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Tue, 25 Jun 2024 09:36:40 +0700 Subject: [PATCH 19/25] Cleanup comments (don't use \=, it's annoying) --- mods/ITEMS/mcl_chests/api.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index dbdb1f185..39b0a2556 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -24,7 +24,7 @@ local function table_merge(tbl, ...) end -- Chest Entity --- ============ +-- ------------ -- This is necessary to show the chest as an animated mesh, as Minetest doesn't support assigning animated meshes to -- nodes directly. We're bypassing this limitation by giving each chest its own entity, and making the chest node -- itself fully transparent. @@ -208,7 +208,8 @@ if screwdriver then end mcl_chests.no_rotate, mcl_chests.simple_rotate = no_rotate, simple_rotate ---[[ List of open chests. +--[[ List of open chests + ------------------- Key: Player name Value: If player is using a chest: { pos = } @@ -417,7 +418,7 @@ local function get_chest_inventories(pos, side) end -- Functions used in double chest registration code ---------------------------------------------------- +-- ------------------------------------------------ -- The `return function` wrapping is necessary to avoid stacking up parameters. -- `side` is either "left" or "right". local function hopper_pull_double(side) return function(pos, hop_pos, hop_inv, hop_list) @@ -549,7 +550,7 @@ function mcl_chests.register_chest(basename, d) end -- Names table - -- =========== + -- ----------- -- Accessed through names["kind"].x (names.kind.x), where x can be: -- a = "actual" -- c = canonical From f1fa6240bbe423e13a8b2462ddf0ac90b6b932dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Wed, 26 Jun 2024 11:52:26 +0700 Subject: [PATCH 20/25] Fix shift-clicking and a few luacheck warnings --- mods/ITEMS/mcl_chests/api.lua | 23 +++++++++++++++-------- mods/ITEMS/mcl_chests/chests.lua | 4 +--- mods/ITEMS/mcl_chests/ender.lua | 2 +- mods/ITEMS/mcl_chests/init.lua | 2 -- mods/ITEMS/mcl_chests/shulkers.lua | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 39b0a2556..92ab84ee9 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -1,4 +1,4 @@ -local S = minetest.get_translator(minetest.get_current_modname()) +local S = minetest.get_translator(minetest.get_current_modname()) local F = minetest.formspec_escape local C = minetest.colorize @@ -6,7 +6,6 @@ local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor local string = string local table = table -local math = math local sf = string.format @@ -162,13 +161,13 @@ local function create_entity(pos, node_name, textures, param2, double, sound_pre dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) local initialization_data = minetest.serialize({pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type, "###mcl_chests:chest###"}) - local obj = minetest.add_entity(entity_pos, "mcl_chests:chest", initialization_data) + local obj = minetest.add_entity(entity_pos, "mcl_chests:chest", initialization_data) if obj and obj:get_pos() then return obj:get_luaentity() else minetest.log("warning", "[mcl_chests] Failed to create entity at " .. (entity_pos and minetest.pos_to_string(entity_pos, 1) or "nil")) - end + end end mcl_chests.create_entity = create_entity @@ -381,7 +380,7 @@ local function limit_put_list(stack, list) end local function limit_put(stack, top_inv, bottom_inv) - local leftover = ItemStack(staick) + local leftover = ItemStack(stack) leftover = limit_put_list(leftover, top_inv:get_list("main")) leftover = limit_put_list(leftover, bottom_inv:get_list("main")) return stack:get_count() - leftover:get_count() @@ -496,9 +495,17 @@ local function log_inventory_put_double(side) return function(pos, listname, ind " moves stuff to chest at " .. minetest.pos_to_string(pos)) -- BEGIN OF LISTRING WORKAROUND if listname == "input" then - local top_inv, bottom_inv = get_chest_inventories(pos, side) - top_inv:set_stack("input", 1, nil) - double_chest_add_item(top_inv, bottom_inv, "main", stack) + local inv = minetest.get_inventory({ type = "node", pos = pos }) + local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) + local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + + inv:set_stack("input", 1, nil) + + if side == "left" then + double_chest_add_item(inv, other_inv, "main", stack) + else + double_chest_add_item(other_inv, inv, "main", stack) + end end -- END OF LISTRING WORKAROUND end end diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 5771c3b87..164ce7a9b 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -1,6 +1,4 @@ -local S = minetest.get_translator(minetest.get_current_modname()) -local F = minetest.formspec_escape -local C = minetest.colorize +local S = minetest.get_translator(minetest.get_current_modname()) local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.") diff --git a/mods/ITEMS/mcl_chests/ender.lua b/mods/ITEMS/mcl_chests/ender.lua index e65c5e722..395c0be5a 100644 --- a/mods/ITEMS/mcl_chests/ender.lua +++ b/mods/ITEMS/mcl_chests/ender.lua @@ -1,4 +1,4 @@ -local S = minetest.get_translator(minetest.get_current_modname()) +local S = minetest.get_translator(minetest.get_current_modname()) local F = minetest.formspec_escape local C = minetest.colorize diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 0a780a660..9294ee642 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -1,6 +1,4 @@ local string = string -local table = table -local math = math local sm = string.match diff --git a/mods/ITEMS/mcl_chests/shulkers.lua b/mods/ITEMS/mcl_chests/shulkers.lua index a80ea7ae1..ac10cbb0d 100644 --- a/mods/ITEMS/mcl_chests/shulkers.lua +++ b/mods/ITEMS/mcl_chests/shulkers.lua @@ -1,4 +1,4 @@ -local S = minetest.get_translator(minetest.get_current_modname()) +local S = minetest.get_translator(minetest.get_current_modname()) local F = minetest.formspec_escape local C = minetest.colorize From 7bf15642caaa5fad25b3b6d448b57536ca3aafc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Thu, 27 Jun 2024 21:04:29 +0700 Subject: [PATCH 21/25] Resolve teknomunk's comments --- mods/ITEMS/mcl_chests/api.lua | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/mods/ITEMS/mcl_chests/api.lua b/mods/ITEMS/mcl_chests/api.lua index 92ab84ee9..3145a1657 100644 --- a/mods/ITEMS/mcl_chests/api.lua +++ b/mods/ITEMS/mcl_chests/api.lua @@ -207,12 +207,12 @@ if screwdriver then end mcl_chests.no_rotate, mcl_chests.simple_rotate = no_rotate, simple_rotate ---[[ List of open chests - ------------------- -Key: Player name -Value: - If player is using a chest: { pos = } - Otherwise: nil ]] +-- List of open chests +-- ------------------- +-- Key: Player name +-- Value: +-- If player is using a chest: { pos = } +-- Otherwise: nil local open_chests = {} mcl_chests.open_chests = open_chests @@ -397,13 +397,11 @@ local function close_forms(canonical_basename, pos) end local function get_chest_inventories(pos, side) - local node = minetest.get_node(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() + local inv = minetest.get_inventory({ type = "node", pos = pos }) + local node = minetest.get_node(pos) local pos_other = get_double_container_neighbor_pos(pos, node.param2, side) - local meta_other = minetest.get_meta(pos_other) - local inv_other = meta_other:get_inventory() + local inv_other = minetest.get_inventory({ type = "node", pos = pos_other }) local top_inv, bottom_inv if side == "left" then @@ -495,17 +493,12 @@ local function log_inventory_put_double(side) return function(pos, listname, ind " moves stuff to chest at " .. minetest.pos_to_string(pos)) -- BEGIN OF LISTRING WORKAROUND if listname == "input" then - local inv = minetest.get_inventory({ type = "node", pos = pos }) - local other_pos = get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, side) - local other_inv = minetest.get_inventory({ type = "node", pos = other_pos }) + local top_inv, bottom_inv = get_chest_inventories(pos, side) - inv:set_stack("input", 1, nil) + top_inv:set_stack("input", 1, nil) + bottom_inv:set_stack("input", 1, nil) - if side == "left" then - double_chest_add_item(inv, other_inv, "main", stack) - else - double_chest_add_item(other_inv, inv, "main", stack) - end + double_chest_add_item(top_inv, bottom_inv, "main", stack) end -- END OF LISTRING WORKAROUND end end From c1e9e4b1a2f52efc930cafb1b4f35562bc6d2776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 28 Jun 2024 19:36:32 +0700 Subject: [PATCH 22/25] Fix typos in API.md --- mods/ITEMS/mcl_chests/API.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_chests/API.md b/mods/ITEMS/mcl_chests/API.md index 680d9374a..5550cfbfa 100644 --- a/mods/ITEMS/mcl_chests/API.md +++ b/mods/ITEMS/mcl_chests/API.md @@ -31,7 +31,7 @@ trapped chests. double = S("Large Stone Chest") -- defaults to `"Large " .. desc` } -- These will be shown when opening the chest (in formspecs). - + longdesc = S( "Stone Chests are containers which provide 27 inventory slots. Stone Chests can be turned into" .. "large stone chests with double the capacity by placing two stone chests next to each other." @@ -113,7 +113,7 @@ For usage examples, see `chests.lua` and `example.lua`. ## `mcl_chests.create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)` -This function creates a chest entity based on parameters: +This function creates a chest entity based on the parameters: * `pos` is the position vector. * `node_name` is a string used in initialization data for the entity. @@ -131,7 +131,7 @@ Returned value is either a luaentity, or `nil` if failed (in which case a warning message gets written into the console). -## `find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)` +## `mcl_chests.find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)` This function finds an existing entity, or creates one if failed. Parameters: @@ -197,7 +197,7 @@ This function is called in `allow_metadata_inventory_put` and ## `mcl_chests.player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker)` -This function opens a chest based on parameters: +This function opens a chest based on the parameters: * `player` is an ObjectRef. * `pos` is the position vector. From 508bc19f6a4e8dd522e3f44a44818f5af37bfc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 28 Jun 2024 19:43:20 +0700 Subject: [PATCH 23/25] Remove nil fields from trapped chest definition --- mods/ITEMS/mcl_chests/chests.lua | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mods/ITEMS/mcl_chests/chests.lua b/mods/ITEMS/mcl_chests/chests.lua index 164ce7a9b..df4d6c13f 100644 --- a/mods/ITEMS/mcl_chests/chests.lua +++ b/mods/ITEMS/mcl_chests/chests.lua @@ -101,14 +101,10 @@ mcl_chests.register_chest("trapped_chest", { }) mcl_chests.register_chest("trapped_chest_on", { - desc = nil, title = { small = S("Chest"), double = S("Large Chest") }, - longdesc = nil, - usagehelp = nil, - tt_help = nil, tiles = traptiles, groups = { handy = 1, @@ -126,9 +122,6 @@ mcl_chests.register_chest("trapped_chest_on", { rules = mesecon.rules.pplate, }, }, - on_rightclick = nil, - on_rightclick_left = nil, - on_rightclick_right = nil, drop = "trapped_chest", canonical_basename = "trapped_chest" }) From 347305eaea1be12345f14a723ae163a199f82be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Fri, 28 Jun 2024 19:50:51 +0700 Subject: [PATCH 24/25] Fix ender chests rotate crash --- mods/ITEMS/mcl_chests/ender.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_chests/ender.lua b/mods/ITEMS/mcl_chests/ender.lua index 395c0be5a..cf3e7b0fe 100644 --- a/mods/ITEMS/mcl_chests/ender.lua +++ b/mods/ITEMS/mcl_chests/ender.lua @@ -97,7 +97,7 @@ minetest.register_node("mcl_chests:ender_chest_small", { _mcl_blast_resistance = 3000, _mcl_hardness = 22.5, _mcl_silk_touch_drop = { "mcl_chests:ender_chest" }, - on_rotate = simple_rotate, + on_rotate = mcl_chests.simple_rotate, }) minetest.register_on_joinplayer(function(player) From 567d1129422abf0ec552af7c4bae23302cee81e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikita=20Wi=C5=9Bniewski?= Date: Sat, 13 Jul 2024 10:48:03 +0700 Subject: [PATCH 25/25] Fix deprecated get_metadata() usage Items are instead written as a serialized string into ItemStackMetaRef, and read from there as well. Old itemstacks get converted to the new format automatically. --- mods/ITEMS/mcl_chests/shulkers.lua | 36 +++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/mods/ITEMS/mcl_chests/shulkers.lua b/mods/ITEMS/mcl_chests/shulkers.lua index ac10cbb0d..b6ada6c46 100644 --- a/mods/ITEMS/mcl_chests/shulkers.lua +++ b/mods/ITEMS/mcl_chests/shulkers.lua @@ -136,12 +136,20 @@ for color, desc in pairs(boxtypes) do end, after_place_node = function(pos, placer, itemstack, pointed_thing) local nmeta = minetest.get_meta(pos) - local imetadata = itemstack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) + local imeta = itemstack:get_meta() + + -- Convert old itemstacks to not use get_metadata() + if not imeta:contains("inv") and + (itemstack:get_metadata() ~= "") then + imeta:set_string("inv", itemstack:get_metadata()) + itemstack:set_metadata("") -- clear + end + + local iinv_main = minetest.deserialize(imeta:get_string("inv")) local ninv = nmeta:get_inventory() ninv:set_list("main", iinv_main) ninv:set_size("main", 9 * 3) - set_shulkerbox_meta(nmeta, itemstack:get_meta()) + set_shulkerbox_meta(nmeta, imeta) if minetest.is_creative_enabled(placer:get_player_name()) then if not ninv:is_empty("main") then @@ -158,11 +166,11 @@ for color, desc in pairs(boxtypes) do if minetest.registered_nodes[dropnode.name].buildable_to then minetest.set_node(droppos, { name = small_name, param2 = minetest.dir_to_facedir(dropdir) }) local ninv = minetest.get_inventory({ type = "node", pos = droppos }) - local imetadata = stack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) + local imeta = stack:get_meta() + local iinv_main = minetest.deserialize(imeta:get_string("inv")) ninv:set_list("main", iinv_main) ninv:set_size("main", 9 * 3) - set_shulkerbox_meta(minetest.get_meta(droppos), stack:get_meta()) + set_shulkerbox_meta(minetest.get_meta(droppos), imeta) stack:take_item() end return stack @@ -213,12 +221,20 @@ for color, desc in pairs(boxtypes) do end, after_place_node = function(pos, placer, itemstack, pointed_thing) local nmeta = minetest.get_meta(pos) - local imetadata = itemstack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) + local imeta = itemstack:get_meta() + + -- Convert old itemstacks to not use get_metadata() + if not imeta:contains("inv") and + (itemstack:get_metadata() ~= "") then + imeta:set_string("inv", itemstack:get_metadata()) + itemstack:set_metadata("") -- clear + end + + local iinv_main = minetest.deserialize(imeta:get_string("inv")) local ninv = nmeta:get_inventory() ninv:set_list("main", iinv_main) ninv:set_size("main", 9 * 3) - set_shulkerbox_meta(nmeta, itemstack:get_meta()) + set_shulkerbox_meta(nmeta, imeta) if minetest.is_creative_enabled(placer:get_player_name()) then if not ninv:is_empty("main") then @@ -252,7 +268,7 @@ for color, desc in pairs(boxtypes) do local boxitem_meta = boxitem:get_meta() boxitem_meta:set_string("description", meta:get_string("description")) boxitem_meta:set_string("name", meta:get_string("name")) - boxitem:set_metadata(data) + boxitem_meta:set_string("inv", data) if minetest.is_creative_enabled("") then if not inv:is_empty("main") then