Replace mining laser ray tracer with a simpler iterable one
authorShadowNinja <shadowninja@minetest.net>
Thu, 25 Sep 2014 00:32:36 +0000 (20:32 -0400)
committerShadowNinja <shadowninja@minetest.net>
Thu, 25 Sep 2014 00:32:36 +0000 (20:32 -0400)
technic/tools/mining_lasers.lua
technic/tools/vector_line.lua [deleted file]

index 5c8d1e4dec818e0846fb72e94f6f2105db0f58f8..872271c49507f51392fe60f16399b781e82de53e 100644 (file)
@@ -32,64 +32,119 @@ minetest.register_craft({
        }
 })
 
-local function table_icontains(t, v)
-       for i = 1,#t do
-               if v == t[i] then
-                       return true
+-- Based on code by Uberi: https://gist.github.com/Uberi/3125280
+local function rayIter(pos, dir, range)
+       local p = vector.round(pos)
+       local x_step,      y_step,      z_step      = 0, 0, 0
+       local x_component, y_component, z_component = 0, 0, 0
+       local x_intersect, y_intersect, z_intersect = 0, 0, 0
+
+       if dir.x == 0 then
+               x_intersect = math.huge
+       elseif dir.x > 0 then
+               x_step = 1
+               x_component = 1 / dir.x
+               x_intersect = x_component
+       else
+               x_step = -1
+               x_component = 1 / -dir.x
+       end
+       if dir.y == 0 then
+               y_intersect = math.huge
+       elseif dir.y > 0 then
+               y_step = 1
+               y_component = 1 / dir.y
+               y_intersect = y_component
+       else
+               y_step = -1
+               y_component = 1 / -dir.y
+       end
+       if dir.z == 0 then
+               z_intersect = math.huge
+       elseif dir.z > 0 then
+               z_step = 1
+               z_component = 1 / dir.z
+               z_intersect = z_component
+       else
+               z_step = -1
+               z_component = 1 / -dir.z
+       end
+
+       return function()
+               if x_intersect < y_intersect then
+                       if x_intersect < z_intersect then
+                               p.x = p.x + x_step
+                               x_intersect = x_intersect + x_component
+                       else
+                               p.z = p.z + z_step
+                               z_intersect = z_intersect + z_component
+                       end
+               elseif y_intersect < z_intersect then
+                       p.y = p.y + y_step
+                       y_intersect = y_intersect + y_component
+               else
+                       p.z = p.z + z_step
+                       z_intersect = z_intersect + z_component
+               end
+               if vector.distance(pos, p) > range then
+                       return nil
                end
+               return p
        end
-       return false
 end
 
-local function laser_node(pos, player)
-       local node = minetest.get_node(pos)
-       if table_icontains({"air", "ignore", "default:lava_source", "default:lava_flowing"}, node.name) then
-               return
-       end
-       local pname = player:get_player_name()
-       if minetest.is_protected(pos, pname) then
-               minetest.record_protection_violation(pos, pname)
-               return
-       end
-       if table_icontains({"default:water_flowing", "default:water_source"}, node.name) then
+local function laser_node(pos, node, player)
+       local def = minetest.registered_nodes[node.name]
+       if def and def.liquidtype ~= "none" then
                minetest.remove_node(pos)
                minetest.add_particle({
                        pos = pos,
                        vel = {x=0, y=2, z=0},
                        acc = {x=0, y=-1, z=0},
                        expirationtime = 1.5,
-                       size = 6+math.random()*2,
-                       texture = "smoke_puff.png^[transform"..math.random(0,7),
+                       size = 6 + math.random() * 2,
+                       texture = "smoke_puff.png^[transform" .. math.random(0, 7),
                })
                return
        end
-       if player then
-               minetest.node_dig(pos, node, player)
-       end
-end
-
-if not vector.line then
-       dofile(technic.modpath.."/tools/vector_line.lua")
+       minetest.node_dig(pos, node, player)
 end
 
+local no_destroy = {
+       ["air"] = true,
+       ["default:lava_source"] = true,
+       ["default:lava_flowing"] = true,
+}
 local function laser_shoot(player, range, particle_texture, sound)
-       local playerpos = player:getpos()
+       local player_pos = player:getpos()
+       local player_name = player:get_player_name()
        local dir = player:get_look_dir()
 
-       local startpos = {x = playerpos.x, y = playerpos.y + 1.625, z = playerpos.z}
-       local mult_dir = vector.multiply(dir, 50)
+       local start_pos = vector.new(player_pos)
+       -- Adjust to head height
+       start_pos.y = start_pos.y + 1.9
        minetest.add_particle({
                pos = startpos,
                vel = dir,
-               acc = mult_dir,
+               acc = vector.multiply(dir, 50),
                expirationtime = range / 11,
                size = 1,
-               texture = particle_texture.."^[transform"..math.random(0,7),
+               texture = particle_texture .. "^[transform" .. math.random(0, 7),
        })
-       for _,pos in ipairs(vector.line(vector.round(startpos), dir, range)) do
-               laser_node(pos, player)
+       minetest.sound_play(sound, {pos = player_pos, max_hear_distance = range})
+       for pos in rayIter(start_pos, dir, range) do
+               if minetest.is_protected(pos, player_name) then
+                       minetest.record_protection_violation(pos, player_name)
+                       break
+               end
+               local node = minetest.get_node_or_nil(pos)
+               if not node then
+                       break
+               end
+               if not no_destroy[node.name] then
+                       laser_node(pos, node, player)
+               end
        end
-       minetest.sound_play(sound, {pos = playerpos, max_hear_distance = range})
 end
 
 
diff --git a/technic/tools/vector_line.lua b/technic/tools/vector_line.lua
deleted file mode 100644 (file)
index 950c93b..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-local twolines = {}
-function vector.twoline(x, y)
-       local pstr = x.." "..y
-       local line = twolines[pstr]
-       if line then
-               return line
-       end
-       line = {}
-       local n = 1
-       local dirx = 1
-       if x < 0 then
-               dirx = -dirx
-       end
-       local ymin, ymax = 0, y
-       if y < 0 then
-               ymin, ymax = ymax, ymin
-       end
-       local m = y/x --y/0 works too
-       local dir = 1
-       if m < 0 then
-               dir = -dir
-       end
-       for i = 0,x,dirx do
-               local p1 = math.max(math.min(math.floor((i-0.5)*m+0.5), ymax), ymin)
-               local p2 = math.max(math.min(math.floor((i+0.5)*m+0.5), ymax), ymin)
-               for j = p1,p2,dir do
-                       line[n] = {i, j}
-                       n = n+1
-               end
-       end
-       twolines[pstr] = line
-       return line
-end
-
-local threelines = {}
-function vector.threeline(x, y, z)
-       local pstr = x.." "..y.." "..z
-       local line = threelines[pstr]
-       if line then
-               return line
-       end
-       if x ~= math.floor(x) then
-               print("[technic] INFO: The position used for vector.threeline isn't round.")
-       end
-       local two_line = vector.twoline(x, y)
-       line = {}
-       local n = 1
-       local zmin, zmax = 0, z
-       if z < 0 then
-               zmin, zmax = zmax, zmin
-       end
-       local m = z/math.hypot(x, y)
-       local dir = 1
-       if m < 0 then
-               dir = -dir
-       end
-       for _,i in ipairs(two_line) do
-               local px, py = unpack(i)
-               local ph = math.hypot(px, py)
-               local z1 = math.max(math.min(math.floor((ph-0.5)*m+0.5), zmax), zmin)
-               local z2 = math.max(math.min(math.floor((ph+0.5)*m+0.5), zmax), zmin)
-               for pz = z1,z2,dir do
-                       line[n] = {px, py, pz}
-                       n = n+1
-               end
-       end
-       threelines[pstr] = line
-       return line
-end
-
-function vector.line(pos, dir, range)
-       if range then --dir = pos2
-               dir = vector.round(vector.multiply(dir, range))
-       else
-               dir = vector.subtract(dir, pos)
-       end
-       local line,n = {},1
-       for _,i in ipairs(vector.threeline(dir.x, dir.y, dir.z)) do
-               line[n] = {x=pos.x+i[1], y=pos.y+i[2], z=pos.z+i[3]}
-               n = n+1
-       end
-       return line
-end