Pit caves (#20)

* A simple pit cave mod, based off of code from the volcano pipes in my magma_conduits mod

* option to seal off ocean pits

* reuse chasm's ignore list

* documentation

* rename findpits to prevent confusion with underworld glowing pits
This commit is contained in:
FaceDeer 2021-05-07 22:59:30 -06:00 committed by GitHub
parent 79fd356477
commit 289c353bb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 294 additions and 2 deletions

View File

@ -5,7 +5,7 @@ Sounds and textures are under various licenses, see the license.txt file in the
License for Code
----------------
Copyright (C) 2018 FaceDeer
Copyright (C) 2021 FaceDeer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -9,6 +9,10 @@ chasms.register_ignore = function(node_name)
ignore[minetest.get_content_id(node_name)] = true
end
chasms.ignore_content_id = function(content_id)
return ignore[content_id]
end
local maxy = tonumber(minetest.settings:get("chasms_maxy")) or -50
local miny = tonumber(minetest.settings:get("chasms_miny")) or -2500
local falloff = tonumber(minetest.settings:get("chasms_falloff")) or 100

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -168,6 +168,12 @@ Not all vast open spaces underground are the result of aeons of erosion by water
The great extent of chasms makes them hospitable to small flying creatures, and their narrowness makes the hospitable to creatures that feed on them - giant cave spider webs can be found strung across them here and there. A dubious salvation for anyone falling from above.
# Pit caves
![Looking up from the bottom of a pit](./df_caverns/screenshots/pit_cave.jpg)
A pit cave is a type of cave which contains one or more significant vertical shafts rather than being predominantly a conventional horizontal cave passage. Pit caves typically form in limestone as a result of long-term erosion by water. Given the vast scope of the flowstone formations found throughout the upper layers of the world's caverns, correspondingly gigantic pits can also be found. They sometimes breach the surface of the world, and at their greatest extent can have a drop over two kilometers deep. Bring plenty of rope when exploring such abysses.
# Sunless Sea
![Sunless river](./df_caverns/screenshots/sunless_river.jpg)

22
pit_caves/LICENSE.txt Normal file
View File

@ -0,0 +1,22 @@
License for Code
----------------
Copyright (C) 2021 FaceDeer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

18
pit_caves/README.md Normal file
View File

@ -0,0 +1,18 @@
A pit cave, shaft cave or vertical cave—or often simply called a pit (in the US) or pot (in the UK); jama in South Slavic languages scientific and colloquial vocabulary (borrowed since early research in the Western Balkan Dinaric Alpine karst)—is a type of cave which contains one or more significant vertical shafts rather than being predominantly a conventional horizontal cave passage. Pit caves typically form in limestone as a result of long-term erosion by water.
In the real world, the deepest known vertical drop in a cave is 603m in Vrtoglavica Cave in Slovenia. This mod adds pits of varying depth, with some under the default settings being up to 2500m deep. They are widely scattered and not all of them breach the surface, so they are a rare find, but with the right tools a pit cave can give access to a huge swath of underground terrain.
## Settings and commands
The following settings are available for configuring pit cave generation:
pit_caves_min_bottom (Lower limit of bottoms of pits) int -2500
pit_caves_max_bottom (Upper limit of bottoms of pits) int -500
pit_caves_min_top (Lower limit of tops of pits) int -100
pit_caves_max_top (Upper limit of tops of pits) int 100
pit_caves_mapblock_spacing (Average number of map blocks between pits) int 16
pit_caves_seal_ocean (Seal off pits that are under ocean water) bool true
The pit_caves_seal_ocean setting isn't perfect, some map generation scenarios can result in a gap through which water can flow. But it's better than having drain holes everywhere.
Users with the "server" privilege can use the ``/find_pit_caves`` command, which will list the locations of nearby pit caves.

221
pit_caves/init.lua Normal file
View File

@ -0,0 +1,221 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
local S = minetest.get_translator(minetest.get_current_modname())
local min_depth = tonumber(minetest.settings:get("pit_caves_min_bottom") or -2500)
local max_depth = tonumber(minetest.settings:get("pit_caves_max_bottom") or -500)
local min_top = tonumber(minetest.settings:get("pit_caves_min_top") or -100)
local max_top = tonumber(minetest.settings:get("pit_caves_max_top") or 100)
local seal_ocean = minetest.settings:get_bool("pit_caves_seal_ocean", true)
assert(min_depth < max_depth, "pit_caves_min_bottom is above pit_caves_max_bottom")
assert(min_top < max_top, "pit_caves_min_top is above pit_caves_max_top")
assert(max_depth < min_top, "pit_caves_max_bottom is above pit_caves_min_top")
local pit_radius = 3 -- approximate minimum radius of pit - noise adds a lot to this
local region_mapblocks = tonumber(minetest.settings:get("pit_caves_mapblock_spacing") or 16)
local mapgen_chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
local pit_region_size = region_mapblocks * mapgen_chunksize * 16
local c_air = minetest.get_content_id("air")
local c_gravel = c_air
local water_node
if minetest.get_modpath("default") then
c_gravel = minetest.get_content_id("default:gravel")
if seal_ocean then
water_node = "default:water_source"
end
end
local ignore
if minetest.get_modpath("chasms") then
-- the chasms mod already sets up a method to allow chasms to avoid overwriting stalactites and whatnot,
-- hijack that.
ignore = chasms.ignore_content_id
end
local water_level = tonumber(minetest.get_mapgen_setting("water_level"))
local mapgen_seed = tonumber(minetest.get_mapgen_setting("seed")) % 2^16
local scatter_2d = function(min_xz, gridscale, border_width)
local bordered_scale = gridscale - 2 * border_width
local point = {}
point.x = math.floor(math.random() * bordered_scale + min_xz.x + border_width)
point.y = 0
point.z = math.floor(math.random() * bordered_scale + min_xz.z + border_width)
return point
end
-- For some reason, map chunks generate with -32, -32, -32 as the "origin" minp. To make the large-scale grid align with map chunks it needs to be offset like this.
local get_corner = function(pos)
return {x = math.floor((pos.x+32) / pit_region_size) * pit_region_size - 32, z = math.floor((pos.z+32) / pit_region_size) * pit_region_size - 32}
end
local get_pit = function(pos)
local corner_xz = get_corner(pos)
local next_seed = math.random(1, 1000000000)
math.randomseed(corner_xz.x + corner_xz.z * 2 ^ 8 + mapgen_seed + 1)
local location = scatter_2d(corner_xz, pit_region_size, 0)
local depth = math.floor(math.random() * (max_depth - min_depth) + min_depth)
local top = math.floor(math.random() * (max_top - min_top) + min_top)
math.randomseed(next_seed)
return {location = location, depth = depth, top = top}
end
local perlin_params = {
offset = 0,
scale = 1,
spread = {x=30, y=30, z=30},
seed = 45011,
octaves = 3,
persist = 0.67
}
local data = {}
minetest.register_on_generated(function(minp, maxp, seed)
if minp.y > max_top or maxp.y < min_depth then
return
end
local pit = get_pit(minp)
if pit == nil then
return -- no pit in this map region
end
local location_x = pit.location.x
local location_z = pit.location.z
-- early out if the pit is too far away to matter
-- The plus 20 is because the noise being added will generally be in the 0-20 range, see the "distance" calculation below
if location_x - 20 > maxp.x or
location_x + 20 < minp.x or
location_z - 20 > maxp.z or
location_z + 20 < minp.z
then
return
end
local top = pit.top
local depth = pit.depth
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
vm:get_data(data)
if water_node and minp.y <= water_level and maxp.y >= water_level-240 then
local test_node = minetest.get_node(vector.new(location_x, water_level, location_z))
if test_node.name == water_node then
top = math.min(-32, top) -- we're coming up under the ocean, abort the pit.
-- note that this does depend on the water-level map block having been generated already,
-- which could lead to a sharp cutoff if that's not the case - if the player's coming
-- up a pit from below into an unexplored ocean, for example. But it should still at least
-- seal the hole before the ocean pours down into it, so that's acceptable. And I expect
-- most of the time the surface world will be explored first before pits are discovered.
end
end
local nvals_perlin = mapgen_helper.perlin3d("pit_caves:pit", emin, emax, perlin_params)
for vi, x, y, z in area:iterp_xyz(emin, emax) do
if not (ignore and ignore(data[vi])) then
local distance_perturbation = (nvals_perlin[vi]+1)*10
local distance = vector.distance({x=x, y=y, z=z}, {x=location_x, y=y, z=location_z}) - distance_perturbation
local taper_min = top - 40
if y < top and y > depth then
if y > top - 40 then
-- taper the top end
distance = distance - ((taper_min - y)/2)
end
if distance < pit_radius then
if y < depth + 20 and data[vi] ~= c_air then
data[vi] = c_gravel
else
data[vi] = c_air
end
end
end
end
end
--send data back to voxelmanip
vm:set_data(data)
--calc lighting
vm:set_lighting({day = 0, night = 0})
vm:calc_lighting()
vm:update_liquids()
--write it to world
vm:write_to_map()
end)
----------------------------------------------------------------------------------------------
-- Debugging and sightseeing commands
function round(val, decimal)
if (decimal) then
return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
else
return math.floor(val+0.5)
end
end
local send_pit_state = function(pos, name)
local pit = get_pit(pos)
if pit == nil then
return false
end
local location = {x=math.floor(pit.location.x), y=pit.top, z=math.floor(pit.location.z)}
minetest.chat_send_player(name, S("Pit at @1, bottom @2", minetest.pos_to_string(location), pit.depth))
return true
end
local send_nearby_states = function(pos, name)
local retval = false
retval = send_pit_state({x=pos.x-pit_region_size, y=0, z=pos.z+pit_region_size}, name) or retval
retval = send_pit_state({x=pos.x, y=0, z=pos.z+pit_region_size}, name) or retval
retval = send_pit_state({x=pos.x+pit_region_size, y=0, z=pos.z+pit_region_size}, name) or retval
retval = send_pit_state({x=pos.x-pit_region_size, y=0, z=pos.z}, name) or retval
retval = send_pit_state(pos, name) or retval
retval = send_pit_state({x=pos.x+pit_region_size, y=0, z=pos.z}, name) or retval
retval = send_pit_state({x=pos.x-pit_region_size, y=0, z=pos.z-pit_region_size}, name) or retval
retval = send_pit_state({x=pos.x, y=0, z=pos.z-pit_region_size}, name) or retval
retval = send_pit_state({x=pos.x+pit_region_size, y=0, z=pos.z-pit_region_size}, name) or retval
return retval
end
minetest.register_chatcommand("find_pit_caves", {
params = "pos", -- Short parameter description
description = S("find the pits near the player's map region, or in the map region containing pos if provided"),
func = function(name, param)
if minetest.check_player_privs(name, {server = true}) then
local pos = {}
pos.x, pos.y, pos.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
pos.x = tonumber(pos.x)
pos.y = tonumber(pos.y)
pos.z = tonumber(pos.z)
if pos.x and pos.y and pos.z then
if not send_nearby_states(pos, name) then
minetest.chat_send_player(name, S("No pits near @1", minetest.pos_to_string(pos)))
end
return true
else
local playerobj = minetest.get_player_by_name(name)
pos = playerobj:get_pos()
if not send_nearby_states(pos, name) then
pos.x = math.floor(pos.x)
pos.y = math.floor(pos.y)
pos.z = math.floor(pos.z)
minetest.chat_send_player(name, S("No pits near @1", minetest.pos_to_string(pos)))
end
return true
end
else
return false, S("You need the server privilege to use this command.")
end
end,
})

View File

@ -0,0 +1,11 @@
# textdomain: pit_caves
### init.lua ###
No pits near @1=
Pit at @1, bottom @2=
You need the server privilege to use this command.=
find the pits near the player's map region, or in the map region containing pos if provided=

4
pit_caves/mod.conf Normal file
View File

@ -0,0 +1,4 @@
name = pit_caves
description = Inserts very tall "pit" caves underground
depends =
optional_depends = mapgen_helper, default, chasms

View File

@ -0,0 +1,6 @@
pit_caves_min_bottom (Lower limit of bottoms of pits) int -2500
pit_caves_max_bottom (Upper limit of bottoms of pits) int -500
pit_caves_min_top (Lower limit of tops of pits) int -100
pit_caves_max_top (Upper limit of tops of pits) int 100
pit_caves_mapblock_spacing (Average number of map blocks between pits) int 16
pit_caves_seal_ocean (Seal off pits that are under ocean water) bool true

@ -1 +1 @@
Subproject commit 1f385fc84a72494dd4e09ef40717c5dcef3398ba
Subproject commit 331d08b1567cf023695832e4ab593d33720b9290