Integrate armor stand

This commit is contained in:
Elias Fleckenstein 2021-04-18 16:03:23 +02:00
parent f0d7715080
commit 4f0bb444fe
3 changed files with 85 additions and 206 deletions

@ -422,10 +422,13 @@ end
function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
-- Call on_rightclick if the pointed node defines it
if pointed_thing and pointed_thing.type == "node" then
local node = minetest.get_node(pointed_thing.under)
local pos = pointed_thing.under
local node = minetest.get_node(pos)
if player and not player:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
local nodedef = minetest.registered_nodes[node.name]
local on_rightclick = nodedef and nodedef.on_rightclick
if on_rightclick then
return on_rightclick(pos, node, player, itemstack, pointed_thing) or itemstack
end
end
end

@ -36,16 +36,30 @@ function mcl_armor.on_unequip(itemstack, obj)
mcl_armor.update(obj)
end
function mcl_armor.equip(itemstack, obj)
function mcl_armor.equip(itemstack, obj, swap)
local def = itemstack:get_definition()
if not def then
return itemstack
end
local element = mcl_armor.elements[def._mcl_armor_element or ""]
local inv = mcl_util.get_inventory(obj)
if element and inv then
if inv:get_stack("armor", element.index):is_empty() then
local equipping_item = itemstack:take_item()
inv:set_stack("armor", element.index, equipping_item)
mcl_armor.on_equip(equipping_item, obj)
local old_stack = inv:get_stack("armor", element.index)
local new_stack
if swap then
new_stack = itemstack
itemstack = old_stack
else
new_stack = itemstack:take_item()
end
if swap or old_stack:is_empty() then
inv:set_stack("armor", element.index, new_stack)
mcl_armor.on_equip(new_stack, obj)
end
end

@ -1,60 +1,41 @@
local S = minetest.get_translator("mcl_armor_stand")
local elements = {"head", "torso", "legs", "feet"}
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 == "mcl_armor_stand:armor_entity" then
-- Remove duplicates
if object then
obj:remove()
else
object = obj
end
end
end
end
return object
-- Spawn a stand entity
local function spawn_stand_entity(pos, node)
local luaentity = minetest.add_entity(pos, "mcl_armor_stand:armor_entity"):get_luaentity()
luaentity:update_rotation(node or minetest.get_node(pos))
return luaentity
end
local function update_entity(pos, node)
local node = node or minetest.get_node(pos)
local object = get_stand_object(pos)
if object then
if not string.find(node.name, "mcl_armor_stand:") then
object:remove()
return
-- Find a stand entity or spawn one
local function get_stand_entity(pos, node)
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0)) do
local luaentity = obj:get_luaentity()
if luaentity and luaentity.name == "mcl_armor_stand:armor_entity" then
return luaentity
end
else
object = minetest.add_entity(pos, "mcl_armor_stand:armor_entity")
end
if object then
local yaw = 0
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
return spawn_stand_entity(pos, node)
end
-- Migrate the old inventory format
local function migrate_inventory(inv)
inv:set_size("armor", 5)
local lists = inv:get_lists()
for name, element in pairs(mcl_armor.elements) do
local listname = "armor_" .. name
local list = lists[listname]
if list then
inv:set_stack("armor", element.index, list[1])
inv:set_size(listname, 0)
end
object:set_yaw(yaw)
mcl_armor.update(object)
end
end
-- Drop all armor of the armor stand on the ground
local drop_armor = function(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)
-- Drop all armor on the ground when it got destroyed
local function drop_inventory(pos)
local inv = minetest.get_meta(pos):get_inventory()
for _, stack in pairs(inv:get_list("armor")) do
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
minetest.add_item(p, stack)
@ -87,136 +68,27 @@ minetest.register_node("mcl_armor_stand:armor_stand", {
_mcl_hardness = 2,
sounds = mcl_sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
for _, element in pairs(elements) do
inv:set_size("armor_"..element, 1)
end
spawn_stand_entity(pos)
end,
on_destruct = function(pos)
drop_inventory(pos)
end,
-- Drop all armor on the ground when it got destroyed
on_destruct = drop_armor,
-- Put piece of armor on armor stand, or take one away
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
print(pos, node, clicker, itemstack, pointed_thing)
local protname = clicker:get_player_name()
if minetest.is_protected(pos, protname) then
minetest.record_protection_violation(pos, protname)
return itemstack
end
local inv = minetest.get_inventory({type = "node", pos = pos})
if not inv then
return itemstack
end
-- Check if player wields armor
local name = itemstack:get_name()
local list
for e=1, #elements do
local g = minetest.get_item_group(name, "armor_" .. elements[e])
if g ~= nil and g ~= 0 then
list = "armor_" .. elements[e]
break
end
end
-- If player wields armor, put it on armor stand
local wielditem = clicker:get_wielded_item()
if list then
-- ... but only if the slot is free
local single_item = ItemStack(itemstack)
single_item:set_count(1)
if inv:is_empty(list) then
inv:add_item(list, single_item)
mcl_armor.play_equip_sound(single_item, nil, pos)
update_entity(pos)
itemstack:take_item()
return itemstack
end
end
-- Take armor from stand if player has a free hand or wields the same armor type (if stackable)
for e=1, #elements do
local stand_armor = inv:get_stack("armor_" .. elements[e], 1)
if not stand_armor:is_empty() then
local pinv = clicker:get_inventory()
local taken = false
-- Empty hand
if wielditem:get_name() == "" then
pinv:set_stack("main", clicker:get_wield_index(), stand_armor)
taken = true
-- Stackable armor type (if not already full). This is the case for e.g. mob heads.
-- This is done purely for convenience.
elseif (wielditem:get_name() == stand_armor:get_name() and wielditem:get_count() < wielditem:get_stack_max()) then
wielditem:set_count(wielditem:get_count()+1)
pinv:set_stack("main", clicker:get_wield_index(), wielditem)
taken = true
end
if taken then
mcl_armor.play_equip_sound(stand_armor, nil, pos, true)
stand_armor:take_item()
inv:set_stack("armor_" .. elements[e], 1, stand_armor)
end
update_entity(pos)
return clicker:get_wielded_item()
end
end
update_entity(pos)
return itemstack
end,
after_place_node = function(pos)
minetest.add_entity(pos, "mcl_armor_stand:armor_entity")
end,
allow_metadata_inventory_take = 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
else
return stack:get_count()
end
end,
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
local def = stack:get_definition() or {}
local groups = def.groups or {}
if groups[listname] then
return 1
end
return 0
end,
allow_metadata_inventory_move = function()
return 0
end,
on_metadata_inventory_put = function(pos)
update_entity(pos)
end,
on_metadata_inventory_take = function(pos)
update_entity(pos)
end,
after_destruct = function(pos)
update_entity(pos)
end,
on_blast = function(pos, _, do_drop)
local object = get_stand_object(pos)
if object then
object:remove()
end
minetest.after(1, function(pos)
update_entity(pos)
end, pos)
minetest.remove_node(pos)
if do_drop then
minetest.add_item(pos, "mcl_armor_stand:armor_stand")
end
return mcl_armor.equip(itemstack, get_stand_entity(pos, node).object, true)
end,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_FACE then
node.param2 = (node.param2 + 1) % 4
minetest.swap_node(pos, node)
update_entity(pos)
get_stand_entity(pos, node):update_rotation(node)
return true
end
return false
@ -224,52 +96,43 @@ minetest.register_node("mcl_armor_stand:armor_stand", {
})
minetest.register_entity("mcl_armor_stand:armor_entity", {
physical = true,
visual = "mesh",
mesh = "3d_armor_entity.obj",
visual_size = {x=1, y=1},
collisionbox = {-0.1,-0.4,-0.1, 0.1,1.3,0.1},
pointable = false,
textures = {"blank.png"},
pos = nil,
timer = 0,
on_activate = function(self, staticdata)
self.object:set_armor_groups({immortal=1})
local pos = self.object:get_pos()
self.pos = vector.round(pos)
self.inventory = minetest.get_meta(pos):get_inventory()
update_entity(pos)
initial_properties = {
physical = true,
visual = "mesh",
mesh = "3d_armor_entity.obj",
visual_size = {x=1, y=1},
collisionbox = {-0.1,-0.4,-0.1, 0.1,1.3,0.1},
pointable = false,
textures = {"blank.png"},
timer = 0,
static_save = false,
},
on_activate = function(self)
self.object:set_armor_groups({immortal = 1})
self.node_pos = vector.round(self.object:get_pos())
self.inventory = minetest.get_meta(self.node_pos):get_inventory()
migrate_inventory(self.inventory)
end,
on_step = function(self, dtime)
if minetest.get_node(self.node_pos).name ~= "mcl_armor_stand:armor_stand" then
self.object:remove()
end
end,
update_armor = function(self, info)
self.object:set_properties({textures = {info.texture}})
end,
on_step = function(self, dtime)
if not self.pos then
return
end
self.timer = self.timer + dtime
if self.timer > 1 then
self.timer = 0
local pos = self.object:get_pos()
if pos then
if vector.equals(vector.round(pos), self.pos) then
return
end
end
update_entity(self.pos)
self.object:remove()
end
update_rotation = function(self, node)
self.object:set_yaw(minetest.dir_to_yaw(minetest.facedir_to_dir(node.param2)))
end,
})
-- FIXME: Armor helper entity can get destroyed by /clearobjects
minetest.register_lbm({
label = "Respawn armor stand entities",
name = "mcl_armor_stand:respawn_entities",
nodenames = {"mcl_armor_stand:armor_stand"},
run_at_every_load = true,
action = function(pos, node)
update_entity(pos, node)
spawn_stand_entity(pos, node)
end,
})
@ -282,7 +145,6 @@ minetest.register_craft({
}
})
-- Legacy handling
minetest.register_alias("3d_armor_stand:armor_stand", "mcl_armor_stand:armor_stand")
minetest.register_entity(":3d_armor_stand:armor_entity", {