Use velocity prediction feature zefram/timed_tube
authorZefram <zefram@fysh.org>
Wed, 16 Jul 2014 19:14:15 +0000 (20:14 +0100)
committerZefram <zefram@fysh.org>
Wed, 23 Jul 2014 14:00:37 +0000 (15:00 +0100)
The engine now permits an entity to specify two velocities, and a time
at which to switch to the second velocity.  Use this to prevent tubed
items visually continuing past the end of a tube.  Each tubed item is
configured to stop imperceptibly past the next tube node, the point at
which its next velocity will be properly assessed.

item_transport.lua
tubes.lua
wielder.lua

index f01506bc5b2494af90adf106bb8d4c89b32084da..d3e8bf9cba3cc37276d448448da2119d48f137d2 100644 (file)
@@ -18,6 +18,14 @@ local function facedir_to_right_dir(facedir)
                                                z=topdir.x*backdir.y - backdir.x*topdir.y}
 end
 
+local function vector_copy(v)
+       return { x = v.x, y = v.y, z = v.z }
+end
+
+local function rectilinear_distance(v)
+       return math.abs(v.x + v.y + v.z)
+end
+
 local fakePlayer = {
     get_player_name = function() return ":pipeworks" end,
     -- any other player functions called by allow_metadata_inventory_take anywhere...
@@ -132,7 +140,8 @@ local function grabAndFire(data,slotseq_mode,filtmeta,frominv,frominvname,frompo
                                end
                                local item1 = pipeworks.tube_item(vector.add(frompos, vector.multiply(dir, 1.4)), item)
                                item1:get_luaentity().start_pos = vector.add(frompos, dir)
-                               item1:setvelocity(dir)
+                               item1:get_luaentity().velocity = vector_copy(dir)
+                               item1:setvelocity(dir, 1.001, {x=0,y=0,z=0})
                                item1:setacceleration({x=0, y=0, z=0})
                                return true-- only fire one item, please
                        end
@@ -269,7 +278,7 @@ local function go_next(pos, velocity, stack)
        local cmeta = minetest.get_meta(pos)
        local n
        local can_go
-       local speed = math.abs(velocity.x + velocity.y + velocity.z)
+       local speed = rectilinear_distance(velocity)
        local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed,speed=speed}
        if speed >= 4.1 then
                speed = 4
@@ -363,14 +372,22 @@ minetest.register_entity("pipeworks:tubed_item", {
        end,
 
        get_staticdata = function(self)
-               if self.start_pos == nil or self.removed then
-                       return
+               if self.removed then return end
+               if self.start_pos == nil then
+                       local pos = self.object:getpos()
+                       self.start_pos = roundpos(pos)
                end
-               local velocity = self.object:getvelocity()
                self.object:setpos(self.start_pos)
+               if self.velocity == nil then
+                       local vel = self.object:getvelocity()
+                       if rectilinear_distance(vel) ~= 0 then
+                               self.velocity = vector_copy(vel)
+                               self.object:setvelocity(self.velocity, 1.001/rectilinear_distance(self.velocity), {x=0,y=0,z=0})
+                       end
+               end
                return  minetest.serialize({
                        itemstring = self.itemstring,
-                       velocity = velocity,
+                       velocity = self.velocity or self.object:getvelocity(),
                        start_pos = self.start_pos
                })
        end,
@@ -386,10 +403,13 @@ minetest.register_entity("pipeworks:tubed_item", {
                end
                
                if itemname then 
-               self.start_pos=item.start_pos
-               self.object:setvelocity(item.velocity)
-               self.object:setacceleration({x=0, y=0, z=0})
-               self.object:setpos(item.start_pos)
+                       self.start_pos = item.start_pos
+                       self.object:setpos(item.start_pos)
+                       if rectilinear_distance(item.velocity) ~= 0 then
+                               self.velocity = item.velocity
+                               self.object:setvelocity(item.velocity, 1.001/rectilinear_distance(item.velocity), {x=0,y=0,z=0})
+                       end
+                       self.object:setacceleration({x=0, y=0, z=0})
                end
                self:set_item(item.itemstring)
        end,
@@ -408,6 +428,11 @@ minetest.register_entity("pipeworks:tubed_item", {
                        local pos = self.object:getpos()
                        self.start_pos = roundpos(pos)
                end
+               if self.velocity == nil then
+                       local vel = self.object:getvelocity()
+                       if rectilinear_distance(vel) == 0 then return end
+                       self.velocity = vector_copy(vel)
+               end
                local pos = self.object:getpos()
                local node = minetest.get_node(pos)
                local meta = minetest.get_meta(pos)
@@ -415,42 +440,29 @@ minetest.register_entity("pipeworks:tubed_item", {
                local stack = ItemStack(self.itemstring)
                local drop_pos = nil
                
-               local velocity = self.object:getvelocity()
-       
-               if velocity == nil then return end
-       
-               local velocitycopy = {x = velocity.x, y = velocity.y, z = velocity.z}
-               
-               local moved = false
-               local speed = math.abs(velocity.x + velocity.y + velocity.z)
-               local vel = {x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed}
-               
-               if math.abs(vel.x) == 1 then
-                       local next_node = math.abs(pos.x - self.start_pos.x)
-                       if next_node >= 1 then 
-                               self.start_pos.x = self.start_pos.x + vel.x
-                               moved = true
-                       end
-               elseif math.abs(vel.y) == 1 then
-               local next_node = math.abs(pos.y - self.start_pos.y)
-                       if next_node >= 1 then 
-                               self.start_pos.y = self.start_pos.y + vel.y
-                               moved = true
-                       end     
-               elseif math.abs(vel.z) == 1 then
-                       local next_node = math.abs(pos.z - self.start_pos.z)
-                       if next_node >= 1 then 
-                               self.start_pos.z = self.start_pos.z + vel.z
-                               moved = true
+               local velocity = self.velocity
+               local dist_moved = rectilinear_distance(vector.subtract(pos, self.start_pos))
+               if dist_moved < 1 then
+                       if rectilinear_distance(self.object:getvelocity()) == 0 then
+                               -- Items shouldn't ever get stuck like this.
+                               -- But it's best to be paranoid about
+                               -- the engine's mathematical correctness.
+                               self.object:setvelocity(velocity, (1.001-dist_moved)/rectilinear_distance(velocity), {x=0,y=0,z=0})
                        end
+                       return
                end
-               
-               local sposcopy = {x = self.start_pos.x, y = self.start_pos.y, z = self.start_pos.z}
+
+               local speed = rectilinear_distance(velocity)
+               local dir = vector.divide(velocity, speed)
+               local velocitycopy = vector_copy(velocity)
+               self.start_pos = vector.add(self.start_pos, dir)
+               local sposcopy = vector_copy(self.start_pos)
                
                node = minetest.get_node(self.start_pos)
-               if moved and minetest.get_item_group(node.name, "tubedevice_receiver") == 1 then
+               if minetest.get_item_group(node.name, "tubedevice_receiver") == 1 then
                        local leftover = nil
                        if minetest.registered_nodes[node.name].tube and minetest.registered_nodes[node.name].tube.insert_object then
+                               local vel = { x = dir.x, y = dir.y, z = dir.z, speed = speed }
                                leftover = minetest.registered_nodes[node.name].tube.insert_object(self.start_pos, node, stack, vel)
                        else
                                leftover = stack
@@ -462,26 +474,30 @@ minetest.register_entity("pipeworks:tubed_item", {
                        velocity.x = -velocity.x
                        velocity.y = -velocity.y
                        velocity.z = -velocity.z
-                       self.object:setvelocity(velocity)
+                       self.velocity = velocity
+                       self.object:setpos(self.start_pos)
+                       self.object:setvelocity(velocity, 1.001/rectilinear_distance(velocity), { x=0, y=0, z=0 })
                        self:set_item(leftover:to_string())
                        return
                end
                
-               if moved then
-                       if go_next (self.start_pos, velocity, stack) == 0 then
-                               drop_pos = minetest.find_node_near(vector.add(self.start_pos, velocity), 1, "air")
-                               if drop_pos then 
-                                       minetest.item_drop(stack, "", drop_pos)
-                                       self:remove()
-                               end
+               if go_next (self.start_pos, velocity, stack) == 0 then
+                       drop_pos = minetest.find_node_near(vector.add(self.start_pos, velocity), 1, "air")
+                       if drop_pos then 
+                               minetest.item_drop(stack, "", drop_pos)
+                               self:remove()
                        end
                end
                
                if velocity.x~=velocitycopy.x or velocity.y~=velocitycopy.y or velocity.z~=velocitycopy.z or 
                                self.start_pos.x~=sposcopy.x or self.start_pos.y~=sposcopy.y or self.start_pos.z~=sposcopy.z then
                        self.object:setpos(self.start_pos)
-                       self.object:setvelocity(velocity)
+                       dist_moved = 0
+                       self.velocity = velocity
+               else
+                       dist_moved = rectilinear_distance(vector.subtract(pos, self.start_pos))
                end
+               self.object:setvelocity(velocity, (1.001-dist_moved)/rectilinear_distance(velocity), {x=0,y=0,z=0})
        end
 })
 
index 97f0237ffe379d85ad077d016b4128814761bd8b..e3d4c57eb9399b948cf0ec7c26478381fedff971 100644 (file)
--- a/tubes.lua
+++ b/tubes.lua
@@ -515,7 +515,8 @@ if pipeworks.enable_sand_tube then
                                                       if object:get_luaentity().itemstring ~= "" then
                                                               local titem = pipeworks.tube_item(pos,object:get_luaentity().itemstring)
                                                               titem:get_luaentity().start_pos = {x = pos.x, y = pos.y-1, z = pos.z}
-                                                              titem:setvelocity({x = 0.01, y = 1, z = -0.01})
+                                                              titem:get_luaentity().velocity = {x=0,y=1,z=0}
+                                                              titem:setvelocity({x=0,y=1,z=0}, 1.001, {x=0,y=0,z=0})
                                                               titem:setacceleration({x = 0, y = 0, z = 0})
                                                       end
                                                       object:get_luaentity().itemstring = ""
@@ -578,7 +579,8 @@ if pipeworks.enable_mese_sand_tube then
                                                       if object:get_luaentity().itemstring ~= "" then
                                                               local titem = pipeworks.tube_item(pos, object:get_luaentity().itemstring)
                                                               titem:get_luaentity().start_pos = {x = pos.x, y = pos.y-1, z = pos.z}
-                                                              titem:setvelocity({x = 0.01, y = 1, z = -0.01})
+                                                              titem:get_luaentity().velocity = {x=0,y=1,z=0}
+                                                              titem:setvelocity({x=0,y=1,z=0}, 1.001, {x=0,y=0,z=0})
                                                               titem:setacceleration({x = 0, y = 0, z = 0})
                                                       end
                                                       object:get_luaentity().itemstring = ""
@@ -635,7 +637,9 @@ if pipeworks.enable_one_way_tube then
                        insert_object = function(pos, node, stack, direction)
                                item1 = pipeworks.tube_item(pos, stack)
                                item1:get_luaentity().start_pos = pos
-                               item1:setvelocity({x = direction.x*direction.speed, y = direction.y*direction.speed, z = direction.z*direction.speed})
+                               local vel = { x = direction.x*direction.speed, y = direction.y*direction.speed, z = direction.z*direction.speed }
+                               item1:get_luaentity().velocity = vel
+                               item1:setvelocity(vel, 1.001, {x=0,y=0,z=0})
                                item1:setacceleration({x = 0, y = 0, z = 0})
                                return ItemStack("")
                        end,
index 6d92a23e64857eb0b7ae74c01073a8a373859607..153ed2cb843cd7c34ddb2f1ca9db608585c6d139 100644 (file)
@@ -103,7 +103,8 @@ local function wielder_on(data, wielder_pos, wielder_node)
                        if not stack:is_empty() then
                                local tubeitem = pipeworks.tube_item(vector_copy(wielder_pos), stack)
                                tubeitem:get_luaentity().start_pos = vector_copy(wielder_pos)
-                               tubeitem:setvelocity(vector_copy(dir))
+                               tubeitem:get_luaentity().velocity = vector_copy(dir)
+                               tubeitem:setvelocity(vector_copy(dir), 1.001, vector.new(0,0,0))
                                tubeitem:setacceleration(vector.new(0,0,0))
                                inv:set_stack("main", i, ItemStack(""))
                        end