1
0
mirror of https://github.com/minetest-mods/3d_armor.git synced 2025-10-19 08:55:20 +02:00
Files
3d_armor/3d_armor_stand/init.lua
Bradley Pierce 8f775a64f0 Rework armor stand inventories (#165)
This change makes armor stands only have one inventory list instead of four, thereby allowing the user to shift-click items back and forth. There is no visual difference in the formspec, and each piece of armor still has its own dedicated slot. An LBM is used to update the inventories of previous armor stands.
2025-09-06 08:06:39 +02:00

375 lines
9.8 KiB
Lua

-- support for i18n
local S = minetest.get_translator(minetest.get_current_modname())
local armor_stand_formspec = "size[8,7]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
default.get_hotbar_bg(0,3) ..
"list[current_name;main;3,0.5;2,1;]" ..
"list[current_name;main;3,1.5;2,1;2]" ..
"image[3,0.5;1,1;3d_armor_stand_head.png]" ..
"image[4,0.5;1,1;3d_armor_stand_torso.png]" ..
"image[3,1.5;1,1;3d_armor_stand_legs.png]" ..
"image[4,1.5;1,1;3d_armor_stand_feet.png]" ..
"list[current_player;main;0,3;8,1;]" ..
"list[current_player;main;0,4.25;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]"
local elements = {"head", "torso", "legs", "feet"}
local function drop_armor(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
for i = 1, 4 do
local stack = inv:get_stack("main", i)
if stack and stack:get_count() > 0 then
armor.drop_armor(pos, stack)
inv:set_stack("main", i, nil)
end
end
end
local function get_stand_object(pos)
local object = nil
local objects = minetest.get_objects_inside_radius(pos, 0.5) or {}
for _, obj in pairs(objects) do
local ent = obj:get_luaentity()
if ent then
if ent.name == "3d_armor_stand:armor_entity" then
-- Remove duplicates
if object then
obj:remove()
else
object = obj
end
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 not string.find(node.name, "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 = "blank.png"
local textures = {}
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local yaw = 0
if inv then
for i, element in ipairs(elements) do
local stack = inv:get_stack("main", i)
if stack:get_count() == 1 then
local item = stack:get_name() or ""
local def = stack:get_definition() or {}
local groups = def.groups or {}
if groups["armor_"..element] then
if def.texture then
table.insert(textures, def.texture)
else
table.insert(textures, item:gsub("%:", "_")..".png")
end
end
end
end
end
if #textures > 0 then
texture = table.concat(textures, "^")
end
if node.param2 then
local rot = node.param2 % 4
if rot == 1 then
yaw = 3 * math.pi / 2
elseif rot == 2 then
yaw = math.pi
elseif rot == 3 then
yaw = math.pi / 2
end
end
object:set_yaw(yaw)
object:set_properties({textures={texture}})
end
end
local function has_locked_armor_stand_privilege(meta, player)
local name = ""
if player then
if minetest.check_player_privs(player, "protection_bypass") then
return true
end
name = player:get_player_name()
end
if name ~= meta:get_string("owner") then
return false
end
return true
end
local function add_hidden_node(pos, player)
local p = {x=pos.x, y=pos.y + 1, z=pos.z}
local name = player:get_player_name()
local node = minetest.get_node(p)
if node.name == "air" and not minetest.is_protected(pos, name) then
minetest.set_node(p, {name="3d_armor_stand:top"})
end
end
local function remove_hidden_node(pos)
local p = {x=pos.x, y=pos.y + 1, z=pos.z}
local node = minetest.get_node(p)
if node.name == "3d_armor_stand:top" then
minetest.remove_node(p)
end
end
minetest.register_node("3d_armor_stand:top", {
description = S("Armor Stand Top"),
paramtype = "light",
drawtype = "plantlike",
sunlight_propagates = true,
walkable = true,
pointable = false,
diggable = false,
buildable_to = false,
drop = "",
groups = {not_in_creative_inventory = 1},
is_ground_content = false,
on_blast = function() end,
tiles = {"blank.png"},
})
local function register_armor_stand(def)
local function owns_armor_stand(pos, meta, player)
if def.name == "locked_armor_stand" and not has_locked_armor_stand_privilege(meta, player) then
return false
end
local has_access = minetest.is_player(player) and not minetest.is_protected(pos, player:get_player_name())
if def.name == "shared_armor_stand" and not has_access then
return false
end
return true
end
minetest.register_node("3d_armor_stand:" .. def.name, {
description = def.description,
drawtype = "mesh",
mesh = "3d_armor_stand.obj",
tiles = {def.texture},
use_texture_alpha = "clip",
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
selection_box = {
type = "fixed",
fixed = {
{-0.25, -0.4375, -0.25, 0.25, 1.4, 0.25},
{-0.5, -0.5, -0.5, 0.5, -0.4375, 0.5},
},
},
groups = {choppy=2, oddly_breakable_by_hand=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", armor_stand_formspec)
meta:set_string("infotext", def.description)
if def.name == "locked_armor_stand" then
meta:set_string("owner", "")
end
local inv = meta:get_inventory()
inv:set_size("main", 4)
end,
can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if not inv:is_empty("main") then
return false
end
return true
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
minetest.add_entity(pos, "3d_armor_stand:armor_entity")
if def.name == "locked_armor_stand" then
meta:set_string("owner", placer:get_player_name() or "")
meta:set_string("infotext", S("Armor Stand (owned by @1)", meta:get_string("owner")))
elseif def.name == "shared_armor_stand" then
meta:set_string("infotext", def.description)
end
add_hidden_node(pos, placer)
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if not owns_armor_stand(pos, meta, player) then
return 0
end
local inv = meta:get_inventory()
local stack_def = stack:get_definition() or {}
local groups = stack_def.groups or {}
for i, element in ipairs(elements) do
if groups["armor_"..element] and inv:get_stack(listname, i):is_empty() then
return 1
end
end
return 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if not owns_armor_stand(pos, meta, player) then
return 0
end
return 1
end,
allow_metadata_inventory_move = function(pos)
return 0
end,
on_metadata_inventory_put = function(pos, listname, index, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack_def = stack:get_definition() or {}
local groups = stack_def.groups or {}
for i, element in ipairs(elements) do
if groups["armor_"..element] then
inv:set_stack(listname, i, stack)
if index ~= i then
inv:set_stack(listname, index, nil)
end
break
end
end
update_entity(pos)
end,
on_metadata_inventory_take = function(pos)
update_entity(pos)
end,
after_destruct = function(pos)
update_entity(pos)
remove_hidden_node(pos)
end,
on_blast = def.on_blast
})
end
register_armor_stand({
name = "armor_stand",
description = S("Armor Stand"),
texture = "3d_armor_stand.png",
on_blast = function(pos)
drop_armor(pos)
armor.drop_armor(pos, "3d_armor_stand:armor_stand")
minetest.remove_node(pos)
end
})
register_armor_stand({
name = "locked_armor_stand",
description = S("Locked Armor Stand"),
texture = "3d_armor_stand_locked.png"
})
register_armor_stand({
name = "shared_armor_stand",
description = S("Shared Armor Stand"),
texture = "3d_armor_stand_shared.png"
})
minetest.register_entity("3d_armor_stand:armor_entity", {
initial_properties = {
physical = true,
visual = "mesh",
mesh = "3d_armor_entity.obj",
visual_size = {x=1, y=1},
collisionbox = {0,0,0,0,0,0},
textures = {"blank.png"},
},
_pos = nil,
on_activate = function(self)
local pos = self.object:get_pos()
if pos then
self._pos = vector.round(pos)
update_entity(pos)
end
end,
on_blast = function(self, damage)
local drops = {}
local node = minetest.get_node(self._pos)
if node.name == "3d_armor_stand:armor_stand" then
drop_armor(self._pos)
self.object:remove()
end
return false, false, drops
end,
})
minetest.register_abm({
nodenames = {"3d_armor_stand:locked_armor_stand", "3d_armor_stand:shared_armor_stand", "3d_armor_stand:armor_stand"},
interval = 15,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local num
num = #minetest.get_objects_inside_radius(pos, 0.5)
if num > 0 then return end
update_entity(pos)
end
})
minetest.register_lbm({
label = "Update armor stand inventories",
name = "3d_armor_stand:update_inventories",
nodenames = {"3d_armor_stand:locked_armor_stand", "3d_armor_stand:shared_armor_stand", "3d_armor_stand:armor_stand"},
run_at_every_load = false,
action = function(pos, node)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local lists = inv:get_lists()
for _, element in pairs(elements) do
if not lists["armor_"..element] then
-- Abort to avoid item loss in case env_meta.txt is corrupted/deleted
return
end
end
inv:set_lists({main = {
lists.armor_head[1],
lists.armor_torso[1],
lists.armor_legs[1],
lists.armor_feet[1]
}})
meta:set_string("formspec", armor_stand_formspec)
update_entity(pos)
end
})
minetest.register_craft({
output = "3d_armor_stand:armor_stand",
recipe = {
{"", "group:fence", ""},
{"", "group:fence", ""},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
}
})
minetest.register_craft({
output = "3d_armor_stand:locked_armor_stand",
recipe = {
{"3d_armor_stand:armor_stand", "default:steel_ingot"},
}
})
minetest.register_craft({
output = "3d_armor_stand:shared_armor_stand",
recipe = {
{"3d_armor_stand:armor_stand", "default:copper_ingot"},
}
})