Skip to content

Commit c9ec1a8

Browse files
authored
Force item entities out of solid nodes (simpler) (#8885)
1 parent 4582691 commit c9ec1a8

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

builtin/game/item_entity.lua

+91
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ core.register_entity(":__builtin:item", {
3535
itemstring = "",
3636
moving_state = true,
3737
slippery_state = false,
38+
physical_state = true,
39+
-- Item expiry
3840
age = 0,
41+
-- Pushing item out of solid nodes
42+
force_out = nil,
43+
force_out_start = nil,
3944

4045
set_item = function(self, item)
4146
local stack = ItemStack(item or self.itemstring)
@@ -131,6 +136,24 @@ core.register_entity(":__builtin:item", {
131136
return true
132137
end,
133138

139+
enable_physics = function(self)
140+
if not self.physical_state then
141+
self.physical_state = true
142+
self.object:set_properties({physical = true})
143+
self.object:set_velocity({x=0, y=0, z=0})
144+
self.object:set_acceleration({x=0, y=-gravity, z=0})
145+
end
146+
end,
147+
148+
disable_physics = function(self)
149+
if self.physical_state then
150+
self.physical_state = false
151+
self.object:set_properties({physical = false})
152+
self.object:set_velocity({x=0, y=0, z=0})
153+
self.object:set_acceleration({x=0, y=0, z=0})
154+
end
155+
end,
156+
134157
on_step = function(self, dtime)
135158
self.age = self.age + dtime
136159
if time_to_live > 0 and self.age > time_to_live then
@@ -152,6 +175,74 @@ core.register_entity(":__builtin:item", {
152175
return
153176
end
154177

178+
local is_stuck = false
179+
local snode = core.get_node_or_nil(pos)
180+
if snode then
181+
local sdef = core.registered_nodes[snode.name] or {}
182+
is_stuck = (sdef.walkable == nil or sdef.walkable == true)
183+
and (sdef.collision_box == nil or sdef.collision_box.type == "regular")
184+
and (sdef.node_box == nil or sdef.node_box.type == "regular")
185+
end
186+
187+
-- Push item out when stuck inside solid node
188+
if is_stuck then
189+
local shootdir
190+
local order = {
191+
{x=1, y=0, z=0}, {x=-1, y=0, z= 0},
192+
{x=0, y=0, z=1}, {x= 0, y=0, z=-1},
193+
}
194+
195+
-- Check which one of the 4 sides is free
196+
for o = 1, #order do
197+
local cnode = core.get_node(vector.add(pos, order[o])).name
198+
local cdef = core.registered_nodes[cnode] or {}
199+
if cnode ~= "ignore" and cdef.walkable == false then
200+
shootdir = order[o]
201+
break
202+
end
203+
end
204+
-- If none of the 4 sides is free, check upwards
205+
if not shootdir then
206+
shootdir = {x=0, y=1, z=0}
207+
local cnode = core.get_node(vector.add(pos, shootdir)).name
208+
if cnode == "ignore" then
209+
shootdir = nil -- Do not push into ignore
210+
end
211+
end
212+
213+
if shootdir then
214+
-- Set new item moving speed accordingly
215+
local newv = vector.multiply(shootdir, 3)
216+
self:disable_physics()
217+
self.object:set_velocity(newv)
218+
219+
self.force_out = newv
220+
self.force_out_start = vector.round(pos)
221+
return
222+
end
223+
elseif self.force_out then
224+
-- This code runs after the entity got a push from the above code.
225+
-- It makes sure the entity is entirely outside the solid node
226+
local c = self.object:get_properties().collisionbox
227+
local s = self.force_out_start
228+
local f = self.force_out
229+
local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
230+
(f.y > 0 and pos.y + c[2] > s.y + 0.5) or
231+
(f.z > 0 and pos.z + c[3] > s.z + 0.5) or
232+
(f.x < 0 and pos.x + c[4] < s.x - 0.5) or
233+
(f.z < 0 and pos.z + c[6] < s.z - 0.5)
234+
if ok then
235+
-- Item was successfully forced out
236+
self.force_out = nil
237+
self:enable_physics()
238+
end
239+
end
240+
241+
if not self.physical_state then
242+
return -- Don't do anything
243+
end
244+
245+
-- Slide on slippery nodes
155246
local vel = self.object:get_velocity()
156247
local def = node and core.registered_nodes[node.name]
157248
local is_moving = (def and not def.walkable) or

0 commit comments

Comments
 (0)