diff --git a/technic/helpers.lua b/technic/helpers.lua index f0c2fc6..5780f27 100644 --- a/technic/helpers.lua +++ b/technic/helpers.lua @@ -110,3 +110,82 @@ function technic.trace_node_ray(pos, dir, range) end, vector.round(pos) end + +--- Like trace_node_ray, but includes extra positions close to the ray. +function technic.trace_node_ray_fat(pos, dir, range) + local x_step = dir.x > 0 and 1 or -1 + local y_step = dir.y > 0 and 1 or -1 + local z_step = dir.z > 0 and 1 or -1 + + local next_poses = {} + + local i = 1 + return function(p) + local ni, np = next(next_poses) + if np then + next_poses[ni] = nil + return np + end + + -- Approximation of where we should be if we weren't rounding + -- to nodes. This moves forward a bit faster then we do. + -- A correction is done below. + local real_x = pos.x + (dir.x * i) + local real_y = pos.y + (dir.y * i) + local real_z = pos.z + (dir.z * i) + + -- How far off we've gotten from where we should be. + local dx = math.abs(real_x - p.x) + local dy = math.abs(real_y - p.y) + local dz = math.abs(real_z - p.z) + + -- If the real position moves ahead too fast, stop it so we + -- can catch up. If it gets too far ahead it will smooth + -- out our movement too much and we won't turn fast enough. + if dx + dy + dz < 2 then + i = i + 1 + end + + -- Step in whichever direction we're most off course in. + local sx, sy, sz -- Whether we've already stepped along each axis + if dx > dy then + if dx > dz then + sx = true + p.x = p.x + x_step + else + sz = true + p.z = p.z + z_step + end + elseif dy > dz then + sy = true + p.y = p.y + y_step + else + sz = true + p.z = p.z + z_step + end + + if vector.distance(pos, p) > range then + return nil + end + + -- Add other positions that we're significantly off on. + -- We can just use fixed integer keys here because the + -- table will be completely cleared before we reach this + -- code block again. + local dlen = math.sqrt(dx*dx + dy*dy + dz*dz) + -- Normalized axis deltas + local dxn, dyn, dzn = dx / dlen, dy / dlen, dz / dlen + if not sx and dxn > 0.5 then + next_poses[1] = vector.new(p.x + x_step, p.y, p.z) + end + if not sy and dyn > 0.5 then + next_poses[2] = vector.new(p.x, p.y + y_step, p.z) + end + if not sz and dzn > 0.5 then + next_poses[3] = vector.new(p.x, p.y, p.z + z_step) + end + + return p + end, vector.round(pos) +end + diff --git a/technic/tools/mining_lasers.lua b/technic/tools/mining_lasers.lua index 2020c4c..ef1eecb 100644 --- a/technic/tools/mining_lasers.lua +++ b/technic/tools/mining_lasers.lua @@ -71,7 +71,7 @@ local function laser_shoot(player, range, particle_texture, sound) texture = particle_texture .. "^[transform" .. math.random(0, 7), }) minetest.sound_play(sound, {pos = player_pos, max_hear_distance = range}) - for pos in technic.trace_node_ray(start_pos, dir, range) do + for pos in technic.trace_node_ray_fat(start_pos, dir, range) do if minetest.is_protected(pos, player_name) then minetest.record_protection_violation(pos, player_name) break