local S = minetest.get_translator_auto(true) local pi, vround = math.pi, vector.round local tconcat = table.concat local b = "blank.png" local function objects_inside_radius(p) return minetest.get_objects_inside_radius(p, 0.5) end local armor_stand_formspec = default.gui .. "label[0.9,0.05;" .. S"Armor Stand" .. "]" .. "list[current_name;armor_head;3.5,1;1,1;]" .. "list[current_name;armor_legs;4.5,1;1,1;]" .. "list[current_name;armor_torso;3.5,2;1,1;]" .. "list[current_name;armor_feet;4.5,2;1,1;]" .. "image[3.5,1;1,1;formspec_cell.png^3d_armor_inv_helmet.png]" .. "image[4.5,1;1,1;formspec_cell.png^3d_armor_inv_leggings.png]" .. "image[3.5,2;1,1;formspec_cell.png^3d_armor_inv_chestplate.png]" .. "image[4.5,2;1,1;formspec_cell.png^3d_armor_inv_boots.png]".. "image[7.95,3.1;1.1,1.1;^[colorize:#D6D5E6]]" local elements = {"head", "torso", "legs", "feet"} local function drop_armor(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() for _, element in pairs(elements) do local stack = inv:get_stack("armor_" .. element, 1) if stack and stack:get_count() > 0 then minetest.item_drop(stack, nil, pos) inv:set_stack("armor_" .. element, 1, nil) end end end local function get_stand_object(pos) local object local objects = objects_inside_radius(pos) for _, obj in pairs(objects) do local ent = obj:get_luaentity() if ent and ent.name == "3d_armor_stand:armor_entity" then -- Remove duplicates if object then obj:remove() else object = obj end end end return object end local function update_entity(pos) local node = minetest.get_node(pos) local object = get_stand_object(pos) if object then if node.name:split(":")[1] ~= "3d_armor_stand" then object:remove() return end else object = minetest.add_entity(pos, "3d_armor_stand:armor_entity") end if object then local texture = b local textures = {} local meta = minetest.get_meta(pos) local inv = meta:get_inventory() if inv then for _, element in pairs(elements) do local stack = inv:get_stack("armor_" .. element, 1) if stack:get_count() == 1 then local item = stack:get_name() or "" local def = stack:get_definition() local groups = def and def.groups if groups and groups["armor_" .. element] then if def.texture then textures[#textures + 1] = def.texture else textures[#textures + 1] = item:gsub("%:", "_") .. ".png" end end end end end if #textures > 0 then texture = tconcat(textures, "^") end if node.param2 then local yaw = 0 local rot = node.param2 % 4 if rot == 1 then yaw = 3 * pi / 2 elseif rot == 2 then yaw = pi elseif rot == 3 then yaw = pi / 2 end object:set_yaw(yaw) end object:set_properties({textures={texture}}) end end local function check_item(pos) local num = #objects_inside_radius(pos) if num > 0 then return end update_entity(pos) end -- -- Armor Stand registration helper -- local function register_armor_stand(name, def) minetest.register_craft({ output = name, recipe = { {"", def.material, ""}, {"", def.material, ""}, {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"} } }) -- Allow almost everything to be overridden local default_fields = { drawtype = "mesh", mesh = "3d_armor_stand.b3d", use_texture_alpha = "clip", paramtype = "light", paramtype2 = "facedir", walkable = false, selection_box = { type = "fixed", fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5} }, on_rotate = function(pos, node, user, mode) if not minetest.is_protected(pos, user:get_player_name()) and mode == 1 then node.param2 = (node.param2 % 8) + 1 if node.param2 > 3 then node.param2 = 0 end minetest.swap_node(pos, node) update_entity(pos) return true end return false end, after_place_node = function(pos, placer) local meta = minetest.get_meta(pos) local pn = placer and placer:get_player_name() or "" meta:set_string("infotext", S"Armor Stand" .. "\n" .. S("Owned by @1", S(pn))) local formspec = armor_stand_formspec .. "item_image[0,-0.1;1,1;" .. name .. "]" meta:set_string("formspec", formspec) local inv = meta:get_inventory() for _, element in pairs(elements) do inv:set_size("armor_" .. element, 1) end minetest.add_entity(pos, "3d_armor_stand:armor_entity") end, can_dig = function(pos, player) local pn = player and player:get_player_name() or "" return not minetest.is_protected(pos, pn) end, on_dig = function(pos, node, player) local pn = player and player:get_player_name() or "" if not minetest.is_protected(pos, pn) then drop_armor(pos) minetest.node_dig(pos, node, player) end end, on_punch = check_item, allow_metadata_inventory_put = function(pos, listname, _, stack, player) local pn = player and player:get_player_name() or "" if minetest.is_protected(pos, pn) then return 0 end local item = minetest.registered_tools[stack:get_name()] local group = item and item.groups if group then for _, element in pairs(elements) do if listname == "armor_" .. element and group["armor_" .. element] then return 1 end end end return 0 end, allow_metadata_inventory_take = function(pos, _, _, stack, player) local pn = player and player:get_player_name() or "" if minetest.is_protected(pos, pn) then return 0 end return stack:get_count() end, allow_metadata_inventory_move = function() return 0 end, on_metadata_inventory_put = update_entity, on_metadata_inventory_take = update_entity, after_destruct = update_entity, on_blast = function(pos) drop_armor(pos) minetest.remove_node(pos) end } for k, v in pairs(default_fields) do if def[k] == nil then def[k] = v end end def.groups.armor_stand = 1 def.material = nil minetest.register_node(name, def) if mesecon and mesecon.register_mvps_stopper then mesecon.register_mvps_stopper(name) end end -- -- Register Stands -- register_armor_stand("3d_armor_stand:armor_stand", { description = S"Apple Wood Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_wood.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_wood_defaults(), material = "default:fence_wood" }) register_armor_stand("3d_armor_stand:armor_stand_acacia_wood", { description = S"Acacia Wood Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_wood_acacia.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_acacia_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_acacia_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_wood_defaults(), material = "default:fence_acacia_wood" }) register_armor_stand("3d_armor_stand:armor_stand_birch_wood", { description = S"Birch Wood Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_wood_birch.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_birch_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_birch_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_wood_defaults(), material = "default:fence_birch_wood" }) register_armor_stand("3d_armor_stand:armor_stand_jungle_wood", { description = S"Jungle Wood Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_wood_jungle.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_jungle_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_jungle_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_wood_defaults(), material = "default:fence_jungle_wood" }) register_armor_stand("3d_armor_stand:armor_stand_pine_wood", { description = S"Pine Wood Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_wood_pine.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_pine_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_pine_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_wood_defaults(), material = "default:fence_pine_wood" }) register_armor_stand("3d_armor_stand:armor_stand_cherry_blossom_wood", { description = S"Cherry Blossom Wood Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_wood_cherry_blossom.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_cherry_blossom_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_wood_cherry_blossom_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_wood_defaults(), material = "default:fence_cherry_blossom_wood" }) register_armor_stand("3d_armor_stand:armor_stand_ice", { description = S"Ice Armor Stand", tiles = {"3d_armor_stand_platform.png^3d_armor_stand_ice.png"}, inventory_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_ice_inv.png", wield_image = "3d_armor_stand_platform_inv.png^3d_armor_stand_ice_inv.png", groups = {choppy = 2, oddly_breakable_by_hand = 2}, sounds = default.node_sound_glass_defaults(), material = "default:fence_ice" }) -- -- Entity -- minetest.register_entity("3d_armor_stand:armor_entity", { physical = false, visual = "mesh", mesh = "3d_armor_entity.b3d", visual_size = {x = 1, y = 1}, collisionbox = {0}, textures = {b}, on_activate = function(self) local pos = self.object:get_pos() if pos then pos = vround(pos) local node = minetest.get_node(pos) if node.name:split(":")[1] == "3d_armor_stand" then update_entity(pos) return end end self.object:remove() end }) minetest.register_lbm({ label = "Check Armor Stand", name = "3d_armor_stand:armor_stand", nodenames = {"group:armor_stand"}, run_at_every_load = true, action = check_item })