@@ -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