EinsDreiDreiSieben/mods/item_physics/init.lua
2025-05-11 00:42:25 +02:00

139 lines
No EOL
4.8 KiB
Lua

minetest.register_on_mods_loaded(function()
local builtin_item = minetest.registered_entities["__builtin:item"]
item_physics = {}
item_physics.settings = {
item_scale = tonumber(minetest.settings:get("item_physics_item_scale")) or nil,
rotation = minetest.settings:get_bool("item_physics_rotation", true),
initial_rotation = minetest.settings:get_bool("item_physics_initial_rotation", true),
}
if (item_physics.settings.item_scale ~= nil) and item_physics.settings.item_scale <= 0.05 then
item_physics.settings.item_scale = nil
end
local flat_node_types = {
signlike = true,
raillike = true,
torchlike = true,
plantlike = true,
}
-- rot = vector.new(0,0,1):rotate(rot)
local new_item_ent = {
_get_is_node = function(self, itemname)
local idef = minetest.registered_items[itemname]
if not idef then return false end
if idef.type ~= "node" then return false end
if flat_node_types[idef.drawtype] ~= nil then return false end
if idef.wield_image ~= "" then return false end
return true
end,
_item_stick = function(self)
self._is_stuck = true
if (not self._is_node) then
local rot = self.object:get_rotation()
rot = vector.new(math.pi*0.5, rot.y, 0)
self.object:set_rotation(rot)
end
end,
_item_unstick = function(self)
self._is_stuck = false
local rot = self.object:get_rotation()
rot = vector.new(0, rot.y, 0)
self.object:set_rotation(rot)
end,
enable_physics = function(self)
local ret = builtin_item.enable_physics(self)
self:_item_unstick()
return ret
end,
disable_physics = function(self)
local ret = builtin_item.disable_physics(self)
self:_item_stick()
return ret
end,
set_item = function(self, item, ...)
local ret = builtin_item.set_item(self, item, ...)
local stack = ItemStack(item or self.itemstring)
local itemname = stack:get_name()
self._is_node = self:_get_is_node(itemname)
local props = self.object:get_properties()
if not props.collisionbox then
props.collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3}
end
-- allow custom size
if item_physics.settings.item_scale then
local s = item_physics.settings.item_scale
local c = s - 0.1
props.collisionbox = {-c,-c,-c, c, c, c}
props.visual_size = vector.new(s,s,s)
else
-- use the other sizes as a reference to make it compat with more things
props.collisionbox[2] = props.collisionbox[1] * 0.6
end
-- items lie flat
if (not self._is_node) then
props.collisionbox[2] = -0.03
end
-- remove `automatic_rotate` because it's broken
self.object:set_properties({
automatic_rotate = 0,
collisionbox = props.collisionbox,
visual_size = props.visual_size,
})
if item_physics.settings.initial_rotation then
self.object:set_rotation(vector.new(0,math.random()*math.pi*2, 0))
end
if self._is_stuck then
self:_item_stick()
else
self:_item_unstick()
end
return ret
end,
try_merge_with = function(self, own_stack, object, entity, ...)
return builtin_item.try_merge_with(self, own_stack, object, entity, ...)
end,
_do_idle_rotation = function(self, amount)
local rot = self.object:get_rotation()
if not rot then return end
rot = vector.new(0, rot.y - amount * self._spin_dir, 0)
self.object:set_rotation(rot)
end,
on_step = function(self, dtime, moveresult, ...)
-- if not self.itemstring then return end
local pos = self.object:get_pos()
-- prevent unloaded nil errors just in case
if not pos then return end
local vel = self.object:get_velocity()
-- if it's not moving, stick
-- items lay flat while sliding, nodes don't
if not self._is_node then vel.x = 0; vel.z = 0; end
local should_stick = (math.abs(vel.x) + math.abs(vel.y) + math.abs(vel.z) < 0.1)
if should_stick and not self._is_stuck then
self:_item_stick()
elseif self._is_stuck and not should_stick then
self:_item_unstick()
end
if not self._spin_dir then
self._spin_dir = math.random(0,1)*2-1
end
if (not self._is_stuck) and item_physics.settings.rotation then
local spin_speed = math.min(0.3, dtime * vel:length())
self._do_idle_rotation(self, spin_speed)
end
builtin_item.on_step(self, dtime, moveresult, ...)
end,
}
setmetatable(new_item_ent, { __index = builtin_item })
minetest.register_entity(":__builtin:item", new_item_ent)
end)