Major rewrite of the API to support more complex decoration methods. Old API still available in legacy.lua
This major rewrite grew out of a desire to be able to have stalactites placed according to a global noise function, which meant the whole decoration approach had to be changed to allow for data to be efficiently carried over between nodes. In the process I cleaned up a lot of old inefficient code, some of it written back when I was still learning LUA in general. The legacy.lua file contains the old deprecated code so in theory any mods depending on subterrane should still function as they did before.
This commit is contained in:
parent
c590d55587
commit
d10e0a6e5a
144
README.md
144
README.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
This mod was based off of Caverealms by HeroOfTheWinds, which was in turn based off of Subterrain by Paramat.
|
This mod was based off of Caverealms by HeroOfTheWinds, which was in turn based off of Subterrain by Paramat.
|
||||||
|
|
||||||
It is intended as a utility mod for other mods to use when creating a more interesting underground experience in Minetest, primarily through the creation of enormous underground "natural" caverns with biome-based features. Installing this mod by itself will not do anything.
|
It is intended as a utility mod for other mods to use when creating a more interesting underground experience in Minetest, primarily through the creation of enormous underground "natural" caverns with hooks to add custom decorations. Installing this mod by itself will not do anything. This mod depends in turn on the [mapgen_helper](https://github.com/minetest-mods/mapgen_helper) mod.
|
||||||
|
|
||||||
The API has the following methods:
|
The API has the following methods:
|
||||||
|
|
||||||
@ -14,13 +14,22 @@ cave_layer_def is a table of the form:
|
|||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
minimum_depth = -- required, the highest elevation this cave layer will be generated in.
|
name = -- optional, defaults to the string "y_min to y_max" (with actual values inserted in place of y_min and y_max). Used for logging.
|
||||||
maximum_depth = -- required, the lowest elevation this cave layer will be generated in.
|
y_max = -- required, the highest elevation this cave layer will be generated in. Upper boundaries for cavern depths are most efficient when they fit the formula (x*80-32)-1 where x is an integer, that way you don't generate map blocks that straddle two cavern layers. The "-1" keeps them from intruding on the map block above.
|
||||||
cave_threshold = -- optional, Cave threshold. Defaults to 0.5. 1 = small rare caves, 0.5 = 1/3rd ground volume, 0 = 1/2 ground volume
|
y_min = -- required, the lowest elevation this cave layer will be generated in. Lower boundaries are most efficient when they fit (x*80-32).
|
||||||
|
cave_threshold = -- optional, Cave threshold. Defaults to 0.5. 1 = small rare caves, 0 = 1/2 ground volume
|
||||||
|
warren_region_threshold = -- optional, defaults to 0.25. Used to determine how much volume warrens take up around caverns. Set it to be equal to or greater than the cave threshold to disable warrens entirely.
|
||||||
|
warren_region_variability_threshold = -- optional, defaults to 0.25. Used to determine how much of the region contained within the warren_region_threshold actually has warrens in it.
|
||||||
|
warren_threshold = -- Optional, defaults to 0.25. Determines how "spongey" warrens are, lower numbers make tighter, less-connected warren passages.
|
||||||
boundary_blend_range = -- optional, range near ymin and ymax over which caves diminish to nothing. Defaults to 128.
|
boundary_blend_range = -- optional, range near ymin and ymax over which caves diminish to nothing. Defaults to 128.
|
||||||
perlin_cave = -- optional, a 3D perlin noise definition table to define the shape of the caves
|
perlin_cave = -- optional, a 3D perlin noise definition table to define the shape of the caves
|
||||||
perlin_wave = -- optional, a 3D perlin noise definition table that's averaged with the cave noise to add floor strata (squash its spread on the y axis relative to perlin_cave to accomplish this)
|
perlin_wave = -- optional, a 3D perlin noise definition table that's averaged with the cave noise to add more horizontal surfaces (squash its spread on the y axis relative to perlin_cave to accomplish this)
|
||||||
columns = -- optional, a column_def table for producing truly enormous stalactite and stalagmite formations that often fuse into columnar features
|
perlin_warren_area = -- optional, a 3D perlin noise definition table for defining what places warrens form in
|
||||||
|
perlin_warrens = -- optional, a 3D perlin noise definition table for defining the warrens
|
||||||
|
solidify_lava = -- when set to true, lava near the edges of caverns is converted into obsidian to prevent it from spilling in.
|
||||||
|
columns = -- optional, a column_def table for producing truly enormous dripstone formations. See below for definition. Set to nil to disable columns.
|
||||||
|
double_frequency = -- when set to true, uses the absolute value of the cavern field to determine where to place caverns instead. This effectively doubles the number of large non-connected caverns.
|
||||||
|
on_decorate = -- optional, a function that is given a table of indices and a variety of other mapgen information so that it can place custom decorations on floors and ceilings. It is given the parameters (minp, maxp, seed, vm, cavern_data, area, data). See below for the cavern_data table's member definitions.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -28,100 +37,65 @@ The column_def table is of the form:
|
|||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
max_column_radius = -- Maximum radius for individual columns, defaults to 10
|
maximum_radius = -- Maximum radius for individual columns, defaults to 10
|
||||||
min_column_radius = -- Minimum radius for individual columns, defaults to 2 (going lower can increase the likelihood of "intermittent" columns with floating sections)
|
minimum_radius = -- Minimum radius for individual columns, defaults to 4 (going lower that this can increase the likelihood of "intermittent" columns with floating sections)
|
||||||
node = -- node name to build columns out of. Defaults to default:stone
|
node = -- node name to build columns out of. Defaults to default:stone
|
||||||
weight = -- a floating point value (usually in the range of 0.5-1) to modify how strongly the column is affected by the surrounding cave. Lower values create a more variable, tapered stalactite/stalagmite combination whereas a value of 1 produces a roughly cylindrical column. Defaults to 0.5
|
warren_node = -- node name to build columns out of in warren areas. If not set, the nodes that would be columns in warrens will be left as original ground contents
|
||||||
maximum_count = -- The maximum number of columns placed in any given column region (each region being a square 4 times the length and width of a map chunk). Defaults to 100
|
weight = -- a floating point value (usually in the range of 0.5-1) to modify how strongly the column is affected by the surrounding cave. Lower values create a more variable, tapered stalactite/stalagmite combination whereas a value of 1 produces a roughly cylindrical column. Defaults to 0.25
|
||||||
minimum_count = -- The minimum number of columns placed in a column region. The actual number placed will be randomly selected between this range. Defaults to 25.
|
maximum_count = -- The maximum number of columns placed in any given column region (each region being a square 4 times the length and width of a map chunk). Defaults to 50
|
||||||
|
minimum_count = -- The minimum number of columns placed in a column region. The actual number placed will be randomly selected between this range. Defaults to 0.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
This causes large caverns to be hollowed out during map generation. By default these caverns are just featureless cavities, but you can add extra subterrane-specific properties to biomes and the mapgen code will use them to add features of your choice. Subterrane's biome properties are:
|
This causes large caverns to be hollowed out during map generation. By default these caverns are just featureless cavities, but you can add extra subterrane-specific properties to biomes and the mapgen code will use them to add features of your choice using the "on_decorate" callback. The callback is passed a data table with the following information:
|
||||||
|
|
||||||
- biome._subterrane_mitigate_lava -- If this is set to a non-false value, subterrane will try to turn all lava within about 10-20 nodes of the cavern into obsidian. This attempts to prevent lava from spilling into the cavern when the player visits, though it is by no means a perfect solution since it cannot account for non-subterrane-generated caves allowing lava to pour in.
|
|
||||||
- biome._subterrane_fill_node -- The nodeid that subterrane will fill the excavated cavern with. You could use this to create enormous underground oceans or lava pockets. If not provided, will default to "air"
|
|
||||||
- biome._subterrane_cave_fill_node -- If this is set to a nodeid, subterrane will use that to replace the air in existing default caves.
|
|
||||||
- biome._subterrane_ceiling_decor = function (area, data, ai, vi, bi, data_param2)
|
|
||||||
- biome._subterrane_floor_decor = function (area, data, ai, vi, bi, data_param2)
|
|
||||||
- biome._subterrane_override_sea_level = -- Optional, the Y coordinate where an underground sea level begins. Biomes' y coordinate cutoffs are unreliable underground, this forces subterrane to take this sea level cutoff into account.
|
|
||||||
- biome._subterrane_override_under_sea_biome = -- When below the override_sea_level, the biome with this name will be looked up and substituted.
|
|
||||||
- biome._subterrane_column_node = -- overrides the node type of a cavern layer's column_def, if there are columns here. This can be useful for example to substitute wet flowstone in humid biomes, or obsidian in volcanic biomes.
|
|
||||||
|
|
||||||
If defined, these functions will be executed once for each floor or ceiling node in the excavated cavern. "area" is the mapgen voxelarea, data and data_param2 are the voxelmanip's data arrays, "ai" is the index of the node "above" the current node, "vi" is the index of the current node, and "bi" is the index of the node "below" the current node.
|
|
||||||
|
|
||||||
The node pointed to by index vi will always start out filled with the cavern's fill node (air by default).
|
|
||||||
|
|
||||||
- biome._subterrane_cave_ceiling_decor = function(area, data, ai, vi, bi, data_param2)
|
|
||||||
- biome._subterrane_cave_floor_decor = function(area, data, ai, vi, bi, data_param2)
|
|
||||||
|
|
||||||
These are basically the same as the previous two methods, but these get executed for pre-existing tunnels instead of the caverns excavated by subterrane.
|
|
||||||
|
|
||||||
## subterrane:register_cave_decor(minimum_depth, maximum_depth)
|
|
||||||
|
|
||||||
Use this method when you want the following biome methods to be applied to pre-existing caves within a range of y values but don't want to excavate giant caverns there:
|
|
||||||
|
|
||||||
- biome._subterrane_cave_fill_node -- If this is set to a nodeid, subterrane will use that to replace the air in existing default caves.
|
|
||||||
- biome._subterrane_cave_ceiling_decor = function(area, data, ai, vi, bi, data_param2)
|
|
||||||
- biome._subterrane_cave_floor_decor = function(area, data, ai, vi, bi, data_param2)
|
|
||||||
|
|
||||||
It's essentially a trimmed-down version of register_cave_layer.
|
|
||||||
|
|
||||||
# Utilities
|
|
||||||
|
|
||||||
## subterrane:vertically_consistent_random(vi, area)
|
|
||||||
|
|
||||||
Takes a voxelmanip index and the corresponding area object, and returns a pseudorandom float from 0-1 based on the x and z coordinates of the index's location.
|
|
||||||
|
|
||||||
This is mainly intended for use when placing stalactites and stalagmites, since in a natural cavern these two features are almost always paired with each other spatially. If you use the following test in both the floor and ceiling decoration methods:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
if subterrane:vertically_consistent_random(vi, area) > 0.05 then
|
{
|
||||||
--stuff
|
cavern_floor_nodes = {} -- List of data indexes for nodes that are part of cavern floors. *Note:* Use ipairs() when iterating this, not pairs()
|
||||||
end
|
cavern_floor_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
cavern_ceiling_nodes = {} -- List of data indexes for nodes that are part of cavern ceilings. *Note:* Use ipairs() when iterating this, not pairs()
|
||||||
|
cavern_ceiling_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
warren_floor_nodes = {} -- List of data indexes for nodes that are part of warren floors. *Note:* Use ipairs() when iterating this, not pairs()
|
||||||
|
warren_floor_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
warren_ceiling_nodes = {} -- List of data indexes for nodes that are part of warren floors. *Note:* Use ipairs() when iterating this, not pairs()
|
||||||
|
warren_ceiling_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
tunnel_floor_nodes = {} -- List of data indexes for nodes that are part of floors in pre-existing tunnels (anything generated before this mapgen runs). *Note:* Use ipairs() when iterating this, not pairs()
|
||||||
|
tunnel_floor_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
tunnel_ceiling_nodes = {} -- List of data indexes for nodes that are part of ceiling in pre-existing tunnels (anything generated before this mapgen runs). *Note:* Use ipairs() when iterating this, not pairs()
|
||||||
|
tunnel_ceiling_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
column_nodes = {} -- Nodes that belong to columns. Note that if the warren_node was not set in the column definition these might not have been replaced by anything yet. This list contains *all* column nodes, not just ones on the outer surface of the column.
|
||||||
|
column_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
|
||||||
|
contains_cavern = false -- Use this if you want a quick check if the generated map chunk has any cavern volume in it. Don't rely on the node counts above, if the map chunk doesn't intersect the floor or ceiling those could be 0 even if a cavern is present.
|
||||||
|
contains_warren = false -- Ditto for contains_cavern, but for warrens instead of caverns.
|
||||||
|
nvals_cave = nvals_cave -- The noise array used to generate the cavern.
|
||||||
|
cave_area = cave_area -- a VoxelArea for indexing nvals_cave
|
||||||
|
cavern_def = cave_layer_def -- a reference to the cave layer def.
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
then you'll get a random distribution that's identical on the floor and ceiling.
|
|
||||||
|
|
||||||
## subterrane:override_biome(biome_def)
|
|
||||||
|
|
||||||
Unfortunately there's no easy way to override a single biome, so this method does it by clearing and re-registering all existing biomes.
|
|
||||||
Not only that, but the decorations also need to be wiped and re-registered - it appears they keep track of the biome they belong to via an internal ID that gets changed when the biomes are re-registered, resulting in them being left assigned to the wrong biomes.
|
|
||||||
|
|
||||||
This method is provided in subterrane because the default mod includes and "underground" biome that covers everything below -113 and would be annoying to work around. Any mod using subterrane in conjunction with the default mod should probably override the "underground" biome.
|
|
||||||
|
|
||||||
# Common cavern features
|
# Common cavern features
|
||||||
|
|
||||||
|
Subterrane has a number of functions bundled with it for generating some basic features commonly found in caverns, both realistic and fantastical.
|
||||||
|
|
||||||
## subterrane.register_stalagmite_nodes(base_name, base_node_def, drop_base_name)
|
* `subterrane.register_stalagmite_nodes(base_name, base_node_def, drop_base_name)`
|
||||||
|
* This registers a set of four standardized stalactite/stalagmite nodes that can be used with the subterrane.stalagmite function below. "base name" is a string that forms the prefix of the names of the nodes defined, for example the coolcaves mod might use "coolcaves:crystal_stal" and the resulting nodes registered would be "coolcaves:crystal_stal_1" through "coolcaves:crystal_stal_4". "base_node_def" is a node definition table much like is used with the usual node registration function. register_stalagmite_nodes will amend or substitute properties in this definition as needed, so the simplest base_node_def might just define the textures used. "drop_base_name" is an optional string that will substitute the node drops with stalagmites created by another use of register_stalagmite_nodes, for example if you wrote
|
||||||
|
* `subterrane.register_stalagmite_nodes("coolcaves:dry_stal", base_dry_node_def)`
|
||||||
|
* `subterrane.register_stalagmite_nodes("coolcaves:wet_stal", base_wet_node_def, "coolcaves:dry_stal")`
|
||||||
|
* then when the player mines a dry stalactite they'll get a dry stalactite node and if they mine a wet stalactite they'll get a corresponding dry stalactite node as the drop instead.
|
||||||
|
* This method returns a table consisting of the content IDs for the four stalactite nodes, which can be used directly in the following methods:
|
||||||
|
|
||||||
This registers a set of four standardized stalactite/stalagmite nodes that can be used with the subterrane:stalagmite function below. "base name" is a string that forms the prefix of the names of the nodes defined, for example the coolcaves mod might use "coolcaves:crystal_stal" and the resulting nodes registered would be "coolcaves:crystal_stal_1" through "coolcaves:crystal_stal_4". "base_node_def" is a node definition table much like is used with the usual node registration function. register_stalagmite_nodes will amend or substitute properties in this definition as needed, so the simplest base_node_def might just define the textures used. "drop_base_name" is an optional string that will substitute the node drops with stalagmites created by another use of register_stalagmite_nodes, for example if you wrote
|
* `subterrane.stalagmite(vi, area, data, param2_data, param2, height, stalagmite_id)`
|
||||||
|
* `subterrane.stalactite(vi, area, data, param2_data, param2, height, stalagmite_id)`
|
||||||
|
* These methods can be used to create a small stalactite or stalagmite, generally no more than 5 nodes tall. Use a negative height to generate a stalactite. The parameter stalagmite_id is a table of four content IDs for the stalagmite nodes, in order from thinnest ("_1") to thickest ("_4"). The register_stalagmite_nodes method returns a table that can be used for this directly.
|
||||||
|
|
||||||
```
|
* `subterrane.big_stalagmite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)`
|
||||||
subterrane.register_stalagmite_nodes("coolcaves:dry_stal", base_dry_node_def)
|
* `subterrane.big_stalactite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)`
|
||||||
subterrane.register_stalagmite_nodes("coolcaves:wet_stal", base_wet_node_def, "coolcaves:dry_stal")
|
* Generates a very large multi-node stalagmite or stalactite three nodes in diameter (with a five-node-diameter "root").
|
||||||
```
|
|
||||||
|
|
||||||
then when the player mines a dry stalactite they'll get a dry stalactite node and if they mine a wet stalactite they'll get a corresponding dry stalactite node as the drop instead.
|
* `subterrane.giant_mushroom(vi, area, data, stem_material, cap_material, gill_material, stem_height, cap_radius)`
|
||||||
|
* Generates an enormous mushroom. Cap radius works well in the range of around 2-6, larger or smaller than that may look odd.
|
||||||
This method returns a table consisting of the content IDs for the four stalactite nodes, which can be used directly in the following method:
|
|
||||||
|
|
||||||
## subterrane:stalagmite(vi, area, data, param2_data, param2, height, stalagmite_id)
|
|
||||||
|
|
||||||
This method can be used to create a small stalactite or stalagmite, generally no more than 5 nodes tall. Use a negative height to generate a stalactite. The parameter stalagmite_id is a table of four content IDs for the stalagmite nodes, in order from thinnest ("_1") to thickest ("_4"). The register_stalagmite_nodes method returns a table that can be used for this directly.
|
|
||||||
|
|
||||||
## subterrane:giant_stalagmite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
|
||||||
|
|
||||||
Generates a very large multi-node stalagmite three nodes in diameter (with a five-node-diameter "root").
|
|
||||||
|
|
||||||
## subterrane:giant_stalactite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
|
||||||
|
|
||||||
Similar to above, but generates a stalactite instead.
|
|
||||||
|
|
||||||
## subterrane:giant_shroom(vi, area, data, stem_material, cap_material, gill_material, stem_height, cap_radius)
|
|
||||||
|
|
||||||
Generates an enormous mushroom. Cap radius works well in the range of around 2-6, larger or smaller than that may look odd.
|
|
||||||
|
|
||||||
# Player spawn
|
# Player spawn
|
||||||
|
|
||||||
|
67
defaults.lua
Normal file
67
defaults.lua
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
subterrane.defaults = {
|
||||||
|
--y_max = Must be provided by the user
|
||||||
|
--y_min = Must be provided by the user
|
||||||
|
cave_threshold = 0.5,
|
||||||
|
warren_region_threshold = 0.25,
|
||||||
|
warren_region_variability_threshold = 0.25,
|
||||||
|
warren_threshold = 0.25,
|
||||||
|
boundary_blend_range = 128,
|
||||||
|
perlin_cave = {
|
||||||
|
offset = 0,
|
||||||
|
scale = 1,
|
||||||
|
spread = {x=256, y=256, z=256},
|
||||||
|
seed = -400000000089,
|
||||||
|
octaves = 3,
|
||||||
|
persist = 0.67
|
||||||
|
},
|
||||||
|
perlin_wave = {
|
||||||
|
offset = 0,
|
||||||
|
scale = 1,
|
||||||
|
spread = {x=512, y=256, z=512}, -- squashed 2:1
|
||||||
|
seed = 59033,
|
||||||
|
octaves = 6,
|
||||||
|
persist = 0.63
|
||||||
|
},
|
||||||
|
perlin_warren_area = {
|
||||||
|
offset = 0,
|
||||||
|
scale = 1,
|
||||||
|
spread = {x=1024, y=128, z=1024},
|
||||||
|
seed = -12554445,
|
||||||
|
octaves = 2,
|
||||||
|
persist = 0.67
|
||||||
|
},
|
||||||
|
perlin_warrens = {
|
||||||
|
offset = 0,
|
||||||
|
scale = 1,
|
||||||
|
spread = {x=32, y=12, z=32},
|
||||||
|
seed = 600089,
|
||||||
|
octaves = 3,
|
||||||
|
persist = 0.67
|
||||||
|
},
|
||||||
|
--solidify_lava =
|
||||||
|
columns = {
|
||||||
|
maximum_radius = 10,
|
||||||
|
minimum_radius = 4,
|
||||||
|
node = "default:stone",
|
||||||
|
weight = 0.25,
|
||||||
|
maximum_count = 50,
|
||||||
|
minimum_count = 0,
|
||||||
|
},
|
||||||
|
--double_frequency = -- when set to true, uses the absolute value of the cavern field to determine where to place caverns instead. This effectively doubles the number of large non-connected caverns.
|
||||||
|
--decorate = -- optional, a function that is given a table of indices and a variety of other mapgen information so that it can place custom decorations on floors and ceilings.
|
||||||
|
}
|
||||||
|
|
||||||
|
local recurse_defaults
|
||||||
|
recurse_defaults = function(target_table, default_table)
|
||||||
|
for k, v in pairs(default_table) do
|
||||||
|
if target_table[k] == nil then
|
||||||
|
target_table[k] = v -- TODO: deep copy if v is a table.
|
||||||
|
elseif type(target_table[k]) == "table" then
|
||||||
|
recurse_defaults(target_table[k], v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subterrane.set_defaults = function(cave_layer_def)
|
||||||
|
recurse_defaults(cave_layer_def, subterrane.defaults)
|
||||||
|
end
|
@ -1,2 +1,3 @@
|
|||||||
default
|
default
|
||||||
intllib?
|
intllib?
|
||||||
|
mapgen_helper
|
182
features.lua
182
features.lua
@ -1,14 +1,130 @@
|
|||||||
local c_air = minetest.get_content_id("air")
|
local c_air = minetest.get_content_id("air")
|
||||||
|
|
||||||
-- use a negative height to turn this into a stalactite
|
---------------------------------------------------------------------------
|
||||||
|
-- For registering a set of stalactite/stalagmite nodes to use with the small stalactite placement function below
|
||||||
|
|
||||||
|
local x_disp = 0.125
|
||||||
|
local z_disp = 0.125
|
||||||
|
|
||||||
|
local stal_on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
local pt = pointed_thing
|
||||||
|
-- check if pointing at a node
|
||||||
|
if not pt then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
if pt.type ~= "node" then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local under = minetest.get_node(pt.under)
|
||||||
|
local above = minetest.get_node(pt.above)
|
||||||
|
|
||||||
|
if minetest.is_protected(pt.above, placer:get_player_name()) then
|
||||||
|
minetest.record_protection_violation(pt.above, placer:get_player_name())
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- return if any of the nodes is not registered
|
||||||
|
if not minetest.registered_nodes[under.name] or not minetest.registered_nodes[above.name] then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
-- check if you can replace the node above the pointed node
|
||||||
|
if not minetest.registered_nodes[above.name].buildable_to then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_param2
|
||||||
|
-- check if pointing at an existing stalactite
|
||||||
|
if minetest.get_item_group(under.name, "subterrane_stal_align") ~= 0 then
|
||||||
|
new_param2 = under.param2
|
||||||
|
else
|
||||||
|
new_param2 = math.random(0,3)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add the node and remove 1 item from the itemstack
|
||||||
|
minetest.add_node(pt.above, {name = itemstack:get_name(), param2 = new_param2})
|
||||||
|
if not minetest.setting_getbool("creative_mode") then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local stal_box_1 = {{-0.0625+x_disp, -0.5, -0.0625+z_disp, 0.0625+x_disp, 0.5, 0.0625+z_disp}}
|
||||||
|
local stal_box_2 = {{-0.125+x_disp, -0.5, -0.125+z_disp, 0.125+x_disp, 0.5, 0.125+z_disp}}
|
||||||
|
local stal_box_3 = {{-0.25+x_disp, -0.5, -0.25+z_disp, 0.25+x_disp, 0.5, 0.25+z_disp}}
|
||||||
|
local stal_box_4 = {{-0.375+x_disp, -0.5, -0.375+z_disp, 0.375+x_disp, 0.5, 0.375+z_disp}}
|
||||||
|
|
||||||
|
-- Note that a circular table reference will result in a crash, TODO: guard against that.
|
||||||
|
-- Unlikely to be needed, though - it'd take a lot of work for users to get into this bit of trouble.
|
||||||
|
local function deep_copy(table_in)
|
||||||
|
local table_out = {}
|
||||||
|
for index, value in pairs(table_in) do
|
||||||
|
if type(value) == "table" then
|
||||||
|
table_out[index] = deep_copy(value)
|
||||||
|
else
|
||||||
|
table_out[index] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table_out
|
||||||
|
end
|
||||||
|
|
||||||
|
subterrane.register_stalagmite_nodes = function(base_name, base_node_def, drop_base_name)
|
||||||
|
base_node_def.groups = base_node_def.groups or {}
|
||||||
|
base_node_def.groups.subterrane_stal_align = 1
|
||||||
|
base_node_def.groups.flow_through = 1
|
||||||
|
base_node_def.drawtype = "nodebox"
|
||||||
|
base_node_def.paramtype = "light"
|
||||||
|
base_node_def.paramtype2 = "facedir"
|
||||||
|
base_node_def.is_ground_content = true
|
||||||
|
base_node_def.node_box = {type = "fixed"}
|
||||||
|
|
||||||
|
local def1 = deep_copy(base_node_def)
|
||||||
|
def1.groups.fall_damage_add_percent = 100
|
||||||
|
def1.node_box.fixed = stal_box_1
|
||||||
|
def1.on_place = stal_on_place
|
||||||
|
if drop_base_name then
|
||||||
|
def1.drop = drop_base_name.."_1"
|
||||||
|
end
|
||||||
|
minetest.register_node(base_name.."_1", def1)
|
||||||
|
|
||||||
|
local def2 = deep_copy(base_node_def)
|
||||||
|
def2.groups.fall_damage_add_percent = 50
|
||||||
|
def2.node_box.fixed = stal_box_2
|
||||||
|
def2.on_place = stal_on_place
|
||||||
|
if drop_base_name then
|
||||||
|
def2.drop = drop_base_name.."_2"
|
||||||
|
end
|
||||||
|
minetest.register_node(base_name.."_2", def2)
|
||||||
|
|
||||||
|
local def3 = deep_copy(base_node_def)
|
||||||
|
def3.node_box.fixed = stal_box_3
|
||||||
|
def3.on_place = stal_on_place
|
||||||
|
if drop_base_name then
|
||||||
|
def3.drop = drop_base_name.."_3"
|
||||||
|
end
|
||||||
|
minetest.register_node(base_name.."_3", def3)
|
||||||
|
|
||||||
|
local def4 = deep_copy(base_node_def)
|
||||||
|
def4.node_box.fixed = stal_box_4
|
||||||
|
def4.on_place = stal_on_place
|
||||||
|
if drop_base_name then
|
||||||
|
def4.drop = drop_base_name.."_4"
|
||||||
|
end
|
||||||
|
minetest.register_node(base_name.."_4", def4)
|
||||||
|
|
||||||
|
return {
|
||||||
|
minetest.get_content_id(base_name.."_1"),
|
||||||
|
minetest.get_content_id(base_name.."_2"),
|
||||||
|
minetest.get_content_id(base_name.."_3"),
|
||||||
|
minetest.get_content_id(base_name.."_4"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------
|
||||||
|
-- Use with stalactite nodes defined above
|
||||||
|
|
||||||
-- stalagmite_id is a table of the content ids of the four stalagmite sections, from _1 to _4.
|
-- stalagmite_id is a table of the content ids of the four stalagmite sections, from _1 to _4.
|
||||||
function subterrane:small_stalagmite(vi, area, data, param2_data, param2, height, stalagmite_id)
|
function subterrane.stalagmite(vi, area, data, param2_data, param2, height, stalagmite_id)
|
||||||
local pos = area:position(vi)
|
|
||||||
|
|
||||||
local x = pos.x
|
|
||||||
local y = pos.y
|
|
||||||
local z = pos.z
|
|
||||||
|
|
||||||
if height == nil then height = math.random(1,4) end
|
if height == nil then height = math.random(1,4) end
|
||||||
if param2 == nil then param2 = math.random(0,3) end
|
if param2 == nil then param2 = math.random(0,3) end
|
||||||
|
|
||||||
@ -21,18 +137,24 @@ function subterrane:small_stalagmite(vi, area, data, param2_data, param2, height
|
|||||||
id_modifier = 0
|
id_modifier = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
data[vi] = c_air -- force the first node to be viable. It's assumed some testing was done before calling this function.
|
|
||||||
for i = 1, math.abs(height) do
|
for i = 1, math.abs(height) do
|
||||||
vi = area:index(x, y + height - i * sign, z)
|
local svi = vi + (height - i * sign) * area.ystride
|
||||||
if data[vi] == c_air then
|
if data[svi] == c_air then -- test for air because we don't want these poking into water
|
||||||
data[vi] = stalagmite_id[math.min(i+id_modifier,4)]
|
data[svi] = stalagmite_id[math.min(i+id_modifier,4)]
|
||||||
param2_data[vi] = param2
|
param2_data[svi] = param2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function subterrane.stalactite(vi, area, data, param2_data, param2, height, stalagmite_id)
|
||||||
|
subterrane.stalagmite(vi, area, data, param2_data, param2, -height, stalagmite_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------
|
||||||
|
-- Builds very large stalactites and stalagmites
|
||||||
|
|
||||||
--giant stalagmite spawner
|
--giant stalagmite spawner
|
||||||
function subterrane:giant_stalagmite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
function subterrane.big_stalagmite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
||||||
local pos = area:position(vi)
|
local pos = area:position(vi)
|
||||||
local x = pos.x
|
local x = pos.x
|
||||||
local y = pos.y
|
local y = pos.y
|
||||||
@ -45,7 +167,7 @@ function subterrane:giant_stalagmite(vi, area, data, min_height, max_height, bas
|
|||||||
if j <= 0 then
|
if j <= 0 then
|
||||||
if k*k + l*l <= 9 then
|
if k*k + l*l <= 9 then
|
||||||
local vi = area:index(x+k, y+j, z+l)
|
local vi = area:index(x+k, y+j, z+l)
|
||||||
if data[vi] == c_air then data[vi] = base_material end
|
if mapgen_helper.buildable_to(data[vi]) then data[vi] = base_material end
|
||||||
end
|
end
|
||||||
elseif j <= top/5 then
|
elseif j <= top/5 then
|
||||||
if k*k + l*l <= 4 then
|
if k*k + l*l <= 4 then
|
||||||
@ -67,7 +189,7 @@ function subterrane:giant_stalagmite(vi, area, data, min_height, max_height, bas
|
|||||||
end
|
end
|
||||||
|
|
||||||
--giant stalactite spawner
|
--giant stalactite spawner
|
||||||
function subterrane:giant_stalactite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
function subterrane.big_stalactite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
||||||
local pos = area:position(vi)
|
local pos = area:position(vi)
|
||||||
local x = pos.x
|
local x = pos.x
|
||||||
local y = pos.y
|
local y = pos.y
|
||||||
@ -80,7 +202,7 @@ function subterrane:giant_stalactite(vi, area, data, min_height, max_height, bas
|
|||||||
if j >= -1 then
|
if j >= -1 then
|
||||||
if k*k + l*l <= 9 then
|
if k*k + l*l <= 9 then
|
||||||
local vi = area:index(x+k, y+j, z+l)
|
local vi = area:index(x+k, y+j, z+l)
|
||||||
if data[vi] == c_air then data[vi] = base_material end
|
if mapgen_helper.buildable_to(data[vi]) then data[vi] = base_material end
|
||||||
end
|
end
|
||||||
elseif j >= bot/5 then
|
elseif j >= bot/5 then
|
||||||
if k*k + l*l <= 4 then
|
if k*k + l*l <= 4 then
|
||||||
@ -101,14 +223,17 @@ function subterrane:giant_stalactite(vi, area, data, min_height, max_height, bas
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------
|
||||||
|
-- Giant mushrooms
|
||||||
|
|
||||||
--function to create giant 'shrooms. Cap radius works well from about 2-6
|
--function to create giant 'shrooms. Cap radius works well from about 2-6
|
||||||
--if ignore_bounds is true this function will place the mushroom even if it overlaps the edge of the voxel area.
|
--if ignore_bounds is true this function will place the mushroom even if it overlaps the edge of the voxel area.
|
||||||
function subterrane:giant_shroom(vi, area, data, stem_material, cap_material, gill_material, stem_height, cap_radius, ignore_bounds)
|
function subterrane.giant_mushroom(vi, area, data, stem_material, cap_material, gill_material, stem_height, cap_radius, ignore_bounds)
|
||||||
|
|
||||||
if not ignore_bounds and
|
if not ignore_bounds and
|
||||||
not (area:containsi(vi - cap_radius - area.zstride*cap_radius) and
|
not (area:containsi(vi - cap_radius - area.zstride*cap_radius) and
|
||||||
area:containsi(vi + cap_radius + stem_height*area.ystride + area.zstride*cap_radius)) then
|
area:containsi(vi + cap_radius + stem_height*area.ystride + area.zstride*cap_radius)) then
|
||||||
return -- mushroom overlaps the bounds of the voxel area, abort.
|
return false -- mushroom overlaps the bounds of the voxel area, abort.
|
||||||
end
|
end
|
||||||
|
|
||||||
local pos = area:position(vi)
|
local pos = area:position(vi)
|
||||||
@ -121,7 +246,7 @@ function subterrane:giant_shroom(vi, area, data, stem_material, cap_material, gi
|
|||||||
for l = -cap_radius, cap_radius do
|
for l = -cap_radius, cap_radius do
|
||||||
if k*k + l*l <= cap_radius*cap_radius then
|
if k*k + l*l <= cap_radius*cap_radius then
|
||||||
local vi = area:index(x+k, y+stem_height, z+l)
|
local vi = area:index(x+k, y+stem_height, z+l)
|
||||||
if data[vi] == c_air then data[vi] = cap_material end
|
if mapgen_helper.buildable_to(data[vi]) then data[vi] = cap_material end
|
||||||
end
|
end
|
||||||
if k*k + l*l <= (cap_radius-1)*(cap_radius-1) and (cap_radius-1) > 0 then
|
if k*k + l*l <= (cap_radius-1)*(cap_radius-1) and (cap_radius-1) > 0 then
|
||||||
local vi = area:index(x+k, y+stem_height+1, z+l)
|
local vi = area:index(x+k, y+stem_height+1, z+l)
|
||||||
@ -131,27 +256,30 @@ function subterrane:giant_shroom(vi, area, data, stem_material, cap_material, gi
|
|||||||
end
|
end
|
||||||
if k*k + l*l <= (cap_radius-2)*(cap_radius-2) and (cap_radius-2) > 0 then
|
if k*k + l*l <= (cap_radius-2)*(cap_radius-2) and (cap_radius-2) > 0 then
|
||||||
local vi = area:index(x+k, y+stem_height+2, z+l)
|
local vi = area:index(x+k, y+stem_height+2, z+l)
|
||||||
if data[vi] == c_air then data[vi] = cap_material end
|
if mapgen_helper.buildable_to(data[vi]) then data[vi] = cap_material end
|
||||||
end
|
end
|
||||||
if k*k + l*l <= (cap_radius-3)*(cap_radius-3) and (cap_radius-3) > 0 then
|
if k*k + l*l <= (cap_radius-3)*(cap_radius-3) and (cap_radius-3) > 0 then
|
||||||
local vi = area:index(x+k, y+stem_height+3, z+l)
|
local vi = area:index(x+k, y+stem_height+3, z+l)
|
||||||
if data[vi] == c_air then data[vi] = cap_material end
|
if mapgen_helper.buildable_to(data[vi]) then data[vi] = cap_material end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--stem
|
--stem
|
||||||
for j = 0, stem_height do
|
for j = -2, stem_height do -- going down to -2 to ensure the stem is flush with the ground
|
||||||
local vi = area:index(x, y+j, z)
|
local vi = area:index(x, y+j, z)
|
||||||
|
if j >= 0 or area:containsi(vi) then -- since -2 puts us below the bounds we've already tested, add a contains check here.
|
||||||
data[vi] = stem_material
|
data[vi] = stem_material
|
||||||
if cap_radius > 3 then
|
if cap_radius > 3 then
|
||||||
local ai = area:index(x, y+j, z+1)
|
local ai = area:index(x, y+j, z+1)
|
||||||
if data[ai] == c_air or data[ai] == gill_material then data[ai] = stem_material end
|
if mapgen_helper.buildable_to(data[ai]) or data[ai] == gill_material then data[ai] = stem_material end
|
||||||
ai = area:index(x, y+j, z-1)
|
ai = area:index(x, y+j, z-1)
|
||||||
if data[ai] == c_air or data[ai] == gill_material then data[ai] = stem_material end
|
if mapgen_helper.buildable_to(data[ai]) or data[ai] == gill_material then data[ai] = stem_material end
|
||||||
ai = area:index(x+1, y+j, z)
|
ai = area:index(x+1, y+j, z)
|
||||||
if data[ai] == c_air or data[ai] == gill_material then data[ai] = stem_material end
|
if mapgen_helper.buildable_to(data[ai]) or data[ai] == gill_material then data[ai] = stem_material end
|
||||||
ai = area:index(x-1, y+j, z)
|
ai = area:index(x-1, y+j, z)
|
||||||
if data[ai] == c_air or data[ai] == gill_material then data[ai] = stem_material end
|
if mapgen_helper.buildable_to(data[ai]) or data[ai] == gill_material then data[ai] = stem_material end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
124
functions.lua
124
functions.lua
@ -1,124 +0,0 @@
|
|||||||
--subterrane functions.lua
|
|
||||||
|
|
||||||
--FUNCTIONS--
|
|
||||||
|
|
||||||
function subterrane:vertically_consistent_random(vi, area)
|
|
||||||
local pos = area:position(vi)
|
|
||||||
local next_seed = math.random(1, 1000000000)
|
|
||||||
math.randomseed(pos.x + pos.z * 2 ^ 8)
|
|
||||||
local output = math.random()
|
|
||||||
math.randomseed(next_seed)
|
|
||||||
return output
|
|
||||||
end
|
|
||||||
|
|
||||||
local scatter_2d = function(min_xz, gridscale, min_output_size, max_output_size)
|
|
||||||
local next_seed = math.random(1, 1000000000)
|
|
||||||
math.randomseed(min_xz.x + min_xz.z * 2 ^ 8)
|
|
||||||
local count = math.random(min_output_size, max_output_size)
|
|
||||||
local result = {}
|
|
||||||
while count > 0 do
|
|
||||||
local point = {}
|
|
||||||
point.val = math.random()
|
|
||||||
point.x = math.random() * gridscale + min_xz.x
|
|
||||||
point.y = 0
|
|
||||||
point.z = math.random() * gridscale + min_xz.z
|
|
||||||
table.insert(result, point)
|
|
||||||
count = count - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
math.randomseed(next_seed)
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local get_nearest_grids = function(pos_xz, gridscale)
|
|
||||||
local half_scale = gridscale / 2
|
|
||||||
local grid_cell = {x = math.floor(pos_xz.x / gridscale) * gridscale, z = math.floor(pos_xz.z / gridscale) * gridscale}
|
|
||||||
local pos_internal = {x = pos_xz.x % gridscale, z = pos_xz.z % gridscale}
|
|
||||||
local result = {grid_cell}
|
|
||||||
local shift_x = gridscale
|
|
||||||
local shift_z = gridscale
|
|
||||||
if (pos_internal.x < half_scale) then
|
|
||||||
shift_x = -gridscale
|
|
||||||
end
|
|
||||||
if (pos_internal.z < half_scale) then
|
|
||||||
shift_z = -gridscale
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(result, {x = grid_cell.x + shift_x, z = grid_cell.z + shift_z})
|
|
||||||
table.insert(result, {x = grid_cell.x + shift_x, z = grid_cell.z})
|
|
||||||
table.insert(result, {x = grid_cell.x, z = grid_cell.z + shift_z})
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
subterrane.get_scatter_grid = function(pos_xz, gridscale, min_output_size, max_output_size)
|
|
||||||
local grids = get_nearest_grids(pos_xz, gridscale)
|
|
||||||
local points = {}
|
|
||||||
for _, grid in pairs(grids) do
|
|
||||||
for _, point in pairs(scatter_2d(grid, gridscale, min_output_size, max_output_size)) do
|
|
||||||
table.insert(points, point)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return points
|
|
||||||
end
|
|
||||||
|
|
||||||
subterrane.prune_points = function(minp, maxp, min_radius, max_radius, points)
|
|
||||||
local result = {}
|
|
||||||
for _, point in pairs(points) do
|
|
||||||
local radius = min_radius + point.val * (max_radius-min_radius)
|
|
||||||
point.val = radius
|
|
||||||
if point.x > minp.x - radius and point.x < maxp.x + radius and point.z > minp.z - radius and point.z < maxp.z + radius then
|
|
||||||
table.insert(result, point)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
subterrane.get_point_heat = function(pos, points)
|
|
||||||
local heat = 0
|
|
||||||
for _, point in pairs(points) do
|
|
||||||
point.y = pos.y
|
|
||||||
local dist = vector.distance(pos, point)
|
|
||||||
if dist < point.val then
|
|
||||||
heat = math.max(heat, 1 - dist/point.val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return heat
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Unfortunately there's no easy way to override a single biome, so do it by wiping everything and re-registering
|
|
||||||
-- Not only that, but the decorations also need to be wiped and re-registered - it appears they keep
|
|
||||||
-- track of the biome they belong to via an internal ID that gets changed when the biomes
|
|
||||||
-- are re-registered, resulting in them being left assigned to the wrong biomes.
|
|
||||||
function subterrane:override_biome(biome_def)
|
|
||||||
|
|
||||||
--Minetest 0.5 adds this "unregister biome" method
|
|
||||||
if minetest.unregister_biome and biome_def.name then
|
|
||||||
minetest.unregister_biome(biome_def.name)
|
|
||||||
minetest.register_biome(biome_def)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local registered_biomes_copy = {}
|
|
||||||
for old_biome_key, old_biome_def in pairs(minetest.registered_biomes) do
|
|
||||||
registered_biomes_copy[old_biome_key] = old_biome_def
|
|
||||||
end
|
|
||||||
local registered_decorations_copy = {}
|
|
||||||
for old_decoration_key, old_decoration_def in pairs(minetest.registered_decorations) do
|
|
||||||
registered_decorations_copy[old_decoration_key] = old_decoration_def
|
|
||||||
end
|
|
||||||
|
|
||||||
registered_biomes_copy[biome_def.name] = biome_def
|
|
||||||
|
|
||||||
minetest.clear_registered_decorations()
|
|
||||||
minetest.clear_registered_biomes()
|
|
||||||
for biome_key, new_biome_def in pairs(registered_biomes_copy) do
|
|
||||||
minetest.register_biome(new_biome_def)
|
|
||||||
end
|
|
||||||
for decoration_key, new_decoration_def in pairs(registered_decorations_copy) do
|
|
||||||
minetest.register_decoration(new_decoration_def)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
799
init.lua
799
init.lua
@ -1,22 +1,44 @@
|
|||||||
-- caverealms v.0.8 by HeroOfTheWinds
|
|
||||||
-- original cave code modified from paramat's subterrain
|
-- original cave code modified from paramat's subterrain
|
||||||
-- For Minetest 0.4.8 stable
|
-- Modified by HeroOfTheWinds for caverealms
|
||||||
|
-- Modified by FaceDeer for subterrane
|
||||||
-- Depends default
|
-- Depends default
|
||||||
-- License: code WTFPL
|
-- License: code MIT
|
||||||
|
|
||||||
|
local c_stone = minetest.get_content_id("default:stone")
|
||||||
|
local c_clay = minetest.get_content_id("default:clay")
|
||||||
|
local c_desert_stone = minetest.get_content_id("default:desert_stone")
|
||||||
|
local c_sandstone = minetest.get_content_id("default:sandstone")
|
||||||
|
|
||||||
|
local c_air = minetest.get_content_id("air")
|
||||||
|
local c_water = minetest.get_content_id("default:water_source")
|
||||||
|
|
||||||
|
local c_obsidian = minetest.get_content_id("default:obsidian")
|
||||||
|
|
||||||
|
local c_cavern_air = c_air
|
||||||
|
local c_warren_air = c_air
|
||||||
|
|
||||||
|
local subterrane_enable_singlenode_mapping_mode = minetest.setting_getbool("subterrane_enable_singlenode_mapping_mode")
|
||||||
|
if subterrane_enable_singlenode_mapping_mode then
|
||||||
|
c_cavern_air = c_stone
|
||||||
|
c_warren_air = c_clay
|
||||||
|
end
|
||||||
|
|
||||||
|
local c_lava_set -- will be populated with a set of nodes that count as lava
|
||||||
|
|
||||||
subterrane = {} --create a container for functions and constants
|
subterrane = {} --create a container for functions and constants
|
||||||
|
|
||||||
|
subterrane.registered_layers = {}
|
||||||
|
|
||||||
--grab a shorthand for the filepath of the mod
|
--grab a shorthand for the filepath of the mod
|
||||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
|
||||||
--load companion lua files
|
--load companion lua files
|
||||||
dofile(modpath.."/nodes.lua")
|
dofile(modpath.."/defaults.lua")
|
||||||
dofile(modpath.."/functions.lua") --function definitions
|
dofile(modpath.."/features.lua") -- some generic cave features useful for a variety of mapgens
|
||||||
dofile(modpath.."/features.lua")
|
dofile(modpath.."/player_spawn.lua") -- Function for spawning a player in a giant cavern
|
||||||
dofile(modpath.."/player_spawn.lua")
|
dofile(modpath.."/legacy.lua") -- contains old node definitions and functions, will be removed at some point in the future.
|
||||||
dofile(modpath.."/legacy.lua") -- contains old node definitions, will be removed at some point in the future.
|
|
||||||
|
|
||||||
subterrane.disable_mapgen_caverns = function()
|
local disable_mapgen_caverns = function()
|
||||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||||
local flags_name
|
local flags_name
|
||||||
local default_flags
|
local default_flags
|
||||||
@ -55,451 +77,486 @@ subterrane.disable_mapgen_caverns = function()
|
|||||||
end
|
end
|
||||||
minetest.set_mapgen_setting(flags_name, table.concat(new_flags, ","), true)
|
minetest.set_mapgen_setting(flags_name, table.concat(new_flags, ","), true)
|
||||||
end
|
end
|
||||||
|
disable_mapgen_caverns()
|
||||||
|
|
||||||
subterrane.disable_mapgen_caverns() -- defaulting to disabling them, for now. Need to assess how to integrate this feature into subterrane better.
|
-- Column stuff
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
local c_lava = minetest.get_content_id("default:lava_source")
|
local grid_size = mapgen_helper.block_size * 4
|
||||||
local c_obsidian = minetest.get_content_id("default:obsidian")
|
|
||||||
local c_stone = minetest.get_content_id("default:stone")
|
|
||||||
local c_air = minetest.get_content_id("air")
|
|
||||||
|
|
||||||
subterrane.default_perlin_cave = {
|
subterrane.get_column_points = function(minp, maxp, column_def)
|
||||||
offset = 0,
|
local grids = mapgen_helper.get_nearest_regions(minp, grid_size)
|
||||||
scale = 1,
|
local points = {}
|
||||||
spread = {x=256, y=256, z=256},
|
for _, grid in ipairs(grids) do
|
||||||
seed = -400000000089,
|
--The y value of the returned point will be the radius of the column
|
||||||
octaves = 3,
|
local minp = {x=grid.x, y = column_def.minimum_radius*100, z=grid.z}
|
||||||
persist = 0.67
|
local maxp = {x=grid.x+grid_size-1, y=column_def.maximum_radius*100, z=grid.z+grid_size-1}
|
||||||
}
|
for _, point in ipairs(mapgen_helper.get_random_points(minp, maxp, column_def.minimum_count, column_def.maximum_count)) do
|
||||||
|
point.y = point.y / 100
|
||||||
|
if point.x > minp.x - point.y
|
||||||
|
and point.x < maxp.x + point.y
|
||||||
|
and point.z > minp.z - point.y
|
||||||
|
and point.z < maxp.z + point.y then
|
||||||
|
table.insert(points, point)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return points
|
||||||
|
end
|
||||||
|
|
||||||
subterrane.default_perlin_wave = {
|
subterrane.get_column_value = function(pos, points)
|
||||||
offset = 0,
|
local heat = 0
|
||||||
scale = 1,
|
for _, point in ipairs(points) do
|
||||||
spread = {x=512, y=256, z=512}, -- squashed 2:1
|
local axis_point = {x=point.x, y=pos.y, z=point.z}
|
||||||
seed = 59033,
|
local radius = point.y
|
||||||
octaves = 6,
|
if (pos.x >= axis_point.x-radius and pos.x <= axis_point.x+radius
|
||||||
persist = 0.63
|
and pos.z >= axis_point.z-radius and pos.z <= axis_point.z+radius) then
|
||||||
}
|
|
||||||
|
|
||||||
local data = {}
|
local dist = vector.distance(pos, axis_point)
|
||||||
local data_param2 = {}
|
if dist < radius then
|
||||||
|
heat = math.max(heat, 1 - dist/radius)
|
||||||
|
end
|
||||||
|
|
||||||
local nvals_cave_buffer = {}
|
end
|
||||||
local nvals_wave_buffer = {}
|
end
|
||||||
|
return heat
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Decoration node lists
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- States any given node can be in. Used to detect boundaries
|
||||||
|
local outside_region = 1
|
||||||
|
local inside_ground = 2
|
||||||
|
local inside_tunnel = 3
|
||||||
|
local inside_cavern = 4
|
||||||
|
local inside_warren = 5
|
||||||
|
local inside_column = 6
|
||||||
|
|
||||||
|
-- These arrays will contain the indices of various nodes relevant to decoration
|
||||||
|
-- Note that table.getn and # will not correctly report the number of items in these since they're reused
|
||||||
|
-- between calls and are not cleared for efficiency. You can iterate through them using ipairs,
|
||||||
|
-- and you can get their content count from the similarly-named variable associated with them.
|
||||||
|
local cavern_data = {}
|
||||||
|
local cavern_floor_nodes = {}
|
||||||
|
cavern_data.cavern_floor_nodes = cavern_floor_nodes
|
||||||
|
cavern_data.cavern_floor_count = 0
|
||||||
|
local cavern_ceiling_nodes = {}
|
||||||
|
cavern_data.cavern_ceiling_nodes = cavern_ceiling_nodes
|
||||||
|
cavern_data.cavern_ceiling_count = 0
|
||||||
|
local warren_floor_nodes = {}
|
||||||
|
cavern_data.warren_floor_nodes = warren_floor_nodes
|
||||||
|
cavern_data.warren_floor_count = 0
|
||||||
|
local warren_ceiling_nodes = {}
|
||||||
|
cavern_data.warren_ceiling_nodes = warren_ceiling_nodes
|
||||||
|
cavern_data.warren_ceiling_count = 0
|
||||||
|
local tunnel_floor_nodes = {}
|
||||||
|
cavern_data.tunnel_floor_nodes = tunnel_floor_nodes
|
||||||
|
cavern_data.tunnel_floor_count = 0
|
||||||
|
local tunnel_ceiling_nodes = {}
|
||||||
|
cavern_data.tunnel_ceiling_nodes = tunnel_ceiling_nodes
|
||||||
|
cavern_data.tunnel_ceiling_count = 0
|
||||||
|
local column_nodes = {}
|
||||||
|
cavern_data.column_nodes = column_nodes
|
||||||
|
cavern_data.column_count = 0
|
||||||
|
|
||||||
|
-- inserts nil after the last node so that ipairs will function correctly
|
||||||
|
local close_node_arrays = function()
|
||||||
|
cavern_ceiling_nodes[cavern_data.cavern_ceiling_count + 1] = nil
|
||||||
|
cavern_floor_nodes[cavern_data.cavern_floor_count + 1] = nil
|
||||||
|
warren_ceiling_nodes[cavern_data.warren_ceiling_count + 1] = nil
|
||||||
|
warren_floor_nodes[cavern_data.warren_floor_count + 1] = nil
|
||||||
|
tunnel_ceiling_nodes[cavern_data.tunnel_ceiling_count + 1] = nil
|
||||||
|
tunnel_floor_nodes[cavern_data.tunnel_floor_count + 1] = nil
|
||||||
|
column_nodes[cavern_data.column_count + 1] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- clear the tables without deleting them - easer on memory management this way
|
||||||
|
local clear_node_arrays = function()
|
||||||
|
cavern_data.cavern_ceiling_count = 0
|
||||||
|
cavern_data.cavern_floor_count = 0
|
||||||
|
cavern_data.warren_ceiling_count = 0
|
||||||
|
cavern_data.warren_floor_count = 0
|
||||||
|
cavern_data.tunnel_ceiling_count = 0
|
||||||
|
cavern_data.tunnel_floor_count = 0
|
||||||
|
cavern_data.column_count = 0
|
||||||
|
close_node_arrays()
|
||||||
|
end
|
||||||
|
|
||||||
-- cave_layer_def
|
-- cave_layer_def
|
||||||
--{
|
--{
|
||||||
-- minimum_depth = -- required, the highest elevation this cave layer will be generated in.
|
-- name = -- optional, defaults to the string "y_min to y_max" (with actual values inserted in place of y_min and y_max). Used for logging.
|
||||||
-- maximum_depth = -- required, the lowest elevation this cave layer will be generated in.
|
-- y_max = -- required, the highest elevation this cave layer will be generated in.
|
||||||
-- cave_threshold = -- optional, Cave threshold. Defaults to 0.5. 1 = small rare caves, 0.5 = 1/3rd ground volume, 0 = 1/2 ground volume
|
-- y_min = -- required, the lowest elevation this cave layer will be generated in.
|
||||||
|
-- cave_threshold = -- optional, Cave threshold. Defaults to 0.5. 1 = small rare caves, 0 = 1/2 ground volume
|
||||||
|
-- warren_region_threshold = -- optional, defaults to 0.25. Used to determine how much volume warrens take up around caverns. Set it to be equal to or greater than the cave threshold to disable warrens entirely.
|
||||||
|
-- warren_region_variability_threshold = -- optional, defaults to 0.25. Used to determine how much of the region contained within the warren_region_threshold actually has warrens in it.
|
||||||
|
-- warren_threshold = -- Optional, defaults to 0.25. Determines how "spongey" warrens are, lower numbers make tighter, less-connected warren passages.
|
||||||
-- boundary_blend_range = -- optional, range near ymin and ymax over which caves diminish to nothing. Defaults to 128.
|
-- boundary_blend_range = -- optional, range near ymin and ymax over which caves diminish to nothing. Defaults to 128.
|
||||||
-- perlin_cave = -- optional, a 3D perlin noise definition table to define the shape of the caves
|
-- perlin_cave = -- optional, a 3D perlin noise definition table to define the shape of the caves
|
||||||
-- perlin_wave = -- optional, a 3D perlin noise definition table that's averaged with the cave noise to add floor strata (squash its spread on the y axis relative to perlin_cave to accomplish this)
|
-- perlin_wave = -- optional, a 3D perlin noise definition table that's averaged with the cave noise to add more horizontal surfaces (squash its spread on the y axis relative to perlin_cave to accomplish this)
|
||||||
-- columns = -- optional, a column_def table for producing truly enormous dripstone formations
|
-- perlin_warren_area = -- optional, a 3D perlin noise definition table for defining what places warrens form in
|
||||||
|
-- perlin_warrens = -- optional, a 3D perlin noise definition table for defining the warrens
|
||||||
|
-- solidify_lava = -- when set to true, lava near the edges of caverns is converted into obsidian to prevent it from spilling in.
|
||||||
|
-- columns = -- optional, a column_def table for producing truly enormous dripstone formations. See below for definition. Set to nil to disable columns.
|
||||||
|
-- double_frequency = -- when set to true, uses the absolute value of the cavern field to determine where to place caverns instead. This effectively doubles the number of large non-connected caverns.
|
||||||
|
-- decorate = -- optional, a function that is given a table of indices and a variety of other mapgen information so that it can place custom decorations on floors and ceilings. It is given the parameters (minp, maxp, seed, vm, cavern_data, area, data). See below for the cavern_data table's member definitions.
|
||||||
--}
|
--}
|
||||||
|
|
||||||
-- column_def
|
-- column_def
|
||||||
--{
|
--{
|
||||||
-- max_column_radius = -- Maximum radius for individual columns, defaults to 10
|
-- maximum_radius = -- Maximum radius for individual columns, defaults to 10
|
||||||
-- min_column_radius = -- Minimum radius for individual columns, defaults to 2 (going lower can increase the likelihood of "intermittent" columns with floating sections)
|
-- minimum_radius = -- Minimum radius for individual columns, defaults to 4 (going lower that this can increase the likelihood of "intermittent" columns with floating sections)
|
||||||
-- node = -- node name to build columns out of. Defaults to default:stone
|
-- node = -- node name to build columns out of. Defaults to default:stone
|
||||||
-- weight = -- a floating point value (usually in the range of 0.5-1) to modify how strongly the column is affected by the surrounding cave. Lower values create a more variable, tapered stalactite/stalagmite combination whereas a value of 1 produces a roughly cylindrical column. Defaults to 0.5
|
-- warren_node = -- node name to build columns out of in warren areas. If not set, the nodes that would be columns in warrens will be left as original ground contents
|
||||||
-- maximum_count = -- The maximum number of columns placed in any given column region (each region being a square 4 times the length and width of a map chunk). Defaults to 100
|
-- weight = -- a floating point value (usually in the range of 0.5-1) to modify how strongly the column is affected by the surrounding cave. Lower values create a more variable, tapered stalactite/stalagmite combination whereas a value of 1 produces a roughly cylindrical column. Defaults to 0.25
|
||||||
-- minimum_count = -- The minimum number of columns placed in a column region. The actual number placed will be randomly selected between this range. Defaults to 25.
|
-- maximum_count = -- The maximum number of columns placed in any given column region (each region being a square 4 times the length and width of a map chunk). Defaults to 50
|
||||||
|
-- minimum_count = -- The minimum number of columns placed in a column region. The actual number placed will be randomly selected between this range. Defaults to 0.
|
||||||
--}
|
--}
|
||||||
|
|
||||||
--extra biome properties used by subterrane
|
-- cavern_data -- This is passed into the decorate method.
|
||||||
--{
|
--{
|
||||||
-- _subterrane_ceiling_decor = -- function for putting stuff on the ceiling of the big caverns
|
-- cavern_floor_nodes = {} -- List of data indexes for nodes that are part of cavern floors. Note: Use ipairs() when iterating this, not pairs()
|
||||||
-- _subterrane_floor_decor = -- function for putting stuff on the floor of the big caverns
|
-- cavern_floor_count = 0 -- the count of nodes in the preceeding list.
|
||||||
-- _subterrane_fill_node = -- node to fill the cavern with (defaults to air)
|
-- cavern_ceiling_nodes = {} -- List of data indexes for nodes that are part of cavern ceilings. Note: Use ipairs() when iterating this, not pairs()
|
||||||
-- _subterrane_column_node = -- override the node the giant columns in this biome are made from
|
-- cavern_ceiling_count = 0 -- the count of nodes in the preceeding list.
|
||||||
-- _subterrane_cave_floor_decor = -- function for putting stuff on the floors of other preexisting open space
|
-- warren_floor_nodes = {} -- List of data indexes for nodes that are part of warren floors. Note: Use ipairs() when iterating this, not pairs()
|
||||||
-- _subterrane_cave_ceiling_decor = -- function for putting stuff on the ceiling of other preexisting open space
|
-- warren_floor_count = 0 -- the count of nodes in the preceeding list.
|
||||||
-- _subterrane_mitigate_lava = -- try to patch the walls of big caverns with obsidian plugs when lava intersects. Not perfect, but helpful.
|
-- warren_ceiling_nodes = {} -- List of data indexes for nodes that are part of warren floors. Note: Use ipairs() when iterating this, not pairs()
|
||||||
-- _subterrane_override_sea_level = -- Y coordinate where an underground sea level begins. Biomes' y coordinate cutoffs are unreliable underground, this forces subterrane to take this sea level cutoff into account.
|
-- warren_ceiling_count = 0 -- the count of nodes in the preceeding list.
|
||||||
-- _subterrane_override_under_sea_biome = -- When below the override_sea_level, the biome with this name will be looked up and substituted.
|
-- tunnel_floor_nodes = {} -- List of data indexes for nodes that are part of floors in pre-existing tunnels (anything generated before this mapgen runs). Note: Use ipairs() when iterating this, not pairs()
|
||||||
-- _subterrane_column_node = -- overrides the node type of a cavern layer's column_def, if there are columns here.
|
-- tunnel_floor_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
-- tunnel_ceiling_nodes = {} -- List of data indexes for nodes that are part of ceiling in pre-existing tunnels (anything generated before this mapgen runs). Note: Use ipairs() when iterating this, not pairs()
|
||||||
|
-- tunnel_ceiling_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
-- column_nodes = {} -- Nodes that belong to columns. Note that if the warren_node was not set in the column definition these might not have been replaced by anything yet. This list contains *all* column nodes, not just ones on the surface.
|
||||||
|
-- column_count = 0 -- the count of nodes in the preceeding list.
|
||||||
|
|
||||||
|
-- contains_cavern = false -- Use this if you want a quick check if the generated map chunk has any cavern volume in it. Don't rely on the node counts above, if the map chunk doesn't intersect the floor or ceiling those could be 0 even if a cavern is present.
|
||||||
|
-- contains_warren = false -- Ditto for contains_cavern
|
||||||
|
-- nvals_cave = nvals_cave -- The noise array used to generate the cavern.
|
||||||
|
-- cave_area = cave_area -- a VoxelArea for indexing nvals_cave
|
||||||
|
-- cavern_def = cave_layer_def -- a reference to the cave layer def.
|
||||||
--}
|
--}
|
||||||
|
|
||||||
local default_column = {
|
subterrane.register_layer = function(cave_layer_def)
|
||||||
max_column_radius = 10,
|
local error_out = false
|
||||||
min_column_radius = 2,
|
if cave_layer_def.y_min == nil then
|
||||||
node = c_stone,
|
minetest.log("error", "[subterrane] cave layer def " .. tostring(cave_layer_def.name) .. " did not have a y_min defined. Not registered.")
|
||||||
weight = 0.25,
|
error_out = true
|
||||||
maximum_count = 100,
|
end
|
||||||
minimum_count = 25,
|
if cave_layer_def.y_max == nil then
|
||||||
}
|
minetest.log("error", "[subterrane] cave layer def " .. tostring(cave_layer_def.name) .. " did not have a y_max defined. Not registered.")
|
||||||
|
error_out = true
|
||||||
|
end
|
||||||
|
if error_out then return end
|
||||||
|
|
||||||
function subterrane:register_cave_layer(cave_layer_def)
|
subterrane.set_defaults(cave_layer_def)
|
||||||
|
|
||||||
local YMIN = cave_layer_def.maximum_depth
|
local YMIN = cave_layer_def.y_min
|
||||||
local YMAX = cave_layer_def.minimum_depth
|
local YMAX = cave_layer_def.y_max
|
||||||
local BLEND = math.min(cave_layer_def.boundary_blend_range or 128, (YMAX-YMIN)/2)
|
|
||||||
local TCAVE = cave_layer_def.cave_threshold or 0.5
|
|
||||||
|
|
||||||
local np_cave = cave_layer_def.perlin_cave or subterrane.default_perlin_cave
|
if cave_layer_def.name == nil then
|
||||||
local np_wave = cave_layer_def.perlin_wave or subterrane.default_perlin_wave
|
cave_layer_def.name = tostring(YMIN) .. " to " .. tostring(YMAX)
|
||||||
|
end
|
||||||
|
|
||||||
local yblmin = YMIN + BLEND * 1.5
|
table.insert(subterrane.registered_layers, cave_layer_def)
|
||||||
local yblmax = YMAX - BLEND * 1.5
|
|
||||||
|
|
||||||
-- noise objects
|
local block_size = mapgen_helper.block_size
|
||||||
local nobj_cave = nil
|
|
||||||
local nobj_wave = nil
|
if (YMAX+32+1)%block_size ~= 0 then
|
||||||
|
local boundary = YMAX -(YMAX+32+1)%block_size
|
||||||
|
minetest.log("warning", "[subterrane] The y_max setting "..tostring(YMAX)..
|
||||||
|
" for cavern layer " .. cave_layer_def.name .. " is not aligned with map chunk boundaries. Consider "..
|
||||||
|
tostring(boundary) .. " or " .. tostring(boundary+block_size) .. " for maximum mapgen efficiency.")
|
||||||
|
end
|
||||||
|
if (YMIN+32)%block_size ~= 0 then
|
||||||
|
local boundary = YMIN - (YMIN+32)%block_size
|
||||||
|
minetest.log("warning", "[subterrane] The y_min setting "..tostring(YMIN)..
|
||||||
|
" for cavern layer " .. cave_layer_def.name .. " is not aligned with map chunk boundaries. Consider "..
|
||||||
|
tostring(boundary) .. " or " .. tostring(boundary+block_size) .. " for maximum mapgen efficiency.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local BLEND = math.min(cave_layer_def.boundary_blend_range, (YMAX-YMIN)/2)
|
||||||
|
|
||||||
|
local TCAVE = cave_layer_def.cave_threshold
|
||||||
|
local warren_area_threshold = cave_layer_def.warren_region_threshold -- determines how much volume warrens are found in around caverns
|
||||||
|
local warren_area_variability_threshold = cave_layer_def.warren_region_variability_threshold -- determines how much of the warren_area_threshold volume actually has warrens in it
|
||||||
|
local warren_threshold = cave_layer_def.warren_threshold -- determines narrowness of warrens themselves
|
||||||
|
|
||||||
|
local solidify_lava = cave_layer_def.solidify_lava
|
||||||
|
|
||||||
|
local np_cave = cave_layer_def.perlin_cave
|
||||||
|
local np_wave = cave_layer_def.perlin_wave
|
||||||
|
local np_warren_area = cave_layer_def.perlin_warren_area
|
||||||
|
local np_warrens = cave_layer_def.perlin_warrens
|
||||||
|
|
||||||
|
local y_blend_min = YMIN + BLEND * 1.5
|
||||||
|
local y_blend_max = YMAX - BLEND * 1.5
|
||||||
|
|
||||||
local column_def = cave_layer_def.columns
|
local column_def = cave_layer_def.columns
|
||||||
local c_column
|
local c_column
|
||||||
|
local c_warren_column
|
||||||
|
|
||||||
if column_def then
|
if column_def then
|
||||||
column_def.max_column_radius = column_def.max_column_radius or default_column.max_column_radius
|
c_column = minetest.get_content_id(column_def.node)
|
||||||
column_def.min_column_radius = column_def.min_column_radius or default_column.min_column_radius
|
if column_def.warren_node then
|
||||||
c_column = column_def.node or default_column.node
|
c_warren_column = minetest.get_content_id(column_def.warren_node)
|
||||||
column_def.weight = column_def.weight or default_column.weight
|
end
|
||||||
column_def.maximum_count = column_def.maximum_count or default_column.maximum_count
|
|
||||||
column_def.minimum_count = column_def.minimum_count or default_column.minimum_count
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- On generated function
|
local double_frequency = cave_layer_def.double_frequency
|
||||||
minetest.register_on_generated(function(minp, maxp, seed)
|
|
||||||
|
local decorate = cave_layer_def.decorate
|
||||||
|
|
||||||
|
if minetest.setting_getbool("subterrane_enable_singlenode_mapping_mode") then
|
||||||
|
decorate = nil
|
||||||
|
c_column = c_air
|
||||||
|
c_warren_column = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- On generated
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
minetest.register_on_generated(function(minp, maxp, seed)
|
||||||
|
|
||||||
--if out of range of cave definition limits, abort
|
--if out of range of cave definition limits, abort
|
||||||
if minp.y > YMAX or maxp.y < YMIN then
|
if minp.y > YMAX or maxp.y < YMIN then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create a table of biome ids for use with the biomemap.
|
|
||||||
if not subterrane.biome_ids then
|
|
||||||
subterrane.biome_ids = {}
|
|
||||||
for name, desc in pairs(minetest.registered_biomes) do
|
|
||||||
local i = minetest.get_biome_id(desc.name)
|
|
||||||
subterrane.biome_ids[i] = desc.name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--easy reference to commonly used values
|
|
||||||
local t_start = os.clock()
|
local t_start = os.clock()
|
||||||
local x_max = maxp.x
|
|
||||||
local y_max = maxp.y
|
|
||||||
local z_max = maxp.z
|
|
||||||
local x_min = minp.x
|
|
||||||
local y_min = minp.y
|
|
||||||
local z_min = minp.z
|
|
||||||
|
|
||||||
print ("[subterrane] chunk minp ("..x_min.." "..y_min.." "..z_min..")") --tell people you are generating a chunk
|
if c_lava_set == nil then
|
||||||
|
c_lava_set = {}
|
||||||
|
for name, def in pairs(minetest.registered_nodes) do
|
||||||
|
if def.groups ~= nil and def.groups.lava ~= nil then
|
||||||
|
c_lava_set[minetest.get_content_id(name)] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
local vm, data, data_param2, area = mapgen_helper.mapgen_vm_data_param2()
|
||||||
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
|
local nvals_cave, cave_area = mapgen_helper.perlin3d("subterrane:cave", minp, maxp, np_cave) --cave noise for structure
|
||||||
vm:get_data(data)
|
local nvals_wave = mapgen_helper.perlin3d("subterrane:wave", minp, maxp, np_wave) --wavy structure of cavern ceilings and floors
|
||||||
vm:get_param2_data(data_param2)
|
|
||||||
|
|
||||||
local biomemap = minetest.get_mapgen_object("biomemap")
|
-- pre-average everything so that the final values can be passed
|
||||||
|
-- along to the decorate function if it wants them
|
||||||
|
for vi, value in ipairs(nvals_cave) do
|
||||||
|
nvals_cave[vi] = (value + nvals_wave[vi])/2
|
||||||
|
end
|
||||||
|
|
||||||
--mandatory values
|
local warren_area_uninitialized = true
|
||||||
local sidelen = x_max - x_min + 1 --length of a mapblock
|
local nvals_warren_area
|
||||||
local chunk_lengths = {x = sidelen, y = sidelen, z = sidelen} --table of chunk edges
|
local warrens_uninitialized = true
|
||||||
local chunk_lengths2D = {x = sidelen, y = sidelen, z = 1}
|
local nvals_warrens
|
||||||
local minposxyz = {x = x_min, y = y_min, z = z_min} --bottom corner
|
|
||||||
local minposxz = {x = x_min, y = z_min} --2D bottom corner
|
-- The interp_yxz iterator iterates upwards in columns along the y axis.
|
||||||
|
-- starts at miny, goes to maxy, then switches to a new x,z and repeats.
|
||||||
|
local cave_iterator = cave_area:iterp_yxz(minp, maxp)
|
||||||
|
|
||||||
|
local previous_y = minp.y
|
||||||
|
local previous_node_state = outside_region
|
||||||
|
|
||||||
local column_points = nil
|
local column_points = nil
|
||||||
local column_weight = nil
|
local column_weight = nil
|
||||||
if column_def then
|
|
||||||
column_points = subterrane.get_scatter_grid(minp, sidelen*4, column_def.minimum_count, column_def.maximum_count)
|
-- This information might be of use to the decorate function.
|
||||||
column_points = subterrane.prune_points(minp, maxp, column_def.min_column_radius, column_def.max_column_radius, column_points)
|
cavern_data.contains_cavern = false
|
||||||
column_weight = column_def.weight
|
cavern_data.contains_warren = false
|
||||||
|
cavern_data.nvals_cave = nvals_cave
|
||||||
|
cavern_data.cave_area = cave_area
|
||||||
|
cavern_data.cavern_def = cave_layer_def
|
||||||
|
|
||||||
|
for vi, x, y, z in area:iterp_yxz(minp, maxp) do
|
||||||
|
local vi3d = cave_iterator() -- for use with noise data
|
||||||
|
|
||||||
|
if y < previous_y then
|
||||||
|
-- we've switched to a new column
|
||||||
|
previous_node_state = outside_region
|
||||||
end
|
end
|
||||||
|
previous_y = y
|
||||||
|
|
||||||
nobj_cave = nobj_cave or minetest.get_perlin_map(np_cave, chunk_lengths)
|
local cave_local_threshold
|
||||||
nobj_wave = nobj_wave or minetest.get_perlin_map(np_wave, chunk_lengths)
|
if y < y_blend_min then
|
||||||
|
cave_local_threshold = TCAVE + ((y_blend_min - y) / BLEND) ^ 2
|
||||||
local nvals_cave = nobj_cave:get3dMap_flat(minposxyz, nvals_cave_buffer) --cave noise for structure
|
elseif y > y_blend_max then
|
||||||
local nvals_wave = nobj_wave:get3dMap_flat(minposxyz, nvals_wave_buffer) --wavy structure of cavern ceilings and floors
|
cave_local_threshold = TCAVE + ((y - y_blend_max) / BLEND) ^ 2
|
||||||
|
|
||||||
local index_3d = 1 --3D node index
|
|
||||||
local index_2d = 1 --2D node index
|
|
||||||
|
|
||||||
for z = z_min, z_max do -- for each xy plane progressing northwards
|
|
||||||
--structure loop, hollows out the cavern
|
|
||||||
for y = y_min, y_max do -- for each x row progressing upwards
|
|
||||||
local tcave --declare variable
|
|
||||||
--determine the overall cave threshold
|
|
||||||
if y < yblmin then
|
|
||||||
tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
|
|
||||||
elseif y > yblmax then
|
|
||||||
tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
|
|
||||||
else
|
else
|
||||||
tcave = TCAVE
|
cave_local_threshold = TCAVE
|
||||||
end
|
end
|
||||||
|
|
||||||
local vi = area:index(x_min, y, z) --current node index
|
local cave_value = nvals_cave[vi3d]
|
||||||
for x = x_min, x_max do -- for each node do
|
if double_frequency then
|
||||||
|
if cave_value < 0 then
|
||||||
local biome_name = subterrane.biome_ids[biomemap[index_2d]]
|
cave_value = -cave_value
|
||||||
local biome = minetest.registered_biomes[biome_name]
|
if subterrane_enable_singlenode_mapping_mode then
|
||||||
|
c_cavern_air = c_desert_stone
|
||||||
if biome and biome._subterrane_override_sea_level and y <= biome._subterrane_override_sea_level then
|
c_warren_air = c_sandstone
|
||||||
local override_name = biome._subterrane_override_under_sea_biome
|
end
|
||||||
if override_name then
|
|
||||||
biome = minetest.registered_biomes[override_name]
|
|
||||||
else
|
else
|
||||||
biome = nil
|
if subterrane_enable_singlenode_mapping_mode then
|
||||||
|
c_cavern_air = c_stone
|
||||||
|
c_warren_air = c_clay
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local fill_node = c_air
|
-- inside a giant cavern
|
||||||
local column_node = c_column
|
if cave_value > cave_local_threshold then
|
||||||
if biome then
|
|
||||||
if biome._subterrane_fill_node then
|
|
||||||
fill_node = biome._subterrane_fill_node
|
|
||||||
end
|
|
||||||
if biome._subterrane_column_node then
|
|
||||||
column_node = biome._subterrane_column_node
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local cave_value = (nvals_cave[index_3d] + nvals_wave[index_3d])/2
|
|
||||||
local column_value = 0
|
local column_value = 0
|
||||||
if column_def then
|
if column_def then
|
||||||
column_value = subterrane.get_point_heat({x=x, y=y, z=z}, column_points)
|
if column_points == nil then
|
||||||
|
column_points = subterrane.get_column_points(minp, maxp, column_def)
|
||||||
|
column_weight = column_def.weight
|
||||||
end
|
end
|
||||||
if cave_value > tcave then --if node falls within cave threshold
|
column_value = subterrane.get_column_value({x=x, y=y, z=z}, column_points)
|
||||||
if cave_value > tcave then
|
|
||||||
if column_value > 0 and cave_value - column_value * column_weight < tcave then
|
|
||||||
data[vi] = column_node -- add a column
|
|
||||||
else
|
|
||||||
data[vi] = fill_node --hollow it out to make the cave
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif biome and biome._subterrane_cave_fill_node and data[vi] == c_air then
|
|
||||||
data[vi] = biome._subterrane_cave_fill_node
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if biome and biome._subterrane_mitigate_lava and cave_value > tcave - 0.1 then -- Eliminate nearby lava to keep it from spilling in
|
if column_value > 0 and cave_value - column_value * column_weight < cave_local_threshold then
|
||||||
if data[vi] == c_lava then
|
data[vi] = c_column -- add a column node
|
||||||
|
previous_node_state = inside_column
|
||||||
|
else
|
||||||
|
data[vi] = c_cavern_air --hollow it out to make the cave
|
||||||
|
cavern_data.contains_cavern = true
|
||||||
|
if previous_node_state == inside_ground then
|
||||||
|
-- we just entered the cavern from below
|
||||||
|
cavern_data.cavern_floor_count = cavern_data.cavern_floor_count + 1
|
||||||
|
cavern_floor_nodes[cavern_data.cavern_floor_count] = vi - area.ystride
|
||||||
|
end
|
||||||
|
previous_node_state = inside_cavern
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If there's lava near the edges of the cavern, solidify it.
|
||||||
|
if solidify_lava and cave_value > cave_local_threshold - 0.05 and c_lava_set[data[vi]] then
|
||||||
data[vi] = c_obsidian
|
data[vi] = c_obsidian
|
||||||
end
|
end
|
||||||
end
|
|
||||||
--increment indices
|
--borderlands of a giant cavern, possible warren area
|
||||||
index_3d = index_3d + 1
|
if cave_value <= cave_local_threshold and cave_value > warren_area_threshold then
|
||||||
index_2d = index_2d + 1
|
|
||||||
vi = vi + 1
|
if warren_area_uninitialized then
|
||||||
end
|
nvals_warren_area = mapgen_helper.perlin3d("subterrane:warren_area", minp, maxp, np_warren_area) -- determine which areas are spongey with warrens
|
||||||
index_2d = index_2d - sidelen --shift the 2D index back
|
warren_area_uninitialized = false
|
||||||
end
|
|
||||||
index_2d = index_2d + sidelen --shift the 2D index up a layer
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local index_3d = 1 --3D node index
|
local warren_area_value = nvals_warren_area[vi3d]
|
||||||
local index_2d = 1 --2D node index
|
if warren_area_value > warren_area_variability_threshold then
|
||||||
|
-- we're in a warren-containing area
|
||||||
|
if solidify_lava and c_lava_set[data[vi]] then
|
||||||
|
data[vi] = c_obsidian
|
||||||
|
end
|
||||||
|
|
||||||
for z = z_min, z_max do -- for each xy plane progressing northwards
|
if warrens_uninitialized then
|
||||||
|
nvals_warrens = mapgen_helper.perlin3d("subterrane:warrens", minp, maxp, np_warrens) --spongey warrens
|
||||||
|
warrens_uninitialized = false
|
||||||
|
end
|
||||||
|
|
||||||
--decoration loop, places nodes on floor and ceiling
|
-- we don't want warrens "cutting off" abruptly at the large-scale boundary noise thresholds, so turn these into gradients
|
||||||
for y = y_min, y_max do -- for each x row progressing upwards
|
-- that can be applied to choke off the warren gradually.
|
||||||
local tcave --same as above
|
local cave_value_edge = math.min(1, (cave_value - warren_area_threshold) * 20) -- make 0.3 = 0 and 0.25 = 1 to produce a border gradient
|
||||||
if y < yblmin then
|
local warren_area_value_edge = math.min(1, warren_area_value * 50) -- make 0 = 0 and 0.02 = 1 to produce a border gradient
|
||||||
tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
|
|
||||||
elseif y > yblmax then
|
local warren_value = nvals_warrens[vi3d]
|
||||||
tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
|
local warren_local_threshold = warren_threshold + (2 - warren_area_value_edge - cave_value_edge)
|
||||||
|
if warren_value > warren_local_threshold then
|
||||||
|
|
||||||
|
local column_value = 0
|
||||||
|
if column_def then
|
||||||
|
if column_points == nil then
|
||||||
|
column_points = subterrane.get_column_points(minp, maxp, column_def)
|
||||||
|
column_weight = column_def.weight
|
||||||
|
end
|
||||||
|
column_value = subterrane.get_column_value({x=x, y=y, z=z}, column_points)
|
||||||
|
end
|
||||||
|
|
||||||
|
if column_value > 0 and column_value + (warren_local_threshold - warren_value) * column_weight > 0 then
|
||||||
|
if c_warren_column then
|
||||||
|
data[vi] = c_warren_column -- add a column node
|
||||||
|
previous_node_state = inside_column
|
||||||
|
end
|
||||||
else
|
else
|
||||||
tcave = TCAVE
|
data[vi] = c_warren_air --hollow it out to make the cave
|
||||||
|
cavern_data.contains_warren = true
|
||||||
|
if previous_node_state == inside_ground then
|
||||||
|
-- we just entered the warren from below
|
||||||
|
cavern_data.warren_floor_count = cavern_data.warren_floor_count + 1
|
||||||
|
warren_floor_nodes[cavern_data.warren_floor_count] = vi - area.ystride
|
||||||
|
end
|
||||||
|
previous_node_state = inside_warren
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
local vi = area:index(x_min, y, z)
|
|
||||||
for x = x_min, x_max do -- for each node do
|
|
||||||
|
|
||||||
local biome_name = subterrane.biome_ids[biomemap[index_2d]]
|
-- If decorate is defined, we want to track all this stuff
|
||||||
local biome = minetest.registered_biomes[biome_name]
|
if decorate ~= nil then
|
||||||
local fill_node = c_air
|
local c_current_node = data[vi]
|
||||||
local cave_fill_node = c_air
|
local current_node_is_open = mapgen_helper.buildable_to(c_current_node)
|
||||||
|
|
||||||
if biome and biome._subterrane_override_sea_level and y <= biome._subterrane_override_sea_level then
|
if previous_node_state == inside_column then
|
||||||
local override_name = biome._subterrane_override_under_sea_biome
|
-- in this case previous node state is actually current node state,
|
||||||
if override_name then
|
-- we placed a column node during this loop
|
||||||
biome = minetest.registered_biomes[override_name]
|
cavern_data.column_count = cavern_data.column_count + 1
|
||||||
|
column_nodes[cavern_data.column_count] = vi
|
||||||
|
elseif previous_node_state == inside_ground and current_node_is_open then
|
||||||
|
-- we just entered a tunnel from below
|
||||||
|
cavern_data.tunnel_floor_count = cavern_data.tunnel_floor_count + 1
|
||||||
|
tunnel_floor_nodes[cavern_data.tunnel_floor_count] = vi-area.ystride
|
||||||
|
previous_node_state = inside_tunnel
|
||||||
|
elseif previous_node_state ~= inside_ground and not current_node_is_open then
|
||||||
|
if previous_node_state == inside_cavern then
|
||||||
|
--we just left the cavern from below
|
||||||
|
cavern_data.cavern_ceiling_count = cavern_data.cavern_ceiling_count + 1
|
||||||
|
cavern_ceiling_nodes[cavern_data.cavern_ceiling_count] = vi
|
||||||
|
elseif previous_node_state == inside_warren then
|
||||||
|
--we just left the cavern from below
|
||||||
|
cavern_data.warren_ceiling_count = cavern_data.warren_ceiling_count + 1
|
||||||
|
warren_ceiling_nodes[cavern_data.warren_ceiling_count] = vi
|
||||||
|
elseif previous_node_state == inside_tunnel then
|
||||||
|
-- we just left a tunnel from below
|
||||||
|
cavern_data.tunnel_ceiling_count = cavern_data.tunnel_ceiling_count + 1
|
||||||
|
tunnel_ceiling_nodes[cavern_data.tunnel_ceiling_count] = vi
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we laid down a column node we don't want to switch to "inside ground",
|
||||||
|
-- if we hit air next node then it'll get flagged as a floor node and we don't want that for columns
|
||||||
|
if previous_node_state ~= inside_column then
|
||||||
|
previous_node_state = inside_ground
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
biome = nil
|
-- This will prevent any values from being inserted into the node lists, saving
|
||||||
|
-- a bunch of memory and processor time
|
||||||
|
previous_node_state = outside_region
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if biome then
|
if decorate then
|
||||||
local cave_value = (nvals_cave[index_3d] + nvals_wave[index_3d])/2
|
close_node_arrays() -- inserts nil after the last node so that ipairs will function correctly
|
||||||
-- only check nodes near the edges of caverns
|
decorate(minp, maxp, seed, vm, cavern_data, area, data)
|
||||||
if math.floor(cave_value*30) == math.floor(tcave*30) then
|
clear_node_arrays() -- if decorate is not defined these arrays will never have anything added to them, so it's safe to not call this in that case
|
||||||
if biome._subterrane_fill_node then
|
|
||||||
fill_node = biome._subterrane_fill_node
|
|
||||||
end
|
|
||||||
--ceiling
|
|
||||||
local ai = area:index(x,y+1,z) --above index
|
|
||||||
local bi = area:index(x,y-1,z) --below index
|
|
||||||
|
|
||||||
if biome._subterrane_ceiling_decor
|
|
||||||
and data[ai] ~= fill_node
|
|
||||||
and data[vi] == fill_node
|
|
||||||
and y < y_max
|
|
||||||
then --ceiling
|
|
||||||
biome._subterrane_ceiling_decor(area, data, ai, vi, bi, data_param2)
|
|
||||||
end
|
|
||||||
--ground
|
|
||||||
if biome._subterrane_floor_decor
|
|
||||||
and data[bi] ~= fill_node
|
|
||||||
and data[vi] == fill_node
|
|
||||||
and y > y_min
|
|
||||||
then --ground
|
|
||||||
biome._subterrane_floor_decor(area, data, ai, vi, bi, data_param2)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif cave_value <= tcave then --if node falls outside cave threshold
|
|
||||||
-- decorate other "native" caves and tunnels
|
|
||||||
if biome._subterrane_cave_fill_node then
|
|
||||||
cave_fill_node = biome._subterrane_cave_fill_node
|
|
||||||
if data[vi] == c_air then
|
|
||||||
data[vi] = cave_fill_node
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ai = area:index(x,y+1,z) --above index
|
|
||||||
local bi = area:index(x,y-1,z) --below index
|
|
||||||
|
|
||||||
if biome._subterrane_cave_ceiling_decor
|
|
||||||
and data[ai] ~= cave_fill_node
|
|
||||||
and data[vi] == cave_fill_node
|
|
||||||
and y < y_max
|
|
||||||
then --ceiling
|
|
||||||
biome._subterrane_cave_ceiling_decor(area, data, ai, vi, bi, data_param2)
|
|
||||||
end
|
|
||||||
if biome._subterrane_cave_floor_decor
|
|
||||||
and data[bi] ~= cave_fill_node
|
|
||||||
and data[vi] == cave_fill_node
|
|
||||||
and y > y_min
|
|
||||||
then --ground
|
|
||||||
biome._subterrane_cave_floor_decor(area, data, ai, vi, bi, data_param2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
index_3d = index_3d + 1
|
|
||||||
index_2d = index_2d + 1
|
|
||||||
vi = vi + 1
|
|
||||||
end
|
|
||||||
index_2d = index_2d - sidelen --shift the 2D index back
|
|
||||||
end
|
|
||||||
index_2d = index_2d + sidelen --shift the 2D index up a layer
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--send data back to voxelmanip
|
--send data back to voxelmanip
|
||||||
vm:set_data(data)
|
vm:set_data(data)
|
||||||
vm:set_param2_data(data_param2)
|
|
||||||
--calc lighting
|
--calc lighting
|
||||||
vm:set_lighting({day = 0, night = 0})
|
vm:set_lighting({day = 0, night = 0})
|
||||||
vm:calc_lighting()
|
vm:calc_lighting()
|
||||||
|
vm:update_liquids()
|
||||||
--write it to world
|
--write it to world
|
||||||
vm:write_to_map()
|
vm:write_to_map()
|
||||||
|
|
||||||
local chunk_generation_time = math.ceil((os.clock() - t_start) * 1000) --grab how long it took
|
local chunk_generation_time = math.ceil((os.clock() - t_start) * 1000) --grab how long it took
|
||||||
print ("[subterrane] "..chunk_generation_time.." ms") --tell people how long
|
if chunk_generation_time < 1000 then
|
||||||
end)
|
minetest.log("info", "[subterrane] "..chunk_generation_time.." ms to generate " .. cave_layer_def.name) --tell people how long
|
||||||
|
else
|
||||||
|
minetest.log("warning", "[subterrane] took "..chunk_generation_time.." ms to generate map block "
|
||||||
|
.. minetest.pos_to_string(minp) .. minetest.pos_to_string(maxp) .. " in cave layer " .. cave_layer_def.name)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
minetest.log("info", "[Subterrane] loaded!")
|
||||||
function subterrane:register_cave_decor(minimum_depth, maximum_depth)
|
|
||||||
|
|
||||||
-- On generated function
|
|
||||||
minetest.register_on_generated(function(minp, maxp, seed)
|
|
||||||
--if out of range of cave definition limits, abort
|
|
||||||
if minp.y > minimum_depth or maxp.y < maximum_depth then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a table of biome ids for use with the biomemap.
|
|
||||||
if not subterrane.biome_ids then
|
|
||||||
subterrane.biome_ids = {}
|
|
||||||
for name, desc in pairs(minetest.registered_biomes) do
|
|
||||||
local i = minetest.get_biome_id(desc.name)
|
|
||||||
subterrane.biome_ids[i] = desc.name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--easy reference to commonly used values
|
|
||||||
local t_start = os.clock()
|
|
||||||
local x_max = maxp.x
|
|
||||||
local y_max = maxp.y
|
|
||||||
local z_max = maxp.z
|
|
||||||
local x_min = minp.x
|
|
||||||
local y_min = minp.y
|
|
||||||
local z_min = minp.z
|
|
||||||
|
|
||||||
print ("[subterrane] chunk minp ("..x_min.." "..y_min.." "..z_min..")") --tell people you are generating a chunk
|
|
||||||
|
|
||||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
|
||||||
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
|
|
||||||
vm:get_data(data)
|
|
||||||
vm:get_param2_data(data_param2)
|
|
||||||
|
|
||||||
local biomemap = minetest.get_mapgen_object("biomemap")
|
|
||||||
|
|
||||||
local sidelen = x_max - x_min + 1 --length of a mapblock
|
|
||||||
|
|
||||||
local index_3d = 1 --3D node index
|
|
||||||
local index_2d = 1 --2D node index
|
|
||||||
|
|
||||||
for z = z_min, z_max do -- for each xy plane progressing northwards
|
|
||||||
--decoration loop, places nodes on floor and ceiling
|
|
||||||
for y = y_min, y_max do -- for each x row progressing upwards
|
|
||||||
local vi = area:index(x_min, y, z)
|
|
||||||
for x = x_min, x_max do -- for each node do
|
|
||||||
|
|
||||||
local biome_name = subterrane.biome_ids[biomemap[index_2d]]
|
|
||||||
local biome = minetest.registered_biomes[biome_name]
|
|
||||||
local cave_fill_node = c_air
|
|
||||||
|
|
||||||
if biome then
|
|
||||||
-- decorate "native" caves and tunnels
|
|
||||||
if biome._subterrane_cave_fill_node then
|
|
||||||
cave_fill_node = biome._subterrane_cave_fill_node
|
|
||||||
if data[vi] == c_air then
|
|
||||||
data[vi] = cave_fill_node
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ai = area:index(x,y+1,z) --above index
|
|
||||||
local bi = area:index(x,y-1,z) --below index
|
|
||||||
|
|
||||||
if biome._subterrane_cave_ceiling_decor
|
|
||||||
and data[ai] ~= cave_fill_node
|
|
||||||
and data[vi] == cave_fill_node
|
|
||||||
and y < y_max
|
|
||||||
then --ceiling
|
|
||||||
biome._subterrane_cave_ceiling_decor(area, data, ai, vi, bi, data_param2)
|
|
||||||
end
|
|
||||||
--ground
|
|
||||||
if biome._subterrane_cave_floor_decor
|
|
||||||
and data[bi] ~= cave_fill_node
|
|
||||||
and data[vi] == cave_fill_node
|
|
||||||
and y > y_min
|
|
||||||
then --ground
|
|
||||||
biome._subterrane_cave_floor_decor(area, data, ai, vi, bi, data_param2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
index_3d = index_3d + 1
|
|
||||||
index_2d = index_2d + 1
|
|
||||||
vi = vi + 1
|
|
||||||
end
|
|
||||||
index_2d = index_2d - sidelen --shift the 2D index back
|
|
||||||
end
|
|
||||||
index_2d = index_2d + sidelen --shift the 2D index up a layer
|
|
||||||
end
|
|
||||||
|
|
||||||
--send data back to voxelmanip
|
|
||||||
vm:set_data(data)
|
|
||||||
vm:set_param2_data(data_param2)
|
|
||||||
--calc lighting
|
|
||||||
vm:set_lighting({day = 0, night = 0})
|
|
||||||
vm:calc_lighting()
|
|
||||||
--write it to world
|
|
||||||
vm:write_to_map()
|
|
||||||
|
|
||||||
local chunk_generation_time = math.ceil((os.clock() - t_start) * 1000) --grab how long it took
|
|
||||||
print ("[subterrane] "..chunk_generation_time.." ms") --tell people how long
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
print("[Subterrane] loaded!")
|
|
||||||
|
484
legacy.lua
484
legacy.lua
@ -43,26 +43,478 @@ minetest.register_node("subterrane:wet_flowstone", {
|
|||||||
sounds = default.node_sound_stone_defaults(),
|
sounds = default.node_sound_stone_defaults(),
|
||||||
})
|
})
|
||||||
|
|
||||||
local dry_stalagmite_ids = {
|
end
|
||||||
minetest.get_content_id("subterrane:dry_stal_1"), -- thinnest
|
|
||||||
minetest.get_content_id("subterrane:dry_stal_2"),
|
function subterrane:small_stalagmite(vi, area, data, param2_data, param2, height, stalagmite_id)
|
||||||
minetest.get_content_id("subterrane:dry_stal_3"),
|
subterrane.stalagmite(vi, area, data, param2_data, param2, height, stalagmite_id)
|
||||||
minetest.get_content_id("subterrane:dry_stal_4"), -- thickest
|
end
|
||||||
|
|
||||||
|
function subterrane:giant_stalagmite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
||||||
|
subterrane.big_stalagmite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
||||||
|
end
|
||||||
|
|
||||||
|
function subterrane:giant_stalactite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
||||||
|
subterrane.big_stalactite(vi, area, data, min_height, max_height, base_material, root_material, shaft_material)
|
||||||
|
end
|
||||||
|
|
||||||
|
function subterrane:giant_shroom(vi, area, data, stem_material, cap_material, gill_material, stem_height, cap_radius, ignore_bounds)
|
||||||
|
subterrane.giant_mushroom(vi, area, data, stem_material, cap_material, gill_material, stem_height, cap_radius, ignore_bounds)
|
||||||
|
end
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
-- Original cave registration code.
|
||||||
|
|
||||||
|
|
||||||
|
local c_lava = minetest.get_content_id("default:lava_source")
|
||||||
|
local c_obsidian = minetest.get_content_id("default:obsidian")
|
||||||
|
local c_stone = minetest.get_content_id("default:stone")
|
||||||
|
local c_air = minetest.get_content_id("air")
|
||||||
|
|
||||||
|
subterrane.default_perlin_cave = {
|
||||||
|
offset = 0,
|
||||||
|
scale = 1,
|
||||||
|
spread = {x=256, y=256, z=256},
|
||||||
|
seed = -400000000089,
|
||||||
|
octaves = 3,
|
||||||
|
persist = 0.67
|
||||||
}
|
}
|
||||||
|
|
||||||
local wet_stalagmite_ids = {
|
subterrane.default_perlin_wave = {
|
||||||
minetest.get_content_id("subterrane:wet_stal_1"), -- thinnest
|
offset = 0,
|
||||||
minetest.get_content_id("subterrane:wet_stal_2"),
|
scale = 1,
|
||||||
minetest.get_content_id("subterrane:wet_stal_3"),
|
spread = {x=512, y=256, z=512}, -- squashed 2:1
|
||||||
minetest.get_content_id("subterrane:wet_stal_4"), -- thickest
|
seed = 59033,
|
||||||
|
octaves = 6,
|
||||||
|
persist = 0.63
|
||||||
}
|
}
|
||||||
|
|
||||||
function subterrane:stalagmite(vi, area, data, param2_data, param2, height, is_wet)
|
-- cave_layer_def
|
||||||
if is_wet then
|
--{
|
||||||
subterrane:small_stalagmite(vi, area, data, param2_data, param2, height, wet_stalagmite_ids)
|
-- minimum_depth = -- required, the highest elevation this cave layer will be generated in.
|
||||||
|
-- maximum_depth = -- required, the lowest elevation this cave layer will be generated in.
|
||||||
|
-- cave_threshold = -- optional, Cave threshold. Defaults to 0.5. 1 = small rare caves, 0.5 = 1/3rd ground volume, 0 = 1/2 ground volume
|
||||||
|
-- boundary_blend_range = -- optional, range near ymin and ymax over which caves diminish to nothing. Defaults to 128.
|
||||||
|
-- perlin_cave = -- optional, a 3D perlin noise definition table to define the shape of the caves
|
||||||
|
-- perlin_wave = -- optional, a 3D perlin noise definition table that's averaged with the cave noise to add more horizontal surfaces (squash its spread on the y axis relative to perlin_cave to accomplish this)
|
||||||
|
-- columns = -- optional, a column_def table for producing truly enormous dripstone formations
|
||||||
|
--}
|
||||||
|
|
||||||
|
-- column_def
|
||||||
|
--{
|
||||||
|
-- max_column_radius = -- Maximum radius for individual columns, defaults to 10
|
||||||
|
-- min_column_radius = -- Minimum radius for individual columns, defaults to 2 (going lower can increase the likelihood of "intermittent" columns with floating sections)
|
||||||
|
-- node = -- node name to build columns out of. Defaults to default:stone
|
||||||
|
-- weight = -- a floating point value (usually in the range of 0.5-1) to modify how strongly the column is affected by the surrounding cave. Lower values create a more variable, tapered stalactite/stalagmite combination whereas a value of 1 produces a roughly cylindrical column. Defaults to 0.5
|
||||||
|
-- maximum_count = -- The maximum number of columns placed in any given column region (each region being a square 4 times the length and width of a map chunk). Defaults to 100
|
||||||
|
-- minimum_count = -- The minimum number of columns placed in a column region. The actual number placed will be randomly selected between this range. Defaults to 25.
|
||||||
|
--}
|
||||||
|
|
||||||
|
--extra biome properties used by subterrane
|
||||||
|
--{
|
||||||
|
-- _subterrane_ceiling_decor = -- function for putting stuff on the ceiling of the big caverns
|
||||||
|
-- _subterrane_floor_decor = -- function for putting stuff on the floor of the big caverns
|
||||||
|
-- _subterrane_fill_node = -- node to fill the cavern with (defaults to air)
|
||||||
|
-- _subterrane_column_node = -- override the node the giant columns in this biome are made from
|
||||||
|
-- _subterrane_cave_floor_decor = -- function for putting stuff on the floors of other preexisting open space
|
||||||
|
-- _subterrane_cave_ceiling_decor = -- function for putting stuff on the ceiling of other preexisting open space
|
||||||
|
-- _subterrane_mitigate_lava = -- try to patch the walls of big caverns with obsidian plugs when lava intersects. Not perfect, but helpful.
|
||||||
|
-- _subterrane_override_sea_level = -- Y coordinate where an underground sea level begins. Biomes' y coordinate cutoffs are unreliable underground, this forces subterrane to take this sea level cutoff into account.
|
||||||
|
-- _subterrane_override_under_sea_biome = -- When below the override_sea_level, the biome with this name will be looked up and substituted.
|
||||||
|
-- _subterrane_column_node = -- overrides the node type of a cavern layer's column_def, if there are columns here.
|
||||||
|
--}
|
||||||
|
|
||||||
|
local default_column = {
|
||||||
|
max_column_radius = 10,
|
||||||
|
min_column_radius = 2,
|
||||||
|
node = c_stone,
|
||||||
|
weight = 0.25,
|
||||||
|
maximum_count = 100,
|
||||||
|
minimum_count = 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
function subterrane:register_cave_layer(cave_layer_def)
|
||||||
|
|
||||||
|
table.insert(subterrane.registered_layers, cave_layer_def)
|
||||||
|
|
||||||
|
local YMIN = cave_layer_def.maximum_depth
|
||||||
|
local YMAX = cave_layer_def.minimum_depth
|
||||||
|
local BLEND = math.min(cave_layer_def.boundary_blend_range or 128, (YMAX-YMIN)/2)
|
||||||
|
local TCAVE = cave_layer_def.cave_threshold or 0.5
|
||||||
|
|
||||||
|
local np_cave = cave_layer_def.perlin_cave or subterrane.default_perlin_cave
|
||||||
|
local np_wave = cave_layer_def.perlin_wave or subterrane.default_perlin_wave
|
||||||
|
|
||||||
|
local yblmin = YMIN + BLEND * 1.5
|
||||||
|
local yblmax = YMAX - BLEND * 1.5
|
||||||
|
|
||||||
|
local column_def = cave_layer_def.columns
|
||||||
|
local c_column
|
||||||
|
|
||||||
|
if column_def then
|
||||||
|
column_def.max_column_radius = column_def.max_column_radius or default_column.max_column_radius
|
||||||
|
column_def.min_column_radius = column_def.min_column_radius or default_column.min_column_radius
|
||||||
|
c_column = column_def.node or default_column.node
|
||||||
|
column_def.weight = column_def.weight or default_column.weight
|
||||||
|
column_def.maximum_count = column_def.maximum_count or default_column.maximum_count
|
||||||
|
column_def.minimum_count = column_def.minimum_count or default_column.minimum_count
|
||||||
|
end
|
||||||
|
|
||||||
|
-- On generated function
|
||||||
|
minetest.register_on_generated(function(minp, maxp, seed)
|
||||||
|
--if out of range of cave definition limits, abort
|
||||||
|
if minp.y > YMAX or maxp.y < YMIN then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local t_start = os.clock()
|
||||||
|
local y_max = maxp.y
|
||||||
|
local y_min = minp.y
|
||||||
|
|
||||||
|
minetest.log("info", "[subterrane] chunk minp " .. minetest.pos_to_string(minp)) --tell people you are generating a chunk
|
||||||
|
|
||||||
|
local vm, data, data_param2, area = mapgen_helper.mapgen_vm_data_param2()
|
||||||
|
|
||||||
|
local layer_range_name = tostring(YMIN).." to "..tostring(YMAX)
|
||||||
|
local nvals_cave, cave_area = mapgen_helper.perlin3d("cave "..layer_range_name, minp, maxp, np_cave) --cave noise for structure
|
||||||
|
local nvals_wave = mapgen_helper.perlin3d("wave "..layer_range_name, minp, maxp, np_wave) --wavy structure of cavern ceilings and floors
|
||||||
|
local cave_iterator = cave_area:iterp(minp, maxp)
|
||||||
|
|
||||||
|
local biomemap = minetest.get_mapgen_object("biomemap")
|
||||||
|
|
||||||
|
local column_points = nil
|
||||||
|
local column_weight = nil
|
||||||
|
if column_def then
|
||||||
|
column_points = subterrane.get_column_points(minp, maxp, column_def)
|
||||||
|
column_weight = column_def.weight
|
||||||
|
end
|
||||||
|
|
||||||
|
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
|
||||||
|
local index_3d = cave_iterator()
|
||||||
|
local index_2d = mapgen_helper.index2d(minp, maxp, x, z)
|
||||||
|
|
||||||
|
local tcave --declare variable
|
||||||
|
--determine the overall cave threshold
|
||||||
|
if y < yblmin then
|
||||||
|
tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
|
||||||
|
elseif y > yblmax then
|
||||||
|
tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
|
||||||
else
|
else
|
||||||
subterrane:small_stalagmite(vi, area, data, param2_data, param2, height, dry_stalagmite_ids)
|
tcave = TCAVE
|
||||||
|
end
|
||||||
|
|
||||||
|
local biome
|
||||||
|
if biomemap then
|
||||||
|
biome = mapgen_helper.get_biome_def(biomemap[index_2d])
|
||||||
|
end
|
||||||
|
|
||||||
|
if biome and biome._subterrane_override_sea_level and y <= biome._subterrane_override_sea_level then
|
||||||
|
local override_name = biome._subterrane_override_under_sea_biome
|
||||||
|
if override_name then
|
||||||
|
biome = minetest.registered_biomes[override_name]
|
||||||
|
else
|
||||||
|
biome = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local fill_node = c_air
|
||||||
|
local column_node = c_column
|
||||||
|
if biome then
|
||||||
|
if biome._subterrane_fill_node then
|
||||||
|
fill_node = biome._subterrane_fill_node
|
||||||
|
end
|
||||||
|
if biome._subterrane_column_node then
|
||||||
|
column_node = biome._subterrane_column_node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cave_value = (nvals_cave[index_3d] + nvals_wave[index_3d])/2
|
||||||
|
if cave_value > tcave then --if node falls within cave threshold
|
||||||
|
local column_value = 0
|
||||||
|
if column_def then
|
||||||
|
column_value = subterrane.get_point_heat({x=x, y=y, z=z}, column_points)
|
||||||
|
end
|
||||||
|
if column_value > 0 and cave_value - column_value * column_weight < tcave then
|
||||||
|
data[vi] = column_node -- add a column
|
||||||
|
else
|
||||||
|
data[vi] = fill_node --hollow it out to make the cave
|
||||||
|
end
|
||||||
|
elseif biome and biome._subterrane_cave_fill_node and data[vi] == c_air then
|
||||||
|
data[vi] = biome._subterrane_cave_fill_node
|
||||||
|
end
|
||||||
|
|
||||||
|
if biome and biome._subterrane_mitigate_lava and cave_value > tcave - 0.1 then -- Eliminate nearby lava to keep it from spilling in
|
||||||
|
if data[vi] == c_lava then
|
||||||
|
data[vi] = c_obsidian
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cave_iterator = cave_area:iterp(minp, maxp) -- reset this iterator
|
||||||
|
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
|
||||||
|
local index_3d = cave_iterator()
|
||||||
|
local index_2d = mapgen_helper.index2d(minp, maxp, x, z)
|
||||||
|
|
||||||
|
local ai = vi + area.ystride
|
||||||
|
local bi = vi - area.ystride
|
||||||
|
|
||||||
|
local tcave --same as above
|
||||||
|
if y < yblmin then
|
||||||
|
tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
|
||||||
|
elseif y > yblmax then
|
||||||
|
tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
|
||||||
|
else
|
||||||
|
tcave = TCAVE
|
||||||
|
end
|
||||||
|
|
||||||
|
local biome
|
||||||
|
if biomemap then
|
||||||
|
biome = mapgen_helper.get_biome_def(biomemap[index_2d])
|
||||||
|
end
|
||||||
|
local fill_node = c_air
|
||||||
|
local cave_fill_node = c_air
|
||||||
|
|
||||||
|
if biome and biome._subterrane_override_sea_level and y <= biome._subterrane_override_sea_level then
|
||||||
|
local override_name = biome._subterrane_override_under_sea_biome
|
||||||
|
if override_name then
|
||||||
|
biome = minetest.registered_biomes[override_name]
|
||||||
|
else
|
||||||
|
biome = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if biome then
|
||||||
|
local cave_value = (nvals_cave[index_3d] + nvals_wave[index_3d])/2
|
||||||
|
-- only check nodes near the edges of caverns
|
||||||
|
if cave_value > tcave - 0.05 and cave_value < tcave + 0.05 then
|
||||||
|
if biome._subterrane_fill_node then
|
||||||
|
fill_node = biome._subterrane_fill_node
|
||||||
|
end
|
||||||
|
--ceiling
|
||||||
|
if biome._subterrane_ceiling_decor
|
||||||
|
and data[ai] ~= fill_node
|
||||||
|
and data[vi] == fill_node
|
||||||
|
and y < y_max
|
||||||
|
then --ceiling
|
||||||
|
biome._subterrane_ceiling_decor(area, data, ai, vi, bi, data_param2)
|
||||||
|
end
|
||||||
|
--floor
|
||||||
|
if biome._subterrane_floor_decor
|
||||||
|
and data[bi] ~= fill_node
|
||||||
|
and data[vi] == fill_node
|
||||||
|
and y > y_min
|
||||||
|
then --floor
|
||||||
|
biome._subterrane_floor_decor(area, data, ai, vi, bi, data_param2)
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif cave_value <= tcave then --if node falls outside cave threshold
|
||||||
|
-- decorate other "native" caves and tunnels
|
||||||
|
if biome._subterrane_cave_fill_node then
|
||||||
|
cave_fill_node = biome._subterrane_cave_fill_node
|
||||||
|
if data[vi] == c_air then
|
||||||
|
data[vi] = cave_fill_node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if biome._subterrane_cave_ceiling_decor
|
||||||
|
and data[ai] ~= cave_fill_node
|
||||||
|
and data[vi] == cave_fill_node
|
||||||
|
and y < y_max
|
||||||
|
then --ceiling
|
||||||
|
biome._subterrane_cave_ceiling_decor(area, data, ai, vi, bi, data_param2)
|
||||||
|
end
|
||||||
|
if biome._subterrane_cave_floor_decor
|
||||||
|
and data[bi] ~= cave_fill_node
|
||||||
|
and data[vi] == cave_fill_node
|
||||||
|
and y > y_min
|
||||||
|
then --ground
|
||||||
|
biome._subterrane_cave_floor_decor(area, data, ai, vi, bi, data_param2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--send data back to voxelmanip
|
||||||
|
vm:set_data(data)
|
||||||
|
vm:set_param2_data(data_param2)
|
||||||
|
--calc lighting
|
||||||
|
vm:set_lighting({day = 0, night = 0})
|
||||||
|
vm:calc_lighting()
|
||||||
|
|
||||||
|
--write it to world
|
||||||
|
vm:write_to_map()
|
||||||
|
|
||||||
|
local chunk_generation_time = math.ceil((os.clock() - t_start) * 1000) --grab how long it took
|
||||||
|
if chunk_generation_time < 1000 then
|
||||||
|
minetest.log("info", "[subterrane] "..chunk_generation_time.." ms") --tell people how long
|
||||||
|
else
|
||||||
|
minetest.log("warning", "[subterrane] took "..chunk_generation_time.." ms to generate map block "
|
||||||
|
.. minetest.pos_to_string(minp) .. minetest.pos_to_string(maxp))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function subterrane:register_cave_decor(minimum_depth, maximum_depth)
|
||||||
|
|
||||||
|
-- On generated function
|
||||||
|
minetest.register_on_generated(function(minp, maxp, seed)
|
||||||
|
--if out of range of cave definition limits, abort
|
||||||
|
if minp.y > minimum_depth or maxp.y < maximum_depth then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--easy reference to commonly used values
|
||||||
|
local t_start = os.clock()
|
||||||
|
local y_max = maxp.y
|
||||||
|
local y_min = minp.y
|
||||||
|
|
||||||
|
minetest.log("info", "[subterrane] chunk minp " .. minetest.pos_to_string(minp)) --tell people you are generating a chunk
|
||||||
|
|
||||||
|
local vm, data, data_param2, area = mapgen_helper.mapgen_vm_data_param2()
|
||||||
|
local biomemap = minetest.get_mapgen_object("biomemap")
|
||||||
|
|
||||||
|
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
|
||||||
|
--decoration loop, places nodes on floor and ceiling
|
||||||
|
local index_2d = mapgen_helper.index2d(minp, maxp, x, z)
|
||||||
|
local ai = vi + area.ystride
|
||||||
|
local bi = vi - area.ystride
|
||||||
|
|
||||||
|
local biome
|
||||||
|
if biomemap then
|
||||||
|
biome = mapgen_helper.get_biome_def(biomemap[index_2d])
|
||||||
|
end
|
||||||
|
local cave_fill_node = c_air
|
||||||
|
|
||||||
|
if biome then
|
||||||
|
-- decorate "native" caves and tunnels
|
||||||
|
if biome._subterrane_cave_fill_node then
|
||||||
|
cave_fill_node = biome._subterrane_cave_fill_node
|
||||||
|
if data[vi] == c_air then
|
||||||
|
data[vi] = cave_fill_node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if biome._subterrane_cave_ceiling_decor
|
||||||
|
and data[ai] ~= cave_fill_node
|
||||||
|
and data[vi] == cave_fill_node
|
||||||
|
and y < y_max
|
||||||
|
then --ceiling
|
||||||
|
biome._subterrane_cave_ceiling_decor(area, data, ai, vi, bi, data_param2)
|
||||||
|
end
|
||||||
|
--ground
|
||||||
|
if biome._subterrane_cave_floor_decor
|
||||||
|
and data[bi] ~= cave_fill_node
|
||||||
|
and data[vi] == cave_fill_node
|
||||||
|
and y > y_min
|
||||||
|
then --ground
|
||||||
|
biome._subterrane_cave_floor_decor(area, data, ai, vi, bi, data_param2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--send data back to voxelmanip
|
||||||
|
vm:set_data(data)
|
||||||
|
vm:set_param2_data(data_param2)
|
||||||
|
--calc lighting
|
||||||
|
vm:set_lighting({day = 0, night = 0})
|
||||||
|
vm:calc_lighting()
|
||||||
|
--write it to world
|
||||||
|
vm:write_to_map()
|
||||||
|
|
||||||
|
local chunk_generation_time = math.ceil((os.clock() - t_start) * 1000) --grab how long it took
|
||||||
|
if chunk_generation_time < 1000 then
|
||||||
|
minetest.log("info", "[subterrane] "..chunk_generation_time.." ms") --tell people how long
|
||||||
|
else
|
||||||
|
minetest.log("warning", "[subterrane] took "..chunk_generation_time.." ms to generate a map block")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
--FUNCTIONS--
|
||||||
|
|
||||||
|
local grid_size = mapgen_helper.block_size * 4
|
||||||
|
|
||||||
|
function subterrane:vertically_consistent_randomp(pos)
|
||||||
|
local next_seed = math.random(1, 1000000000000)
|
||||||
|
math.randomseed(minetest.hash_node_position({x=pos.x, y=0, z=pos.z}))
|
||||||
|
local output = math.random()
|
||||||
|
math.randomseed(next_seed)
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
function subterrane:vertically_consistent_random(vi, area)
|
||||||
|
local pos = area:position(vi)
|
||||||
|
return subterrane:vertically_consistent_randomp(pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
subterrane.get_column_points = function(minp, maxp, column_def)
|
||||||
|
local grids = mapgen_helper.get_nearest_regions(minp, grid_size)
|
||||||
|
local points = {}
|
||||||
|
for _, grid in ipairs(grids) do
|
||||||
|
--The y value of the returned point will be the radius of the column
|
||||||
|
local minp = {x=grid.x, y = column_def.min_column_radius*100, z=grid.z}
|
||||||
|
local maxp = {x=grid.x+grid_size-1, y=column_def.max_column_radius*100, z=grid.z+grid_size-1}
|
||||||
|
for _, point in ipairs(mapgen_helper.get_random_points(minp, maxp, column_def.minimum_count, column_def.maximum_count)) do
|
||||||
|
point.y = point.y / 100
|
||||||
|
if point.x > minp.x - point.y
|
||||||
|
and point.x < maxp.x + point.y
|
||||||
|
and point.z > minp.z - point.y
|
||||||
|
and point.z < maxp.z + point.y then
|
||||||
|
table.insert(points, point)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return points
|
||||||
|
end
|
||||||
|
|
||||||
|
subterrane.get_point_heat = function(pos, points)
|
||||||
|
local heat = 0
|
||||||
|
for _, point in ipairs(points) do
|
||||||
|
local axis_point = {x=point.x, y=pos.y, z=point.z}
|
||||||
|
local radius = point.y
|
||||||
|
if (pos.x >= axis_point.x-radius and pos.x <= axis_point.x+radius
|
||||||
|
and pos.z >= axis_point.z-radius and pos.z <= axis_point.z+radius) then
|
||||||
|
|
||||||
|
local dist = vector.distance(pos, axis_point)
|
||||||
|
if dist < radius then
|
||||||
|
heat = math.max(heat, 1 - dist/radius)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return heat
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Unfortunately there's no easy way to override a single biome, so do it by wiping everything and re-registering
|
||||||
|
-- Not only that, but the decorations also need to be wiped and re-registered - it appears they keep
|
||||||
|
-- track of the biome they belong to via an internal ID that gets changed when the biomes
|
||||||
|
-- are re-registered, resulting in them being left assigned to the wrong biomes.
|
||||||
|
function subterrane:override_biome(biome_def)
|
||||||
|
|
||||||
|
--Minetest 0.5 adds this "unregister biome" method
|
||||||
|
if minetest.unregister_biome and biome_def.name then
|
||||||
|
minetest.unregister_biome(biome_def.name)
|
||||||
|
minetest.register_biome(biome_def)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local registered_biomes_copy = {}
|
||||||
|
for old_biome_key, old_biome_def in pairs(minetest.registered_biomes) do
|
||||||
|
registered_biomes_copy[old_biome_key] = old_biome_def
|
||||||
|
end
|
||||||
|
local registered_decorations_copy = {}
|
||||||
|
for old_decoration_key, old_decoration_def in pairs(minetest.registered_decorations) do
|
||||||
|
registered_decorations_copy[old_decoration_key] = old_decoration_def
|
||||||
|
end
|
||||||
|
|
||||||
|
registered_biomes_copy[biome_def.name] = biome_def
|
||||||
|
|
||||||
|
minetest.clear_registered_decorations()
|
||||||
|
minetest.clear_registered_biomes()
|
||||||
|
for biome_key, new_biome_def in pairs(registered_biomes_copy) do
|
||||||
|
minetest.register_biome(new_biome_def)
|
||||||
|
end
|
||||||
|
for decoration_key, new_decoration_def in pairs(registered_decorations_copy) do
|
||||||
|
minetest.register_decoration(new_decoration_def)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
|
118
nodes.lua
118
nodes.lua
@ -1,118 +0,0 @@
|
|||||||
local x_disp = 0.125
|
|
||||||
local z_disp = 0.125
|
|
||||||
|
|
||||||
local stal_on_place = function(itemstack, placer, pointed_thing, itemname)
|
|
||||||
local pt = pointed_thing
|
|
||||||
-- check if pointing at a node
|
|
||||||
if not pt then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
if pt.type ~= "node" then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local under = minetest.get_node(pt.under)
|
|
||||||
local above = minetest.get_node(pt.above)
|
|
||||||
|
|
||||||
if minetest.is_protected(pt.above, placer:get_player_name()) then
|
|
||||||
minetest.record_protection_violation(pt.above, placer:get_player_name())
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return if any of the nodes is not registered
|
|
||||||
if not minetest.registered_nodes[under.name] or not minetest.registered_nodes[above.name] then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
-- check if you can replace the node above the pointed node
|
|
||||||
if not minetest.registered_nodes[above.name].buildable_to then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local new_param2
|
|
||||||
-- check if pointing at an existing stalactite
|
|
||||||
if minetest.get_item_group(under.name, "subterrane_stal_align") ~= 0 then
|
|
||||||
new_param2 = under.param2
|
|
||||||
else
|
|
||||||
new_param2 = math.random(0,3)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- add the node and remove 1 item from the itemstack
|
|
||||||
minetest.add_node(pt.above, {name = itemname, param2 = new_param2})
|
|
||||||
if not minetest.setting_getbool("creative_mode") then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
local stal_box_1 = {{-0.0625+x_disp, -0.5, -0.0625+z_disp, 0.0625+x_disp, 0.5, 0.0625+z_disp}}
|
|
||||||
local stal_box_2 = {{-0.125+x_disp, -0.5, -0.125+z_disp, 0.125+x_disp, 0.5, 0.125+z_disp}}
|
|
||||||
local stal_box_3 = {{-0.25+x_disp, -0.5, -0.25+z_disp, 0.25+x_disp, 0.5, 0.25+z_disp}}
|
|
||||||
local stal_box_4 = {{-0.375+x_disp, -0.5, -0.375+z_disp, 0.375+x_disp, 0.5, 0.375+z_disp}}
|
|
||||||
|
|
||||||
local simple_copy = function(t)
|
|
||||||
local r = {}
|
|
||||||
for k, v in pairs(t) do
|
|
||||||
r[k] = v
|
|
||||||
end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
subterrane.register_stalagmite_nodes = function(base_name, base_node_def, drop_base_name)
|
|
||||||
base_node_def.groups = base_node_def.groups or {}
|
|
||||||
base_node_def.groups.subterrane_stal_align = 1
|
|
||||||
base_node_def.groups.flow_through = 1
|
|
||||||
base_node_def.drawtype = "nodebox"
|
|
||||||
base_node_def.paramtype = "light"
|
|
||||||
base_node_def.paramtype2 = "facedir"
|
|
||||||
base_node_def.is_ground_content = true
|
|
||||||
base_node_def.node_box = {type = "fixed"}
|
|
||||||
|
|
||||||
local def1 = simple_copy(base_node_def)
|
|
||||||
def1.groups.fall_damage_add_percent = 100
|
|
||||||
def1.node_box.fixed = stal_box_1
|
|
||||||
def1.on_place = function(itemstack, placer, pointed_thing)
|
|
||||||
return stal_on_place(itemstack, placer, pointed_thing, base_name.."_1")
|
|
||||||
end
|
|
||||||
if drop_base_name then
|
|
||||||
def1.drop = drop_base_name.."_1"
|
|
||||||
end
|
|
||||||
minetest.register_node(base_name.."_1", def1)
|
|
||||||
|
|
||||||
local def2 = simple_copy(base_node_def)
|
|
||||||
def2.groups.fall_damage_add_percent = 50
|
|
||||||
def2.node_box.fixed = stal_box_2
|
|
||||||
def2.on_place = function(itemstack, placer, pointed_thing)
|
|
||||||
return stal_on_place(itemstack, placer, pointed_thing, base_name.."_2")
|
|
||||||
end
|
|
||||||
if drop_base_name then
|
|
||||||
def2.drop = drop_base_name.."_2"
|
|
||||||
end
|
|
||||||
minetest.register_node(base_name.."_2", def2)
|
|
||||||
|
|
||||||
local def3 = simple_copy(base_node_def)
|
|
||||||
def3.node_box.fixed = stal_box_3
|
|
||||||
def3.on_place = function(itemstack, placer, pointed_thing)
|
|
||||||
return stal_on_place(itemstack, placer, pointed_thing, base_name.."_3")
|
|
||||||
end
|
|
||||||
if drop_base_name then
|
|
||||||
def3.drop = drop_base_name.."_3"
|
|
||||||
end
|
|
||||||
minetest.register_node(base_name.."_3", def3)
|
|
||||||
|
|
||||||
local def4 = simple_copy(base_node_def)
|
|
||||||
def4.node_box.fixed = stal_box_4
|
|
||||||
def4.on_place = function(itemstack, placer, pointed_thing)
|
|
||||||
return stal_on_place(itemstack, placer, pointed_thing, base_name.."_4")
|
|
||||||
end
|
|
||||||
if drop_base_name then
|
|
||||||
def4.drop = drop_base_name.."_4"
|
|
||||||
end
|
|
||||||
minetest.register_node(base_name.."_4", def4)
|
|
||||||
|
|
||||||
return {
|
|
||||||
minetest.get_content_id(base_name.."_1"),
|
|
||||||
minetest.get_content_id(base_name.."_2"),
|
|
||||||
minetest.get_content_id(base_name.."_3"),
|
|
||||||
minetest.get_content_id(base_name.."_4"),
|
|
||||||
}
|
|
||||||
end
|
|
178
player_spawn.lua
178
player_spawn.lua
@ -1,111 +1,141 @@
|
|||||||
local spawned = false
|
local sidelen = mapgen_helper.block_size
|
||||||
|
|
||||||
|
local snap_to_minp = function(ydepth)
|
||||||
|
return ydepth - (ydepth+32) % sidelen -- put ydepth at the minp.y of mapblocks
|
||||||
|
end
|
||||||
|
|
||||||
function subterrane:register_cave_spawn(cave_layer_def, start_depth)
|
function subterrane:register_cave_spawn(cave_layer_def, start_depth)
|
||||||
local ydepth = start_depth or cave_layer_def.minimum_depth;
|
|
||||||
minetest.register_on_newplayer(function(player)
|
minetest.register_on_newplayer(function(player)
|
||||||
|
local ydepth = snap_to_minp(start_depth or cave_layer_def.y_max)
|
||||||
|
local spawned = false
|
||||||
while spawned ~= true do
|
while spawned ~= true do
|
||||||
player:setpos({x=0,y=ydepth,z=0})
|
spawned = spawnplayer(cave_layer_def, player, ydepth)
|
||||||
spawnplayer(cave_layer_def, player, ydepth)
|
ydepth = ydepth - sidelen
|
||||||
ydepth = ydepth - 80
|
if ydepth < cave_layer_def.y_min then
|
||||||
if ydepth < cave_layer_def.maximum_depth then
|
ydepth = snap_to_minp(cave_layer_def.y_max)
|
||||||
ydepth = cave_layer_def.minimum_depth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_on_respawnplayer(function(player)
|
minetest.register_on_respawnplayer(function(player)
|
||||||
|
local ydepth = snap_to_minp(start_depth or cave_layer_def.y_max)
|
||||||
|
local spawned = false
|
||||||
while spawned ~= true do
|
while spawned ~= true do
|
||||||
player:setpos({x=0,y=ydepth,z=0})
|
spawned = spawnplayer(cave_layer_def, player, ydepth)
|
||||||
spawnplayer(cave_layer_def, player, ydepth)
|
ydepth = ydepth - sidelen
|
||||||
ydepth = ydepth - 80
|
if ydepth < cave_layer_def.y_min then
|
||||||
if ydepth < cave_layer_def.maximum_depth then
|
ydepth = snap_to_minp(cave_layer_def.y_max)
|
||||||
ydepth = cave_layer_def.minimum_depth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spawn player underground
|
local outside_region = 0
|
||||||
|
local inside_ground = 1
|
||||||
|
local inside_cavern = 2
|
||||||
|
|
||||||
|
-- Spawn player underground in a giant cavern
|
||||||
function spawnplayer(cave_layer_def, player, ydepth)
|
function spawnplayer(cave_layer_def, player, ydepth)
|
||||||
|
subterrane.set_defaults(cave_layer_def)
|
||||||
|
|
||||||
local YMIN = cave_layer_def.maximum_depth
|
local YMIN = cave_layer_def.y_min
|
||||||
local YMAX = cave_layer_def.minimum_depth
|
local YMAX = cave_layer_def.y_max
|
||||||
local BLEND = math.min(cave_layer_def.boundary_blend_range or 128, (YMIN-YMAX)/2)
|
local BLEND = math.min(cave_layer_def.boundary_blend_range, (YMAX-YMIN)/2)
|
||||||
local TCAVE = cave_layer_def.cave_threshold or 0.5
|
|
||||||
|
|
||||||
-- 3D noise for cave
|
local TCAVE = cave_layer_def.cave_threshold
|
||||||
local np_cave = cave_layer_def.perlin_cave or subterrane.default_perlin_cave
|
|
||||||
-- 3D noise for wave
|
|
||||||
local np_wave = cave_layer_def.perlin_wave or subterrane.default_perlin_wave
|
|
||||||
|
|
||||||
local yblmin = YMIN + BLEND * 1.5
|
local np_cave = cave_layer_def.perlin_cave
|
||||||
local yblmax = YMAX - BLEND * 1.5
|
local np_wave = cave_layer_def.perlin_wave
|
||||||
|
|
||||||
local xsp
|
local y_blend_min = YMIN + BLEND * 1.5
|
||||||
local ysp
|
local y_blend_max = YMAX - BLEND * 1.5
|
||||||
local zsp
|
|
||||||
|
local column_def = cave_layer_def.columns
|
||||||
|
local double_frequency = cave_layer_def.double_frequency
|
||||||
|
|
||||||
|
local options = {}
|
||||||
|
|
||||||
for chunk = 1, 64 do
|
for chunk = 1, 64 do
|
||||||
print ("[subterrane] searching for spawn "..chunk)
|
minetest.log("info", "[subterrane] searching for spawn "..chunk)
|
||||||
local x0 = 80 * math.random(-32, 32) - 32
|
|
||||||
local z0 = 80 * math.random(-32, 32) - 32
|
|
||||||
local y0 = ydepth-32
|
|
||||||
local x1 = x0 + 79
|
|
||||||
local z1 = z0 + 79
|
|
||||||
local y1 = ydepth+47
|
|
||||||
|
|
||||||
local sidelen = 80
|
local minp = {x = sidelen * math.random(-32, 32) - 32, z = sidelen * math.random(-32, 32) - 32, y = ydepth}
|
||||||
local chulens = {x=sidelen, y=sidelen, z=sidelen}
|
local maxp = {x = minp.x + sidelen - 1, z = minp.z + sidelen - 1, y = ydepth + sidelen - 1}
|
||||||
local minposxyz = {x=x0, y=y0, z=z0}
|
|
||||||
local minposxz = {x=x0, y=z0}
|
|
||||||
|
|
||||||
local nvals_cave = minetest.get_perlin_map(np_cave, chulens):get3dMap_flat(minposxyz) --cave noise for structure
|
local nvals_cave, cave_area = mapgen_helper.perlin3d("subterrane:cave", minp, maxp, np_cave) --cave noise for structure
|
||||||
local nvals_wave = minetest.get_perlin_map(np_wave, chulens):get3dMap_flat(minposxyz) --wavy structure of cavern ceilings and floors
|
local nvals_wave = mapgen_helper.perlin3d("subterrane:wave", minp, maxp, np_wave) --wavy structure of cavern ceilings and floors
|
||||||
|
|
||||||
local nixz = 1
|
-- pre-average everything
|
||||||
local nixyz = 1
|
for vi, value in ipairs(nvals_cave) do
|
||||||
for z = z0, z1 do
|
nvals_cave[vi] = (value + nvals_wave[vi])/2
|
||||||
for y = y0, y1 do
|
end
|
||||||
for x = x0, x1 do
|
local column_points = nil
|
||||||
local n_abscave = math.abs(nvals_cave[nixyz])
|
local column_weight = nil
|
||||||
local n_abswave = math.abs(nvals_wave[nixyz])
|
|
||||||
|
|
||||||
local tcave --declare variable
|
local previous_y = minp.y
|
||||||
--determine the overal cave threshold
|
local previous_node_state = outside_region
|
||||||
if y < yblmin then
|
|
||||||
tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
|
for vi, x, y, z in cave_area:iterp_yxz(vector.add(minp, 2), vector.subtract(maxp, 2)) do
|
||||||
elseif y > yblmax then
|
|
||||||
tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
|
if y < previous_y then
|
||||||
|
previous_node_state = outside_region
|
||||||
|
end
|
||||||
|
previous_y = y
|
||||||
|
|
||||||
|
local cave_local_threshold
|
||||||
|
if y < y_blend_min then
|
||||||
|
cave_local_threshold = TCAVE + ((y_blend_min - y) / BLEND) ^ 2
|
||||||
|
elseif y > y_blend_max then
|
||||||
|
cave_local_threshold = TCAVE + ((y - y_blend_max) / BLEND) ^ 2
|
||||||
else
|
else
|
||||||
tcave = TCAVE
|
cave_local_threshold = TCAVE
|
||||||
end
|
end
|
||||||
|
|
||||||
--if y >= 1 and density > -0.01 and density < 0 then
|
local cave_value = nvals_cave[vi]
|
||||||
if (nvals_cave[nixyz] + nvals_wave[nixyz])/2 > tcave + 0.005 and (nvals_cave[nixyz] + nvals_wave[nixyz])/2 < tcave + 0.015 then --if node falls within cave threshold
|
if double_frequency then
|
||||||
ysp = y + 1
|
cave_value = math.abs(cave_value)
|
||||||
xsp = x
|
|
||||||
zsp = z
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
nixz = nixz + 1
|
|
||||||
nixyz = nixyz + 1
|
-- inside a giant cavern
|
||||||
|
if cave_value > cave_local_threshold then
|
||||||
|
local column_value = 0
|
||||||
|
local inside_column = false
|
||||||
|
if column_def then
|
||||||
|
if column_points == nil then
|
||||||
|
column_points = subterrane.get_column_points(minp, maxp, column_def)
|
||||||
|
column_weight = column_def.weight
|
||||||
end
|
end
|
||||||
if ysp then
|
column_value = subterrane.get_column_value({x=x, y=y, z=z}, column_points)
|
||||||
break
|
|
||||||
end
|
end
|
||||||
nixz = nixz - 80
|
inside_column = column_value > 0 and cave_value - column_value * column_weight < cave_local_threshold
|
||||||
|
if previous_node_state == inside_ground and not inside_column then
|
||||||
|
-- we just entered the cavern from below. Do a quick check for head space.
|
||||||
|
local val_above = nvals_cave[vi+cave_area.ystride]
|
||||||
|
if double_frequency then
|
||||||
|
val_above = math.abs(val_above)
|
||||||
end
|
end
|
||||||
if ysp then
|
if val_above > cave_local_threshold then
|
||||||
break
|
table.insert(options, {x=x, y=y+1, z=z})
|
||||||
end
|
|
||||||
nixz = nixz + 80
|
|
||||||
end
|
|
||||||
if ysp then
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print ("[subterrane] spawn player ("..xsp.." "..ysp.." "..zsp..")")
|
if not inside_column then
|
||||||
player:setpos({x=xsp, y=ysp, z=zsp})
|
previous_node_state = inside_cavern
|
||||||
spawned = true
|
else
|
||||||
|
previous_node_state = inside_ground
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
previous_node_state = inside_ground
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if table.getn(options) > 20 then -- minimum 20 to ensure the player is put in a place with a modicum of size
|
||||||
|
local choice = math.random(1, table.getn(options))
|
||||||
|
local spawnpoint = options[ choice ]
|
||||||
|
minetest.log("action", "[subterrane] spawning player " .. minetest.pos_to_string(spawnpoint))
|
||||||
|
player:setpos(spawnpoint)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
@ -1 +1,3 @@
|
|||||||
subterrane_enable_legacy_dripstone (Adds old dripstone node definitions) bool true
|
subterrane_enable_legacy_dripstone (Adds old dripstone node definitions) bool true
|
||||||
|
#Use this mode with singlenode mapgen to produce a large-scale map of a particular world's caverns
|
||||||
|
subterrane_enable_singlenode_mapping_mode (Fills cavern volume with stone and warrens with desertstone) bool false
|
Loading…
x
Reference in New Issue
Block a user