depth_finder/init.lua

140 lines
4.8 KiB
Lua

-- Depth Finder [depth_finder]
-- by David G (kestral246@gmail.com)
-- 2020-03-15
-- Currently just two flints--bang them together and the return echo indicates depth.
local scan_angle = math.pi/4 -- corresponds to 90°
-- Maximum number of nodes to check
local maxcount = 150000
-- The wear and radius can now be set independently for each lamp tool.
-- Set to true to print debug statistics.
local debug = false
local scanned = {} -- Set containing scanned nodes, so they don't get scanned multiple times.
local tocheck = {} -- Table of nodes to check.
local max_depth = {} -- Deepest node scanned.
minetest.register_on_joinplayer(function(player)
local pname = player:get_player_name()
scanned[pname] = {}
tocheck[pname] = {}
max_depth[pname] = 0
end)
minetest.register_on_leaveplayer(function(player)
local pname = player:get_player_name()
scanned[pname] = nil
tocheck[pname] = nil
max_depth[pname] = nil
end)
-- Determine number of elements in table, for summary output.
local tlength = function(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
-- Scan neighboring nodes, flag for checking if air.
local scan_node = function(pname, pos, origin, vdir, maxdist)
-- Update to search a cone pattern in direction pointed.
-- Need small sphere to get cone out.
local origin2d = {x=origin.x, y=0, z=origin.z}
local pos2d = {x=pos.x, y=0, z=pos.z}
local radius = vector.distance(origin2d, pos2d)
if radius <= 2 or (radius <= maxdist and vector.angle(vdir, vector.direction(origin2d, pos2d)) < scan_angle) then
local enc_pos = minetest.hash_node_position(pos)
if scanned[pname][enc_pos] ~= true then -- hasn't been scanned
local name = minetest.get_node(pos).name
if name == "air" then -- checkable
table.insert(tocheck[pname], enc_pos) -- add to check list
end
scanned[pname][enc_pos] = true -- don't scan again
end
end
end
-- To check, scan all neighbors and determine if this node needs to be converted to light.
local check_node = function(pname, pos, origin, vdir, maxdist, height, depth)
local enc_pos = minetest.hash_node_position(pos)
local name = minetest.get_node(pos).name
scan_node(pname, vector.add(pos, {x=0,y=0,z=1}), origin, vdir, maxdist) -- north
scan_node(pname, vector.add(pos, {x=1,y=0,z=0}), origin, vdir, maxdist) -- east
scan_node(pname, vector.add(pos, {x=0,y=0,z=-1}), origin, vdir, maxdist) -- south
scan_node(pname, vector.add(pos, {x=-1,y=0,z=0}), origin, vdir, maxdist) -- west
if pos.y > origin.y - depth then
scan_node(pname, vector.add(pos, {x=0,y=-1,z=0}), origin, vdir, maxdist) -- down
end
if pos.y < origin.y + height then
scan_node(pname, vector.add(pos, {x=0,y=1,z=0}), origin, vdir, maxdist) -- up
end
if pos.y < max_depth[pname] then
max_depth[pname] = pos.y
end
end
local use_finder = function(player, itemstack, radius, height, depth, wear)
local pname = player:get_player_name()
local pos = vector.add(vector.round(player:get_pos()), {x=0,y=0,z=0}) -- position of wand
local theta = math.fmod(player:get_look_horizontal() + math.pi/2, 2*math.pi)
local vdir = vector.normalize({x=math.cos(theta), y=0, z=math.sin(theta)})
local key_stats = player:get_player_control()
local wear_cost = wear
-- Initialize temporary tables for safety.
scanned[pname] = {}
tocheck[pname] = {}
max_depth[pname] = pos.y
-- Search starts at finder position.
table.insert(tocheck[pname], minetest.hash_node_position(pos))
local count = 1
while count <= table.getn(tocheck[pname]) and count <= maxcount do
check_node(pname, minetest.get_position_from_hash(tocheck[pname][count]), pos, vdir, radius, height, depth)
count = count + 1
end
count = count - 1
if debug then -- print statistics
minetest.debug("depth_finder: y = "..tostring(pos.y)..", scan = "..
tostring(tlength(scanned[pname]))..", check = "..tostring(count)..", depth = "..
tostring(max_depth[pname]))
end
--minetest.chat_send_player(pname, "pdir = "..tostring(minetest.serialize(vdir)))
minetest.chat_send_player(pname, "depth = "..tostring(max_depth[pname] - pos.y))
-- Clear temporary tables, which could be large.
scanned[pname] = {}
tocheck[pname] = {}
max_depth[pname] = 0
-- Add wear to finder
itemstack:add_wear(wear_cost)
return itemstack
end
minetest.register_tool("depth_finder:simple", {
description = "Simple Depth Finder",
inventory_image = "depth_finder_simple.png",
stack_max = 1,
on_use = function(itemstack, player, pointed_thing)
local radius = 50
local height = 10
local depth = 80
local wear = math.floor(65535/25)
local worn_item = use_finder(player, itemstack, radius, height, depth, wear)
return worn_item
end,
})
-- Need default for crafting recipe.
-- Example craft recipe.
if minetest.get_modpath("default") ~= nil then
minetest.register_craft({
output = "depth_finder:simple",
recipe = {
{"default:flint", "default:flint"}
}
})
end