254 lines
7.2 KiB
Lua
254 lines
7.2 KiB
Lua
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 == "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 = "3d_armor_trans.png"
|
|
local textures = {}
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local yaw = 0
|
|
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() or {}
|
|
local groups = def.groups or {}
|
|
if groups["armor_"..element] then
|
|
local texture = def.texture or item:gsub("%:", "_")
|
|
table.insert(textures, texture..".png")
|
|
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:setyaw(yaw)
|
|
object:set_properties({textures={texture}})
|
|
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)
|
|
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)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- FIXME: The armor stand should be an entity
|
|
minetest.register_node("3d_armor_stand:armor_stand", {
|
|
description = "Armor Stand",
|
|
_doc_items_longdesc = "An armor stand is a decorative object which can display different pieces of armor. Anything which players can wear as armor can also be put on an armor stand.",
|
|
_doc_items_usagehelp = "Hold an armor item in your hand and rightclick the armor stand to put it on the armor stand. To take a piece of armor from the armor stand, select your hand and rightclick the armor stand. You'll retrieve the first armor item from above.",
|
|
drawtype = "mesh",
|
|
mesh = "3d_armor_stand.obj",
|
|
inventory_image = "3d_armor_stand_item.png",
|
|
wield_image = "3d_armor_stand_item.png",
|
|
tiles = {"default_wood.png", "mcl_stairs_stone_slab_top.png"},
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
walkable = false,
|
|
is_ground_content = false,
|
|
stack_max = 16,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-0.5,-0.5,-0.5, 0.5,1.4,0.5}
|
|
},
|
|
-- FIXME: This should be breakable by 2 quick punches
|
|
groups = {handy=1, deco_block=1},
|
|
_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
|
|
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)
|
|
-- 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 inv = minetest.get_inventory({type = "node", pos = pos})
|
|
local wielditem = clicker:get_wielded_item()
|
|
if not inv then return end
|
|
if list then
|
|
-- ... but only if the slot is free
|
|
local single_item = itemstack:get_name()
|
|
if inv:is_empty(list) then
|
|
inv:add_item(list, single_item)
|
|
update_entity(pos)
|
|
itemstack:take_item()
|
|
return itmstack
|
|
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
|
|
stand_armor:take_item()
|
|
inv:set_stack("armor_" .. elements[e], 1, stand_armor)
|
|
update_entity(pos)
|
|
end
|
|
return clicker:get_wielded_item()
|
|
end
|
|
end
|
|
return itemstack
|
|
end,
|
|
after_place_node = function(pos)
|
|
minetest.add_entity(pos, "3d_armor_stand:armor_entity")
|
|
end,
|
|
allow_metadata_inventory_put = function(pos, listname, index, stack)
|
|
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(pos)
|
|
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)
|
|
local object = get_stand_object(pos)
|
|
if object then
|
|
object:remove()
|
|
end
|
|
minetest.after(1, function(pos)
|
|
update_entity(pos)
|
|
end, pos)
|
|
end,
|
|
})
|
|
|
|
minetest.register_entity("3d_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},
|
|
textures = {"3d_armor_trans.png"},
|
|
pos = nil,
|
|
timer = 0,
|
|
on_activate = function(self)
|
|
local pos = self.object:getpos()
|
|
self.object:set_armor_groups({immortal=1})
|
|
if pos then
|
|
self.pos = vector.round(pos)
|
|
update_entity(pos)
|
|
end
|
|
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:getpos()
|
|
if pos then
|
|
if vector.equals(vector.round(pos), self.pos) then
|
|
return
|
|
end
|
|
end
|
|
update_entity(self.pos)
|
|
self.object:remove()
|
|
end
|
|
end,
|
|
})
|
|
|
|
if minetest.get_modpath("doc_identifier") ~= nil then
|
|
doc.sub.identifier.register_object("3d_armor_stand:armor_entity", "nodes", "3d_armor_stand:armor_stand")
|
|
end
|
|
|
|
minetest.register_craft({
|
|
output = "3d_armor_stand:armor_stand",
|
|
recipe = {
|
|
{"mcl_core:stick", "mcl_core:stick", "mcl_core:stick"},
|
|
{"", "mcl_core:stick", ""},
|
|
{"mcl_core:stick", "mcl_stairs:slab_stone", "mcl_core:stick"},
|
|
}
|
|
})
|
|
|