Fix negative offsets not being supported by container[]
[oweals/minetest.git] / builtin / game / falling.lua
index 7649093613351f298d25741d0e073579b76e571a..df213e304f0912ec9df80304a13eca672717e94b 100644 (file)
@@ -18,9 +18,11 @@ core.register_entity(":__builtin:falling_node", {
        },
 
        node = {},
+       meta = {},
 
-       set_node = function(self, node)
+       set_node = function(self, node, meta)
                self.node = node
+               self.meta = meta or {}
                self.object:set_properties({
                        is_visible = true,
                        textures = {node.name},
@@ -28,15 +30,21 @@ core.register_entity(":__builtin:falling_node", {
        end,
 
        get_staticdata = function(self)
-               return core.serialize(self.node)
+               local ds = {
+                       node = self.node,
+                       meta = self.meta,
+               }
+               return core.serialize(ds)
        end,
 
        on_activate = function(self, staticdata)
                self.object:set_armor_groups({immortal = 1})
-               
-               local node = core.deserialize(staticdata)
-               if node then
-                       self:set_node(node)
+
+               local ds = core.deserialize(staticdata)
+               if ds and ds.node then
+                       self:set_node(ds.node, ds.meta)
+               elseif ds then
+                       self:set_node(ds)
                elseif staticdata ~= "" then
                        self:set_node({name = staticdata})
                end
@@ -44,16 +52,21 @@ core.register_entity(":__builtin:falling_node", {
 
        on_step = function(self, dtime)
                -- Set gravity
-               local acceleration = self.object:getacceleration()
+               local acceleration = self.object:get_acceleration()
                if not vector.equals(acceleration, {x = 0, y = -10, z = 0}) then
-                       self.object:setacceleration({x = 0, y = -10, z = 0})
+                       self.object:set_acceleration({x = 0, y = -10, z = 0})
                end
                -- Turn to actual node when colliding with ground, or continue to move
-               local pos = self.object:getpos()
+               local pos = self.object:get_pos()
                -- Position of bottom center point
                local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z}
-               -- Avoid bugs caused by an unloaded node below
+               -- 'bcn' is nil for unloaded nodes
                local bcn = core.get_node_or_nil(bcp)
+               -- Delete on contact with ignore at world edges
+               if bcn and bcn.name == "ignore" then
+                       self.object:remove()
+                       return
+               end
                local bcd = bcn and core.registered_nodes[bcn.name]
                if bcn and
                                (not bcd or bcd.walkable or
@@ -85,7 +98,7 @@ core.register_entity(":__builtin:falling_node", {
                                core.remove_node(np)
                                if nd and nd.buildable_to == false then
                                        -- Add dropped items
-                                       local drops = core.get_node_drops(n2.name, "")
+                                       local drops = core.get_node_drops(n2, "")
                                        for _, dropped_item in pairs(drops) do
                                                core.add_item(np, dropped_item)
                                        end
@@ -96,26 +109,46 @@ core.register_entity(":__builtin:falling_node", {
                                end
                        end
                        -- Create node and remove entity
-                       if core.registered_nodes[self.node.name] then
+                       local def = core.registered_nodes[self.node.name]
+                       if def then
                                core.add_node(np, self.node)
+                               if self.meta then
+                                       local meta = core.get_meta(np)
+                                       meta:from_table(self.meta)
+                               end
+                               if def.sounds and def.sounds.place then
+                                       core.sound_play(def.sounds.place, {pos = np})
+                               end
                        end
                        self.object:remove()
                        core.check_for_falling(np)
                        return
                end
-               local vel = self.object:getvelocity()
+               local vel = self.object:get_velocity()
                if vector.equals(vel, {x = 0, y = 0, z = 0}) then
-                       local npos = self.object:getpos()
-                       self.object:setpos(vector.round(npos))
+                       local npos = self.object:get_pos()
+                       self.object:set_pos(vector.round(npos))
                end
        end
 })
 
-local function spawn_falling_node(p, node)
-       local obj = core.add_entity(p, "__builtin:falling_node")
-       if obj then
-               obj:get_luaentity():set_node(node)
+local function convert_to_falling_node(pos, node)
+       local obj = core.add_entity(pos, "__builtin:falling_node")
+       if not obj then
+               return false
+       end
+       node.level = core.get_node_level(pos)
+       local meta = core.get_meta(pos)
+       local metatable = meta and meta:to_table() or {}
+
+       local def = core.registered_nodes[node.name]
+       if def and def.sounds and def.sounds.fall then
+               core.sound_play(def.sounds.fall, {pos = pos})
        end
+
+       obj:get_luaentity():set_node(node, metatable)
+       core.remove_node(pos)
+       return true
 end
 
 function core.spawn_falling_node(pos)
@@ -123,19 +156,30 @@ function core.spawn_falling_node(pos)
        if node.name == "air" or node.name == "ignore" then
                return false
        end
-       local obj = core.add_entity(pos, "__builtin:falling_node")
-       if obj then
-               obj:get_luaentity():set_node(node)
-               core.remove_node(pos)
-               return true
-       end
-       return false
+       return convert_to_falling_node(pos, node)
 end
 
 local function drop_attached_node(p)
-       local nn = core.get_node(p).name
+       local n = core.get_node(p)
+       local drops = core.get_node_drops(n, "")
+       local def = core.registered_items[n.name]
+       if def and def.preserve_metadata then
+               local oldmeta = core.get_meta(p):to_table().fields
+               -- Copy pos and node because the callback can modify them.
+               local pos_copy = {x=p.x, y=p.y, z=p.z}
+               local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
+               local drop_stacks = {}
+               for k, v in pairs(drops) do
+                       drop_stacks[k] = ItemStack(v)
+               end
+               drops = drop_stacks
+               def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
+       end
+       if def and def.sounds and def.sounds.fall then
+               core.sound_play(def.sounds.fall, {pos = p})
+       end
        core.remove_node(p)
-       for _, item in pairs(core.get_node_drops(nn, "")) do
+       for _, item in pairs(drops) do
                local pos = {
                        x = p.x + math.random()/2 - 0.25,
                        y = p.y + math.random()/2 - 0.25,
@@ -188,9 +232,7 @@ function core.check_single_for_falling(p)
                                core.get_node_max_level(p_bottom))) and
 
                                (not d_bottom.walkable or d_bottom.buildable_to) then
-                       n.level = core.get_node_level(p)
-                       core.remove_node(p)
-                       spawn_falling_node(p, n)
+                       convert_to_falling_node(p, n)
                        return true
                end
        end
@@ -291,19 +333,3 @@ local function on_punchnode(p, node)
        core.check_for_falling(p)
 end
 core.register_on_punchnode(on_punchnode)
-
---
--- Globally exported functions
---
-
--- TODO remove this function after the 0.4.15 release
-function nodeupdate(p)
-       core.log("deprecated", "nodeupdate: deprecated, please use core.check_for_falling instead")
-       core.check_for_falling(p)
-end
-
--- TODO remove this function after the 0.4.15 release
-function nodeupdate_single(p)
-       core.log("deprecated", "nodeupdate_single: deprecated, please use core.check_single_for_falling instead")
-       core.check_single_for_falling(p)
-end