diff options
-rw-r--r-- | builtin/game/item_entity.lua | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index a330e8723..87fec93ea 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -35,7 +35,12 @@ core.register_entity(":__builtin:item", { itemstring = "", moving_state = true, slippery_state = false, + physical_state = true, + -- Item expiry age = 0, + -- Pushing item out of solid nodes + force_out = nil, + force_out_start = nil, set_item = function(self, item) local stack = ItemStack(item or self.itemstring) @@ -131,6 +136,24 @@ core.register_entity(":__builtin:item", { return true end, + enable_physics = function(self) + if not self.physical_state then + self.physical_state = true + self.object:set_properties({physical = true}) + self.object:set_velocity({x=0, y=0, z=0}) + self.object:set_acceleration({x=0, y=-gravity, z=0}) + end + end, + + disable_physics = function(self) + if self.physical_state then + self.physical_state = false + self.object:set_properties({physical = false}) + self.object:set_velocity({x=0, y=0, z=0}) + self.object:set_acceleration({x=0, y=0, z=0}) + end + end, + on_step = function(self, dtime) self.age = self.age + dtime if time_to_live > 0 and self.age > time_to_live then @@ -152,6 +175,74 @@ core.register_entity(":__builtin:item", { return end + local is_stuck = false + local snode = core.get_node_or_nil(pos) + if snode then + local sdef = core.registered_nodes[snode.name] or {} + is_stuck = (sdef.walkable == nil or sdef.walkable == true) + and (sdef.collision_box == nil or sdef.collision_box.type == "regular") + and (sdef.node_box == nil or sdef.node_box.type == "regular") + end + + -- Push item out when stuck inside solid node + if is_stuck then + local shootdir + local order = { + {x=1, y=0, z=0}, {x=-1, y=0, z= 0}, + {x=0, y=0, z=1}, {x= 0, y=0, z=-1}, + } + + -- Check which one of the 4 sides is free + for o = 1, #order do + local cnode = core.get_node(vector.add(pos, order[o])).name + local cdef = core.registered_nodes[cnode] or {} + if cnode ~= "ignore" and cdef.walkable == false then + shootdir = order[o] + break + end + end + -- If none of the 4 sides is free, check upwards + if not shootdir then + shootdir = {x=0, y=1, z=0} + local cnode = core.get_node(vector.add(pos, shootdir)).name + if cnode == "ignore" then + shootdir = nil -- Do not push into ignore + end + end + + if shootdir then + -- Set new item moving speed accordingly + local newv = vector.multiply(shootdir, 3) + self:disable_physics() + self.object:set_velocity(newv) + + self.force_out = newv + self.force_out_start = vector.round(pos) + return + end + elseif self.force_out then + -- This code runs after the entity got a push from the above code. + -- It makes sure the entity is entirely outside the solid node + local c = self.object:get_properties().collisionbox + local s = self.force_out_start + local f = self.force_out + local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or + (f.y > 0 and pos.y + c[2] > s.y + 0.5) or + (f.z > 0 and pos.z + c[3] > s.z + 0.5) or + (f.x < 0 and pos.x + c[4] < s.x - 0.5) or + (f.z < 0 and pos.z + c[6] < s.z - 0.5) + if ok then + -- Item was successfully forced out + self.force_out = nil + self:enable_physics() + end + end + + if not self.physical_state then + return -- Don't do anything + end + + -- Slide on slippery nodes local vel = self.object:get_velocity() local def = node and core.registered_nodes[node.name] local is_moving = (def and not def.walkable) or |