From 4f0bb444fed5548ea1c1bb308519c57706a2f5fb Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Sun, 18 Apr 2021 16:03:23 +0200 Subject: [PATCH] Integrate armor stand --- mods/CORE/mcl_util/init.lua | 9 +- mods/ITEMS/mcl_armor/api.lua | 24 ++- mods/ITEMS/mcl_armor_stand/init.lua | 258 +++++++--------------------- 3 files changed, 85 insertions(+), 206 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 55f308602c..49d1c82a15 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -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 diff --git a/mods/ITEMS/mcl_armor/api.lua b/mods/ITEMS/mcl_armor/api.lua index 9f9384c6dd..c3a84f2659 100644 --- a/mods/ITEMS/mcl_armor/api.lua +++ b/mods/ITEMS/mcl_armor/api.lua @@ -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 diff --git a/mods/ITEMS/mcl_armor_stand/init.lua b/mods/ITEMS/mcl_armor_stand/init.lua index 9a5a6d0508..8bcb061340 100644 --- a/mods/ITEMS/mcl_armor_stand/init.lua +++ b/mods/ITEMS/mcl_armor_stand/init.lua @@ -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", {