visible_wielditem/init.lua

168 lines
5.1 KiB
Lua

visible_wielditem = {}
local entity_name = "visible_wielditem:visible_wielditem"
local entities = {}
local function create_entity(player)
local pos = player:get_pos()
if not pos then return end -- HACK deal with player object being invalidated before on_leaveplayer has been called
minetest.add_entity(pos, entity_name):get_luaentity():_set_player(player)
end
minetest.register_on_joinplayer(create_entity)
minetest.register_on_leaveplayer(function(player)
entities[player:get_player_name()]:_remove()
end)
visible_wielditem.model_attachments = {
default = {
bonename = "Arm_Right",
position = vector.new(0, 0.375, 0),
rotation = vector.new(180, 90, 0),
scale = 0.25
}
}
visible_wielditem.item_tweaks = {
types = {
unknown = {},
node = {
position = vector.new(0, 0.2, 0),
rotation = vector.new(0, 0, 180)
},
tool = {
position = vector.new(0, 0, 0.3),
rotation = vector.new(0, 0, 135)
},
craft = {
position = vector.new(0, 0, 0.3),
rotation = vector.new(0, 0, 45),
}
},
groups = {},
names = {}
}
local quaternion_from_euler_rot_deg = modlib.quaternion.from_euler_rotation_deg
local quaternion_to_euler_rot_deg = modlib.quaternion.to_euler_rotation_irrlicht
-- HACK quaternion multiplication should be fixed in modlib eventually
local function quaternion_compose(other, self)
local X, Y, Z, W = unpack(self)
return modlib.quaternion.normalize{
(other[4] * X) + (other[1] * W) + (other[2] * Z) - (other[3] * Y);
(other[4] * Y) + (other[2] * W) + (other[3] * X) - (other[1] * Z);
(other[4] * Z) + (other[3] * W) + (other[1] * Y) - (other[2] * X);
(other[4] * W) - (other[1] * X) - (other[2] * Y) - (other[3] * Z);
}
end
function visible_wielditem.get_attachment(modelname, itemname)
local model_attachments = visible_wielditem.model_attachments
local attachment = modlib.table.copy(model_attachments[modelname] or model_attachments.default)
local rotation = quaternion_from_euler_rot_deg(attachment.rotation)
local function apply_tweaks(tweaks)
tweaks = tweaks or {}
if tweaks.position then attachment.position = vector.add(attachment.position, tweaks.position) end
if tweaks.rotation then
rotation = quaternion_compose(rotation, quaternion_from_euler_rot_deg(tweaks.rotation))
end
if tweaks.scale then attachment.scale = attachment.scale * tweaks.scale end
end
local def = minetest.registered_items[itemname] or {}
local item_tweaks = visible_wielditem.item_tweaks
apply_tweaks(item_tweaks.types[def.type or "unknown"])
for groupname, rating in pairs(def.groups or {}) do
if rating ~= 0 then
apply_tweaks(item_tweaks.groups[groupname])
end
end
apply_tweaks(item_tweaks.names[itemname])
attachment.rotation = quaternion_to_euler_rot_deg(rotation)
return attachment
end
minetest.register_entity(entity_name, {
initial_properties = {
physical = false,
collide_with_objects = false,
pointable = false,
visual = "wielditem" ,
wield_item = "",
is_visible = false,
backface_culling = false,
use_texture_alpha = true,
glow = 0,
static_save = false,
shaded = true
},
on_activate = function(self)
local object = self.object
object:set_armor_groups{immortal = 1}
end,
_force_resend = function(self) -- HACK
self.object:set_pos(self.object:get_pos())
end,
_update_attachment = function(self)
if not self._item then return end
local object = self.object
local attachment = visible_wielditem.get_attachment(self._player:get_properties().mesh, self._item:get_name() or "")
-- TODO softcode
local stack_scale = 1
if self._item:get_definition().type ~= "tool" then
stack_scale = 0.75 + 0.5 * math.min(1, self._item:get_count() / self._item:get_stack_max())
end
local vsize = attachment.scale * stack_scale
object:set_properties{visual_size = vector.new(vsize, vsize, vsize)}
object:set_attach(self._player,
attachment.bonename,
vector.multiply(vector.offset(attachment.position, 0, vsize / 2, 0), 10),
attachment.rotation)
self:_force_resend()
end,
_set_player = function(self, player)
self._player = player -- HACK this assumes that PlayerRefs don't change
self:_set_item(player:get_wielded_item())
self:_update_attachment()
entities[player:get_player_name()] = self
end,
on_deactivate = function(self)
create_entity(self._player)
end,
_set_item = function(self, item)
self._item = item
local object = self.object
if item:is_empty() then
object:set_properties{
is_visible = false,
wield_item = ""
}
return
end
object:set_properties{
is_visible = true,
wield_item = item:to_string(),
glow = item:get_definition().light_source or 0
}
self:_update_attachment()
end,
_remove = function(self)
self.on_deactivate = modlib.func.no_op -- don't recreate entity; it's supposed to be removed
self.object:remove()
entities[self._player:get_player_name()] = nil
end
-- TODO on_step: reattach regularly to work around engine bugs?
})
modlib.minetest.register_on_wielditem_change(function(player, _, _, item)
local entity = entities[player:get_player_name()]
if entity.object:get_pos() then
entity:_set_item(item)
else -- recreate entity if necessary
create_entity(player)
end
end)