commit 930af27e222356b982ef4156a1b3eb1b56cbe8af Author: David G <kestral246@gmail.com> Date: Fri Sep 18 13:29:17 2020 -0700 Initial commit. diff --git a/README.md b/README.md new file mode 100644 index 0000000..25b60f1 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +Cave Tools [cavetools] +====================== +Some useful tools for exploring caves. + +By David G (kestral246@gmail.com) + +![Cave Tools](screenshot.png "Cave Tools") + +A compilation of some of my other mods focused on making cave exploration easier. + +A set of flash lamps that momentarily light up the direction the player is pointed. + +- Flash Lamp +- Flash Wand +- Super Flash Lamp + +A set of depth finders that probe depth in the direction the player is pointed. + +- Flint Depth Finder +- Diamond Depth Finder + +An override to the default ladders to allow them to be easily extended down by just right clicking a ladder with another ladder. + +- **Note:** This can create freestanding ladders. If the lack of ladder thickness bothers you, you can use Linuxdirks redef mod, which provides 3-d ladders. + + + +Dependencies +------------ + +- Ladder overrides only enabled if default exists. +- Craft recipes optionally depend on default and tnt. +- Wand also optionally depends on mana. + + +Craft Recipes +------------- + +![Flash Lamp](images/craft_lamp1.png "Flash Lamp (copper)") +![Flash Lamp](images/craft_lamp2.png "Flash Lamp (tin)") + +![Flash Wand](images/craft_wand.png "Flash Wand") +![Super Flash Lamp](images/craft_super.png "Super Flash Lamp") + +![Flint Depth Finder](images/craft_flint.png "Flint Depth Finder") +![Diamond Depth Finder](images/craft_diamond.png "Diamond Depth Finder") + + +Licenses +-------- + +Source code + +> The MIT License (MIT) + +Media (textures) + +> Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) + +- Flint texture copied from default\_flint.png by Gambit. +- Diamond texture copied from default\_diamond.png by BlockMen. +- Stick texture copied from default\_stick.png by BlockMen. diff --git a/depth_finder.lua b/depth_finder.lua new file mode 100644 index 0000000..341abe4 --- /dev/null +++ b/depth_finder.lua @@ -0,0 +1,158 @@ +-- Depth Finder [depth_finder] +-- by David G (kestral246@gmail.com) +-- 2020-06-22 + +-- Either two flints or two diamonds--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, "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("cavetools:depth_finder_flint", { + description = "Flint Depth Finder", + inventory_image = "cavetools_depth_finder_flint.png", + stack_max = 1, + on_use = function(itemstack, player, pointed_thing) + local radius = 30 + local height = 6 + local depth = 40 + local wear = math.floor(65535/25) + local worn_item = use_finder(player, itemstack, radius, height, depth, wear) + return worn_item + end, +}) + +minetest.register_tool("cavetools:depth_finder_diamond", { + description = "Diamond Depth Finder", + inventory_image = "cavetools_depth_finder_diamond.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/40) + 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 = "cavetools:depth_finder_flint", + recipe = { + {"default:flint", "default:flint"} + } + }) + minetest.register_craft({ + output = "cavetools:depth_finder_diamond", + recipe = { + {"default:diamond", "default:diamond"} + } + }) +end diff --git a/flash_lamps.lua b/flash_lamps.lua new file mode 100644 index 0000000..6f5c738 --- /dev/null +++ b/flash_lamps.lua @@ -0,0 +1,274 @@ +-- Flash Lamps (formerly Wand of Illumination) [flash_lamps] +-- by David G (kestral246@gmail.com) +-- 2020-06-22 + +-- Lights up what's in front, but only for a moment. +-- Provides a flash_lamp, flash_wand, and super_flash_lamp. + +-- How bright and wide to make lights. +-- For reference, the default:torch has a brightness of 12. +local brightness_value = 11 +local light_cone = math.pi/3 -- corresponds to 120° + +-- Maximum number of nodes to check (use debug to determine). +local maxcount = 100000 + +-- The wear, mana, and radius can now be set independently for each lamp tool. +-- For extended range, all of these values are doubled. + +-- Setting to allow optional use of ABM for light decay. +-- Make sure all lights have faded before disabling this option. +local use_abm = minetest.settings:get_bool("cavetools_use_abm", false) +if use_abm then + minetest.log("warning", "Flash Lamps using ABM for light decay.") +end + +-- Set to true to print debug statistics. +local debug = false + +-- Check for mana mod +local using_mana_mod = false +if minetest.get_modpath("mana") ~= nil then + using_mana_mod = true +end + +local scanned = {} -- Set containing scanned nodes, so they don't get scanned multiple times. +local tocheck = {} -- Table of nodes to check. +local tolight = {} -- Table of nodes that need to be converted to fill lights. + +minetest.register_on_joinplayer(function(player) + local pname = player:get_player_name() + scanned[pname] = {} + tocheck[pname] = {} + tolight[pname] = {} +end) + +minetest.register_on_leaveplayer(function(player) + local pname = player:get_player_name() + scanned[pname] = nil + tocheck[pname] = nil + tolight[pname] = nil +end) + +local mana_check = function(player, cost) + local allowed + if cost > 0 then + if using_mana_mod then + if mana.subtract(player:get_player_name(), cost) then + allowed = true + else + allowed = false + end + else + allowed = true + end + return allowed + else -- always allowed when not using mana + return true + end +end + +minetest.register_node("cavetools:light", { + description = "Flash Lamp Light", + drawtype = "airlike", + walkable = false, + paramtype = "light", + sunlight_propagates = true, + light_source = brightness_value, + pointable = false, + buildable_to = true, + drops = "", + groups = {cracky = 3, oddly_breakable_by_hand = 3, fill_hidden = 1, not_in_creative_inventory = 1}, + on_timer = function(pos) -- use node timer + minetest.remove_node(pos) + end, +}) + +if use_abm then -- optional ABM + minetest.register_abm({ + nodenames = {"cavetools:light"}, + interval = 2.0, -- need to tune + chance = 10, -- need to tune + action = function(pos, node, active_object_count, active_object_count_wider) + minetest.set_node(pos, {name = "air"}) + end + }) +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 send out a cone of light in direction pointed. + -- Need small sphere to get cone of light out. + local radius = vector.distance(origin, pos) + if radius <= 2 or (radius <= maxdist and vector.angle(vdir, vector.direction(origin, pos)) < light_cone) 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) + 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 + scan_node(pname, vector.add(pos, {x=0,y=-1,z=0}), origin, vdir, maxdist) -- down + scan_node(pname, vector.add(pos, {x=0,y=1,z=0}), origin, vdir, maxdist) -- up + if name == "air" and ((pos.x%4 == 0 and pos.y%8 == 0 and pos.z%4 == 0) or + (pos.x%4 == 2 and pos.y%8 == 4 and pos.z%4 == 2)) + and minetest.get_node_light(pos) < brightness_value then + table.insert(tolight[pname], enc_pos) + end +end + +local use_lamp = function(player, itemstack, radius, wear, mana) + local pname = player:get_player_name() + local pos = vector.add(vector.round(player:get_pos()), {x=0,y=1,z=0}) -- position of wand + local theta = math.fmod(player:get_look_horizontal() + math.pi/2, 2*math.pi) + local phi = player:get_look_vertical() + math.pi/2 + local vdir = vector.normalize({x=math.sin(phi)*math.cos(theta), y=math.cos(phi), z=math.sin(phi)*math.sin(theta)}) + -- For debug only. + --minetest.chat_send_player(pname, "theta = "..tostring(theta)..", phi = "..tostring(phi)..", vdir = "..tostring(minetest.serialize(vdir))) + local key_stats = player:get_player_control() + local wear_cost = wear + local mana_cost = mana + if key_stats.sneak or key_stats.aux1 then -- extended + radius = 2 * radius + wear_cost = 2 * wear_cost + mana_cost = 2 * mana_cost + end + if mana_check(player, mana_cost) then + -- Initialize temporary tables for safety. + scanned[pname] = {} + tocheck[pname] = {} + tolight[pname] = {} + -- Search starts at wand 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) + count = count + 1 + end + count = count - 1 + if debug then -- print statistics + minetest.debug("flash_lamps: y = "..tostring(pos.y)..", scan = ".. + tostring(tlength(scanned[pname]))..", check = "..tostring(count)..", lights = ".. + tostring(tlength(tolight[pname]))) + end + -- Add lights to all locations flagged for lighting. + for _,v in ipairs(tolight[pname]) do + local fpos = minetest.get_position_from_hash(v) + minetest.set_node(fpos, {name="cavetools:light"}) + if not use_abm then + local timer = minetest.get_node_timer(fpos) -- use node timer + timer:set(math.random(60), 0) + end + end + -- Clear temporary tables, which could be large. + scanned[pname] = {} + tocheck[pname] = {} + tolight[pname] = {} + -- Add wear to wand + itemstack:add_wear(wear_cost) + return itemstack + end +end + +minetest.register_tool("cavetools:flash_wand", { + description = "Flash Wand", + inventory_image = "cavetools_flash_wand.png", + stack_max = 1, + on_use = function(itemstack, player, pointed_thing) + local radius = 15 -- or 30 + local wear = math.floor(65535/25) + local mana = 100 + local worn_item = use_lamp(player, itemstack, radius, wear, mana) + return worn_item + end, +}) + +minetest.register_tool("cavetools:flash_lamp", { + description = "Flash Lamp", + inventory_image = "cavetools_flash_lamp.png", + stack_max = 1, + on_use = function(itemstack, player, pointed_thing) + local radius = 10 -- or 20 + local wear = math.floor(65535/15) + local mana = 0 + local worn_item = use_lamp(player, itemstack, radius, wear, mana) + return worn_item + end, +}) + +minetest.register_tool("cavetools:super_flash_lamp", { + description = "Super Flash Lamp", + inventory_image = "cavetools_super_flash_lamp.png", + stack_max = 1, + on_use = function(itemstack, player, pointed_thing) + local radius = 20 -- or 40 + local wear = math.floor(65535/40) + local mana = 0 + local worn_item = use_lamp(player, itemstack, radius, wear, mana) + return worn_item + end, +}) + +-- Need default for crafting recipe. +-- Example craft recipe for wand. +if minetest.get_modpath("default") ~= nil then + minetest.register_craft({ + output = "cavetools:flash_wand", + recipe = { + {"default:mese_crystal"}, + {"group:stick"} + } + }) +end + +-- Need default and tnt mods for crafting recipe. +-- Add copper or tin to make harder to craft. +if minetest.get_modpath("default") ~= nil and minetest.get_modpath("tnt") ~= nil then + minetest.register_craft({ + output = "cavetools:flash_lamp", + recipe = { + {"tnt:gunpowder", "tnt:gunpowder", "tnt:gunpowder"}, + {"", "default:tin_ingot", ""}, + {"", "group:stick", ""} + } + }) + minetest.register_craft({ + output = "cavetools:flash_lamp", + recipe = { + {"tnt:gunpowder", "tnt:gunpowder", "tnt:gunpowder"}, + {"", "default:copper_ingot", ""}, + {"", "group:stick", ""} + } + }) +end + +-- Need default for crafting recipe. +if minetest.get_modpath("default") ~= nil then + minetest.register_craft({ + output = "cavetools:super_flash_lamp", + recipe = { + {"default:meselamp", "default:obsidian", "default:meselamp"}, + {"default:obsidian", "default:meselamp", "default:obsidian"}, + {"default:meselamp", "default:obsidian", "default:meselamp"} + } + }) +end diff --git a/images/craft_diamond.png b/images/craft_diamond.png new file mode 100644 index 0000000..3741f8b Binary files /dev/null and b/images/craft_diamond.png differ diff --git a/images/craft_flint.png b/images/craft_flint.png new file mode 100644 index 0000000..39fa8d6 Binary files /dev/null and b/images/craft_flint.png differ diff --git a/images/craft_lamp1.png b/images/craft_lamp1.png new file mode 100644 index 0000000..3dc1330 Binary files /dev/null and b/images/craft_lamp1.png differ diff --git a/images/craft_lamp2.png b/images/craft_lamp2.png new file mode 100644 index 0000000..17223b3 Binary files /dev/null and b/images/craft_lamp2.png differ diff --git a/images/craft_super.png b/images/craft_super.png new file mode 100644 index 0000000..5da36c2 Binary files /dev/null and b/images/craft_super.png differ diff --git a/images/craft_wand.png b/images/craft_wand.png new file mode 100644 index 0000000..807f226 Binary files /dev/null and b/images/craft_wand.png differ diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..f09bbaf --- /dev/null +++ b/init.lua @@ -0,0 +1,9 @@ +-- Cave Tools [cavetools] + +dofile(minetest.get_modpath("cavetools").."/depth_finder.lua") +dofile(minetest.get_modpath("cavetools").."/flash_lamps.lua") + +if minetest.get_modpath("default") ~= nil then + dofile(minetest.get_modpath("cavetools").."/ladder_overrides.lua") +end + diff --git a/ladder_overrides.lua b/ladder_overrides.lua new file mode 100644 index 0000000..211fd4f --- /dev/null +++ b/ladder_overrides.lua @@ -0,0 +1,29 @@ +-- Cave Ladders [cave_ladders] +-- by David G (kestral246@gmail.com) +-- 2020-06-22 + +minetest.override_item("default:ladder_wood", { + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if clicker:get_wielded_item():get_name() == "default:ladder_wood" then + local below = {x = pos.x, y = pos.y - 1, z = pos.z} + if minetest.get_node(below).name == "air" then + minetest.set_node(below, {name = "default:ladder_wood", param2 = node.param2}) + itemstack:take_item(1) + end + return itemstack + end + end +}) + +minetest.override_item("default:ladder_steel", { + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if clicker:get_wielded_item():get_name() == "default:ladder_steel" then + local below = {x = pos.x, y = pos.y - 1, z = pos.z} + if minetest.get_node(below).name == "air" then + minetest.set_node(below, {name = "default:ladder_steel", param2 = node.param2}) + itemstack:take_item(1) + end + return itemstack + end + end +}) diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..3f57f63 --- /dev/null +++ b/mod.conf @@ -0,0 +1,3 @@ +name = cavetools +description = Some useful tools for exploring caves. +optional_depends = default, mana, tnt diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..0edf267 Binary files /dev/null and b/screenshot.png differ diff --git a/textures/cavetools_depth_finder_diamond.png b/textures/cavetools_depth_finder_diamond.png new file mode 100644 index 0000000..fd6d70e Binary files /dev/null and b/textures/cavetools_depth_finder_diamond.png differ diff --git a/textures/cavetools_depth_finder_flint.png b/textures/cavetools_depth_finder_flint.png new file mode 100644 index 0000000..3fd56c4 Binary files /dev/null and b/textures/cavetools_depth_finder_flint.png differ diff --git a/textures/cavetools_flash_lamp.png b/textures/cavetools_flash_lamp.png new file mode 100644 index 0000000..2ccf906 Binary files /dev/null and b/textures/cavetools_flash_lamp.png differ diff --git a/textures/cavetools_flash_wand.png b/textures/cavetools_flash_wand.png new file mode 100644 index 0000000..11948de Binary files /dev/null and b/textures/cavetools_flash_wand.png differ diff --git a/textures/cavetools_super_flash_lamp.png b/textures/cavetools_super_flash_lamp.png new file mode 100644 index 0000000..51a4f50 Binary files /dev/null and b/textures/cavetools_super_flash_lamp.png differ