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) function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
-- Call on_rightclick if the pointed node defines it -- Call on_rightclick if the pointed node defines it
if pointed_thing and pointed_thing.type == "node" then 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 player and not player:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then local nodedef = minetest.registered_nodes[node.name]
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack 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 end
end end

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

@ -1,60 +1,41 @@
local S = minetest.get_translator("mcl_armor_stand") local S = minetest.get_translator("mcl_armor_stand")
local elements = {"head", "torso", "legs", "feet"} -- Spawn a stand entity
local function spawn_stand_entity(pos, node)
local function get_stand_object(pos) local luaentity = minetest.add_entity(pos, "mcl_armor_stand:armor_entity"):get_luaentity()
local object = nil luaentity:update_rotation(node or minetest.get_node(pos))
local objects = minetest.get_objects_inside_radius(pos, 0.5) or {} return luaentity
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
end end
local function update_entity(pos, node) -- Find a stand entity or spawn one
local node = node or minetest.get_node(pos) local function get_stand_entity(pos, node)
local object = get_stand_object(pos) for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0)) do
if object then local luaentity = obj:get_luaentity()
if not string.find(node.name, "mcl_armor_stand:") then if luaentity and luaentity.name == "mcl_armor_stand:armor_entity" then
object:remove() return luaentity
return
end end
else
object = minetest.add_entity(pos, "mcl_armor_stand:armor_entity")
end end
if object then return spawn_stand_entity(pos, node)
local yaw = 0 end
if node.param2 then
local rot = node.param2 % 4 -- Migrate the old inventory format
if rot == 1 then local function migrate_inventory(inv)
yaw = 3 * math.pi / 2 inv:set_size("armor", 5)
elseif rot == 2 then local lists = inv:get_lists()
yaw = math.pi for name, element in pairs(mcl_armor.elements) do
elseif rot == 3 then local listname = "armor_" .. name
yaw = math.pi / 2 local list = lists[listname]
end if list then
inv:set_stack("armor", element.index, list[1])
inv:set_size(listname, 0)
end end
object:set_yaw(yaw)
mcl_armor.update(object)
end end
end end
-- Drop all armor of the armor stand on the ground -- Drop all armor on the ground when it got destroyed
local drop_armor = function(pos) local function drop_inventory(pos)
local meta = minetest.get_meta(pos) local inv = minetest.get_meta(pos):get_inventory()
local inv = meta:get_inventory() for _, stack in pairs(inv:get_list("armor")) do
for _, element in pairs(elements) do
local stack = inv:get_stack("armor_"..element, 1)
if not stack:is_empty() then 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} 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) minetest.add_item(p, stack)
@ -87,136 +68,27 @@ minetest.register_node("mcl_armor_stand:armor_stand", {
_mcl_hardness = 2, _mcl_hardness = 2,
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) spawn_stand_entity(pos)
local inv = meta:get_inventory() end,
for _, element in pairs(elements) do on_destruct = function(pos)
inv:set_size("armor_"..element, 1) drop_inventory(pos)
end
end, 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) on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
print(pos, node, clicker, itemstack, pointed_thing)
local protname = clicker:get_player_name() local protname = clicker:get_player_name()
if minetest.is_protected(pos, protname) then if minetest.is_protected(pos, protname) then
minetest.record_protection_violation(pos, protname) minetest.record_protection_violation(pos, protname)
return itemstack return itemstack
end end
local inv = minetest.get_inventory({type = "node", pos = pos}) return mcl_armor.equip(itemstack, get_stand_entity(pos, node).object, true)
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
end, end,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_FACE then if mode == screwdriver.ROTATE_FACE then
node.param2 = (node.param2 + 1) % 4 node.param2 = (node.param2 + 1) % 4
minetest.swap_node(pos, node) minetest.swap_node(pos, node)
update_entity(pos) get_stand_entity(pos, node):update_rotation(node)
return true return true
end end
return false return false
@ -224,52 +96,43 @@ minetest.register_node("mcl_armor_stand:armor_stand", {
}) })
minetest.register_entity("mcl_armor_stand:armor_entity", { minetest.register_entity("mcl_armor_stand:armor_entity", {
physical = true, initial_properties = {
visual = "mesh", physical = true,
mesh = "3d_armor_entity.obj", visual = "mesh",
visual_size = {x=1, y=1}, mesh = "3d_armor_entity.obj",
collisionbox = {-0.1,-0.4,-0.1, 0.1,1.3,0.1}, visual_size = {x=1, y=1},
pointable = false, collisionbox = {-0.1,-0.4,-0.1, 0.1,1.3,0.1},
textures = {"blank.png"}, pointable = false,
pos = nil, textures = {"blank.png"},
timer = 0, timer = 0,
on_activate = function(self, staticdata) static_save = false,
self.object:set_armor_groups({immortal=1}) },
local pos = self.object:get_pos() on_activate = function(self)
self.pos = vector.round(pos) self.object:set_armor_groups({immortal = 1})
self.inventory = minetest.get_meta(pos):get_inventory() self.node_pos = vector.round(self.object:get_pos())
update_entity(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, end,
update_armor = function(self, info) update_armor = function(self, info)
self.object:set_properties({textures = {info.texture}}) self.object:set_properties({textures = {info.texture}})
end, end,
on_step = function(self, dtime) update_rotation = function(self, node)
if not self.pos then self.object:set_yaw(minetest.dir_to_yaw(minetest.facedir_to_dir(node.param2)))
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
end, end,
}) })
-- FIXME: Armor helper entity can get destroyed by /clearobjects
minetest.register_lbm({ minetest.register_lbm({
label = "Respawn armor stand entities", label = "Respawn armor stand entities",
name = "mcl_armor_stand:respawn_entities", name = "mcl_armor_stand:respawn_entities",
nodenames = {"mcl_armor_stand:armor_stand"}, nodenames = {"mcl_armor_stand:armor_stand"},
run_at_every_load = true, run_at_every_load = true,
action = function(pos, node) action = function(pos, node)
update_entity(pos, node) spawn_stand_entity(pos, node)
end, end,
}) })
@ -282,7 +145,6 @@ minetest.register_craft({
} }
}) })
-- Legacy handling -- Legacy handling
minetest.register_alias("3d_armor_stand:armor_stand", "mcl_armor_stand:armor_stand") minetest.register_alias("3d_armor_stand:armor_stand", "mcl_armor_stand:armor_stand")
minetest.register_entity(":3d_armor_stand:armor_entity", { minetest.register_entity(":3d_armor_stand:armor_entity", {