Volcanoes (#2)

* Initial stab at volcano mapgen

* Make caldera conical, randomized size a bit

* remove grass from underwater soil

* add snow, fix some errors in mountain shape calculations

* improved soil and snow generation, more efficient mapgen

* add some configuration for volcanos, and a find volcanoes command for debugging

* enhance findvolcano, chunk sizes limited to multiples of mapblocks

* more volcano options, also add soil cooking

* update readme and screenshot

* cosmetic tweaks, slight change to default slope range

* fixed the mapgen v7 glitch via a horrible hack :(

* fix the need-to-make-changes test

* use biome-defined material to decorate slopes

* apparently set_lighting and calc_lighting are only supposed to be called on mapgen voxelmanipulators

* add magma chambers at the base of the volcano pipe

* fixes to large-scale grid calculations

* remove a line I added for debugging purposes

* bringing back configurable limits now that the bug's been fixed for six months

* generalizing the heating/cooling ABM

* actually turn the cooling material into the targeted material.

* Revert "actually turn the cooling material into the targeted material."

This reverts commit 508e10acc7c3871cc56fb8347e7931e5595b5f54.

* Fix cooling target node type for real

* very strange, abms don't seem to be updating node groups somehow.

* Workaround for Minetest issue #7864

* using minetest.after proved unreliable, trying different approach

* remove the lava cutoff setting, no longer needed now that veins are fixed

* improve findvolcano command

* using the same solution for v7 mapgen bug as for the lava crash

This is still not 100% reliable, but it's still better than the post-gen patching.

* fix randomization of lava depth

* remove debugging

* extinct volcano lava depth wasn't working right either
This commit is contained in:
FaceDeer 2018-12-26 02:06:38 -07:00 committed by GitHub
parent ffeeb026dc
commit 665dbe2636
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 787 additions and 183 deletions

@ -1,10 +1,37 @@
A relatively simple mapgen mod. It adds magma conduits that thread through the ground like a second vertically-oriented cave system.
This mapgen mod modifies how lava is distributed and how lava interacts with neighboring nodes in a variety of ways. Its various features can be enabled or disabled independently via configuration settings.
It also has the following optional features, configurable via settings:
This mod makes magma more sparsely distributed across the map, but where it is found it will be available in massive quantities. Using this mod in conjunction with the dynamic_liquid mod can result in !!fun!! lava flows when caves intersect with magma conduits.
* Removes default mapgen lava (it leaves the empty caves behind)
* Removes surface lava that would otherwise spill out into the surrounding landscape, leaving stable lava pools dotted here and there.
* Adds an obsidian lining to magma conduits, providing a warning for unsuspecting miners
* Adds "glowing rock" that forms from stone and obsidian adjacent to magma, providing a brief warning to miners that they're about to hit trouble.
## Lava interaction with adjacent nodes
This mod makes magma more sparsely distributed across the map, but where it is found it will be available in massive quantities. Using this mod in conjunction with the dynamic_liquid mod can result in !!fun!! lava flows when caves intersect with magma conduits.
This mod provides an ABM that causes some types of nodes adjacent to lava to slowly convert into glowing "hot" forms, and then back to cool forms again if the lava goes away. Stone is converted into hot cobble and obsidian converts to a glowing red form.
Other mods can hook into this ABM by adding the following to node definitions:
```
group:lava_heatable
_magma_conduits_heats_to = hot node name
```
and
```
group:lava_heated
_magma_conduits_cools_to = cool node name
```
so for example default:stone is added to the lava_heatable group and given _magma_conduits_heats_to = "magma_conduits:hot_cobble" and magma_conduits:hot_cobble is in the lava_heated group and is given _magma_conduits_cools_to = "default:cobble".
Also included is an ABM that causes soil adjacent to lava to be cooked into barren sand.
## Magma veins
Magma veins are magma-filled conduits that thread through the ground somewhat like a cave system. The veins have a bias toward vertical orientation but can curve and twist in any direction.
Magma veins can be optionally be lined with a layer of obsidian. The density of vein generation is configurable.
## Volcanoes
Volcanoes are large cone-shaped mountains formed from magma that rises through a central vertical pipe. This mod can scatter randomly sized and shaped volcanoes across the map, with configurable spacing and probability. Volcanoes come in "extinct", "dormant" and "active" forms, and tall volcanoes can have a mantle of snow clinging to their peaks.
If a player has the "findvolcano" priviledge he can use the "findvolcano" console command to locate nearby volcanoes.

@ -23,15 +23,32 @@ local function setting(stype, name, default, description)
end
end
setting("int", "spread", 400, "Approximate spacing between magma conduits")
setting("bool", "remove_default_lava", true, "Removes default mapgen lava")
setting("bool", "ameliorate_floods", true, "Ameliorate lava floods on the surface")
setting("bool", "obsidian_lining", true, "Add an obsidian lining to magma conduits")
setting("bool", "glowing_rock", true, "Cause rock adjacent to lava to convert into glowing form")
setting("int", "remove_lava_above", 512, "Remove lava above this y level")
setting("bool", "cook_soil", true, "Cause soil and carbon-containing ores to be cooked into other forms by lava")
setting("bool", "magma_veins", true, "Enable magma veins")
setting("int", "spread", 400, "Approximate spacing between magma conduits")
setting("bool", "obsidian_lining", true, "Add an obsidian lining to magma conduits")
setting("bool", "ameliorate_floods", true, "Ameliorate lava floods on the surface")
-- Removing this setting on account of issue https://github.com/minetest/minetest/issues/7364
--setting("int", "upper_limit", 512, "Upper extent of magma conduits")
--setting("int", "lower_limit", -31000, "Lower extent of magma conduits")
--from settingtypes.txt:
--magma_conduits_upper_limit (Upper extent of magma conduits) int 512
--magma_conduits_lower_limit (Lower extent of magma conduits) int -31000
-- Fixed with commit https://github.com/minetest/minetest/commit/5c1edc58ab2abe8bc1f1bbcbb2f30a5899586968
setting("int", "upper_limit", -256, "Upper extent of magma conduits")
setting("int", "lower_limit", -31000, "Lower extent of magma conduits")
setting("bool", "volcanoes", true, "Enable volcanoes")
setting("int", "volcano_min_height", 20, "Minimum volcano peak")
setting("int", "volcano_max_height", 200, "Maximum volcano peak")
setting("float", "volcano_min_slope", 0.75, "Minimum volcano slope")
setting("float", "volcano_max_slope", 1.5, "Maximum volcano slope")
setting("int", "volcano_region_mapblocks", 16, "Map blocks per chunk")
setting("int", "volcano_min_depth", -3000, "Lowest point the magma pipe goes to")
setting("bool", "volcano_magma_chambers", true, "Enable magma chambers at the base of the magma pipe")
setting("float", "volcano_magma_chamber_radius_multiplier", 0.5, "Magma chamber radius multiplier")
setting("float", "volcano_probability_active", 0.3, "Probability that there's an active volcano in each region")
setting("float", "volcano_probability_dormant", 0.15, "Probability that there's a dormant volcano in each region")
setting("float", "volcano_probability_extinct", 0.15, "Probability that there's an extinct volcano in each region")

11
cook_soil.lua Normal file

@ -0,0 +1,11 @@
minetest.register_abm{
label = "soil burning",
nodenames = {"group:soil"},
neighbors = {"default:lava_source", "default:lava_flowing"},
interval = 10,
chance = 5,
action = function(pos)
minetest.set_node(pos, {name = "default:sand"})
minetest.check_for_falling(pos)
end,
}

@ -1 +1 @@
Removes default mapgen lava and adds widely-spaced vertical lava "veins".
Removes default mapgen lava and adds widely-spaced volcanoes and lava "veins".

@ -9,7 +9,8 @@ minetest.register_node("magma_conduits:hot_cobble", {
_doc_items_usagehelp = S("When normal stone is heated by lava it is converted into this. Beware when digging here!"),
tiles = {"magma_conduits_hot_cobble.png"},
is_ground_content = false,
groups = {cracky = 3, stone = 2, hot=1},
groups = {cracky = 3, stone = 2, lava_heated=1},
_magma_conduits_cools_to = "default:cobble",
sounds = default.node_sound_stone_defaults(),
light_source = 6,
drop = "default:cobble",
@ -22,53 +23,85 @@ minetest.register_node("magma_conduits:glow_obsidian", {
tiles = {"magma_conduits_glow_obsidian.png"},
is_ground_content = true,
sounds = default.node_sound_stone_defaults(),
groups = {cracky=1, hot=1, level=2},
groups = {cracky=1, lava_heated=1, level=2},
_magma_conduits_cools_to = "default:obsidian",
light_source = 6,
drop = "default:obsidian",
})
local simple_copy
simple_copy = function(t)
local r = {}
for k, v in pairs(t) do
if type(v) == "table" then
r[k] = simple_copy(v)
else
r[k] = v
end
end
return r
end
-- can't use minetest.override_item to change group memberships here due to issue https://github.com/minetest/minetest/issues/5518
local make_heatable = function(nodename, heats_to)
local original_def = minetest.registered_nodes[nodename]
if original_def ~= nil then
local def = simple_copy(original_def)
def.groups.lava_heatable = 1
def._magma_conduits_heats_to = heats_to
minetest.register_node(":"..nodename, def)
end
end
make_heatable("default:obsidian", "magma_conduits:glow_obsidian")
make_heatable("default:stone", "magma_conduits:hot_cobble")
make_heatable("default:cobble", "magma_conduits:hot_cobble")
make_heatable("default:mossycobble", "magma_conduits:hot_cobble")
make_heatable("default:stone_with_coal", "magma_conduits:hot_cobble")
make_heatable("default:stone_with_diamond", "magma_conduits:hot_cobble")
make_heatable("default:permafrost", "default:dirt")
make_heatable("default:permafrost_with_stones", "default:dirt")
make_heatable("default:permafrost_with_moss", "default:dirt")
minetest.register_abm{
label = "stone heating",
nodenames = {"default:stone", "default:cobble", "default:mossycobble"},
label = "magma_conduits lava heating neighbors",
nodenames = {"group:lava_heatable"},
neighbors = {"default:lava_source", "default:lava_flowing"},
interval = 10,
chance = 5,
action = function(pos)
minetest.set_node(pos, {name = "magma_conduits:hot_cobble"})
end,
}
minetest.register_abm{
label = "obsidian heating",
nodenames = {"default:obsidian"},
neighbors = {"default:lava_source", "default:lava_flowing"},
interval = 10,
chance = 5,
action = function(pos)
minetest.set_node(pos, {name = "magma_conduits:glow_obsidian"})
end,
}
minetest.register_abm{
label = "stone cooling",
nodenames = {"magma_conduits:hot_cobble"},
interval = 100,
chance = 10,
action = function(pos)
if not minetest.find_node_near(pos, 2, {"default:lava_source", "default:lava_flowing"}, false) then
minetest.set_node(pos, {name = "default:cobble"})
local name = minetest.get_node(pos).name
local def = minetest.registered_nodes[name]
if def.groups.lava_heatable then
local target = def._magma_conduits_heats_to
if target then
minetest.set_node(pos, {name = target})
else
minetest.log("error", name .. " is in group lava_heatable but doesn't have a _magma_conduits_heats_to property defined in its definition")
end
end
end,
}
minetest.register_abm{
label = "obsidian cooling",
nodenames = {"magma_conduits:glow_obsidian"},
label = "magma_conduits cooling stuff heated by lava",
nodenames = {"group:lava_heated"},
interval = 100,
chance = 10,
action = function(pos)
if not minetest.find_node_near(pos, 2, {"default:lava_source", "default:lava_flowing"}, false) then
minetest.set_node(pos, {name = "default:obsidian"})
local name = minetest.get_node(pos).name
local def = minetest.registered_nodes[name]
local target = def._magma_conduits_cools_to
if target then
minetest.set_node(pos, {name = target})
else
minetest.log("error", name .. " is in group lava_heated but doesn't have a _magma_conduits_cools_to property defined in its definition")
end
end
end,
}

140
init.lua

@ -6,138 +6,16 @@ dofile(modpath.."/voxelarea_iterator.lua")
dofile(modpath.."/hot_rock.lua")
if magma_conduits.config.remove_default_lava then
minetest.register_alias_force("mapgen_lava_source", "air") -- veins of lava are far more realistic
minetest.register_alias_force("mapgen_lava_source", "air")
end
-- Hard-coding on account of issue https://github.com/minetest/minetest/issues/7364
local height_min = -31000 -- magma_conduits.config.lower_limit
local height_max = 31000 --magma_conduits.config.upper_limit
minetest.register_ore({
ore_type = "vein",
ore = "default:lava_source",
wherein = {
"default:stone",
"default:desert_stone",
"default:sandstone",
"default:stone_with_coal",
"default:stone_with_iron",
"default:stone_with_copper",
"default:stone_with_tin",
"default:stone_with_gold",
"default:stone_with_diamond",
"default:dirt",
"default:dirt_with_grass",
"default:dirt_with_dry_grass",
"default:dirt_with_snow",
"default:dirt_with_rainforest_litter",
"default:dirt_with_coniferous_litter",
"default:sand",
"default:desert_sand",
"default:silver_sand",
"default:gravel",
},
column_height_min = 2,
column_height_max = 6,
y_min = height_min,
y_max = height_max,
noise_threshold = 0.9,
noise_params = {
offset = 0,
scale = 3,
spread = {x=magma_conduits.config.spread, y=magma_conduits.config.spread*2, z=magma_conduits.config.spread},
seed = 25391,
octaves = 4,
persist = 0.5,
flags = "eased",
},
random_factor = 0,
})
-------------------------------------------------------------------------------------------------
local water_level = tonumber(minetest.get_mapgen_setting("water_level"))
local lava_y_cutoff = magma_conduits.config.remove_lava_above
-- if the y cutoff is at or below water level, ameliorate_floods is pointless.
local ameliorate_floods = magma_conduits.config.ameliorate_floods and lava_y_cutoff > water_level
local obsidian_lining = magma_conduits.config.obsidian_lining
local c_air = minetest.get_content_id("air")
local c_lava = minetest.get_content_id("default:lava_source")
local c_stone = minetest.get_content_id("default:stone")
local c_obsidian = minetest.get_content_id("default:obsidian")
local is_adjacent_to_air = function(area, data, x, y, z)
return (data[area:index(x+1, y, z)] == c_air
or data[area:index(x-1, y, z)] == c_air
or data[area:index(x, y, z+1)] == c_air
or data[area:index(x, y, z-1)] == c_air
or data[area:index(x, y-1, z)] == c_air)
if magma_conduits.config.magma_veins then
dofile(modpath.."/magma_veins.lua")
end
if magma_conduits.config.volcanoes then
dofile(modpath.."/volcanoes.lua")
end
if magma_conduits.config.cook_soil then
dofile(modpath.."/cook_soil.lua")
end
local remove_unsupported_lava
remove_unsupported_lava = function(area, data, vi, x, y, z)
--if below water level, abort. Caverns are on their own.
if y < water_level or y > lava_y_cutoff or not area:contains(x, y, z) then return end
if data[vi] == c_lava then
if is_adjacent_to_air(area, data, x, y, z) then
data[vi] = c_air
for pi, x2, y2, z2 in area:iter_xyz(x-1, y, z-1, x+1, y+1, z+1) do
if pi ~= vi and area:containsi(pi) then
remove_unsupported_lava(area, data, pi, x2, y2, z2)
end
end
end
end
end
-- If we're adding glow versions of rock, then place glow obsidian directly.
local obsidianize_id
if magma_conduits.config.glowing_rock then
obsidianize_id = minetest.get_content_id("magma_conduits:glow_obsidian")
else
obsidianize_id = c_obsidian
end
local obsidianize = function(area, data, vi, x, y, z, minp, maxp)
if data[vi] == c_lava then
for pi in area:iter(math.max(x-1, minp.x), math.max(y-1, minp.y), math.max(z-1, minp.z),
math.min(x+1, maxp.x), math.min(y+1, maxp.y), math.min(z+1, maxp.z)) do
if data[pi] == c_stone then
data[pi] = obsidianize_id
end
end
end
end
local data = {}
minetest.register_on_generated(function(minp, maxp, seed)
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
vm:get_data(data)
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
if y > lava_y_cutoff and data[vi] == c_lava then
data[vi] = c_air
else
if obsidian_lining then
obsidianize(area, data, vi, x, y, z, minp, maxp)
end
if ameliorate_floods then
remove_unsupported_lava(area, data, vi, x, y, z)
end
end
end
--send data back to voxelmanip
vm:set_data(data)
--calc lighting
vm:set_lighting({day = 0, night = 0})
vm:calc_lighting()
vm:update_liquids()
--write it to world
vm:write_to_map()
end)

126
magma_veins.lua Normal file

@ -0,0 +1,126 @@
-- Hard-coding on account of issue https://github.com/minetest/minetest/issues/7364
local height_min = magma_conduits.config.lower_limit
local height_max = magma_conduits.config.upper_limit
minetest.register_ore({
ore_type = "vein",
ore = "default:lava_source",
wherein = {
"default:stone",
"default:desert_stone",
"default:sandstone",
"default:stone_with_coal",
"default:stone_with_iron",
"default:stone_with_copper",
"default:stone_with_tin",
"default:stone_with_gold",
"default:stone_with_diamond",
"default:dirt",
"default:dirt_with_grass",
"default:dirt_with_dry_grass",
"default:dirt_with_snow",
"default:dirt_with_rainforest_litter",
"default:dirt_with_coniferous_litter",
"default:sand",
"default:desert_sand",
"default:silver_sand",
"default:gravel",
},
column_height_min = 2,
column_height_max = 6,
y_min = height_min,
y_max = height_max,
noise_threshold = 0.9,
noise_params = {
offset = 0,
scale = 3,
spread = {x=magma_conduits.config.spread, y=magma_conduits.config.spread*2, z=magma_conduits.config.spread},
seed = 25391,
octaves = 4,
persist = 0.5,
flags = "eased",
},
random_factor = 0,
})
-------------------------------------------------------------------------------------------------
local water_level = tonumber(minetest.get_mapgen_setting("water_level"))
local ameliorate_floods = magma_conduits.config.ameliorate_floods
local obsidian_lining = magma_conduits.config.obsidian_lining
local c_air = minetest.get_content_id("air")
local c_lava = minetest.get_content_id("default:lava_source")
local c_stone = minetest.get_content_id("default:stone")
local c_obsidian = minetest.get_content_id("default:obsidian")
local is_adjacent_to_air = function(area, data, x, y, z)
return (data[area:index(x+1, y, z)] == c_air
or data[area:index(x-1, y, z)] == c_air
or data[area:index(x, y, z+1)] == c_air
or data[area:index(x, y, z-1)] == c_air
or data[area:index(x, y-1, z)] == c_air)
end
local remove_unsupported_lava
remove_unsupported_lava = function(area, data, vi, x, y, z)
--if below water level, abort. Caverns are on their own.
if y < water_level or not area:contains(x, y, z) then return end
if data[vi] == c_lava then
if is_adjacent_to_air(area, data, x, y, z) then
data[vi] = c_air
for pi, x2, y2, z2 in area:iter_xyz(x-1, y, z-1, x+1, y+1, z+1) do
if pi ~= vi and area:containsi(pi) then
remove_unsupported_lava(area, data, pi, x2, y2, z2)
end
end
end
end
end
-- If we're adding glow versions of rock, then place glow obsidian directly.
local obsidianize_id
if magma_conduits.config.glowing_rock then
obsidianize_id = minetest.get_content_id("magma_conduits:glow_obsidian")
else
obsidianize_id = c_obsidian
end
local obsidianize = function(area, data, vi, x, y, z, minp, maxp)
if data[vi] == c_lava then
for pi in area:iter(math.max(x-1, minp.x), math.max(y-1, minp.y), math.max(z-1, minp.z),
math.min(x+1, maxp.x), math.min(y+1, maxp.y), math.min(z+1, maxp.z)) do
if data[pi] == c_stone then
data[pi] = obsidianize_id
end
end
end
end
local data = {}
minetest.register_on_generated(function(minp, maxp, seed)
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
vm:get_data(data)
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
if obsidian_lining then
obsidianize(area, data, vi, x, y, z, minp, maxp)
end
if ameliorate_floods then
remove_unsupported_lava(area, data, vi, x, y, z)
end
end
--send data back to voxelmanip
vm:set_data(data)
--calc lighting
vm:set_lighting({day = 0, night = 0})
vm:calc_lighting()
vm:update_liquids()
--write it to world
vm:write_to_map()
end)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 157 KiB

@ -1,6 +1,43 @@
magma_conduits_spread (Approximate spacing between magma conduits) int 400
magma_conduits_ameliorate_floods (Ameliorate lava floods on the surface) bool true
magma_conduits_obsidian_lining (Adds an obsidian lining to magma conduits) bool true
magma_conduits_remove_default_lava (Remove default mapgen lava) bool true
magma_conduits_glowing_rock (Cause rock adjacent to lava to convert into "glowing" form) bool true
magma_conduits_remove_lava_above (Removes any lava above this y level) int 512
magma_conduits_cook_soil (Cause soil and carbonaceous ores adjacent to lava to cook) bool true
magma_conduits_remove_default_lava (Remove default mapgen lava) bool true
[Magma Veins]
#If this is false none of the other settings in this section apply
magma_conduits_magma_veins (Enable magma veins) bool true
magma_conduits_spread (Approximate spacing between magma conduits) int 400
magma_conduits_obsidian_lining (Add an obsidian lining to magma conduits) bool true
#Removes magma near the surface that could spill out of an open magma vein onto
#surrounding terrain
magma_conduits_ameliorate_floods (Ameliorate lava floods on the surface) bool true
# Note: Setting limits near "0" in versions of Minetest before June 2018
# commit 5c1edc5 may cause a crash due to a bug in vein ore generation
magma_conduits_upper_limit (Upper extent of magma conduits) int -256
magma_conduits_lower_limit (Lower extent of magma conduits) int -31000
[Volcanoes]
#If this is false none of the other settings in this section apply
magma_conduits_volcanoes (Enable volcanoes) bool true
magma_conduits_volcano_min_height (Minimum volcano peak elevation) int 20
magma_conduits_volcano_max_height (Maximum volcano peak elevation) int 200
#smaller slopes are steeper. 0.5 is probably the lowest this should go,
#things get unrealistic around there
magma_conduits_volcano_min_slope (Minimum volcano slope) float 0.75
#above 1.5 and the mountain becomes more of a shield volcano,
#taking up a lot of map area.
magma_conduits_volcano_max_slope (Maximum volcano slope) float 1.5
#The size of the region within which each volcano is randomly placed
#measured in mapblocks, which is usually 80 nodes on a side.
#Eg, the default of 16 means that one volcano is placed in each
#1280x1280 node region.
magma_conduits_volcano_region_mapblocks (Region mapblocks) int 16
magma_conduits_volcano_min_depth (Lowest depth magma pipes extend to) int -3000
magma_conduits_volcano_magma_chambers (Enables magma chambers at base of pipes) bool true
#With a multiplier of 1, magma chambers are the same size as the mountain above them.
magma_conduits_volcano_magma_chamber_radius_multiplier (Magma chamber radius multiplier) float 0.5
magma_conduits_volcano_probability_active (Probability of active volcano in region) float 0.3
magma_conduits_volcano_probability_dormant (Probability of dormant volcano in region) float 0.15
magma_conduits_volcano_probability_extinct (Probability of extinct volcano in region) float 0.15

76
volcano_lava.lua Normal file

@ -0,0 +1,76 @@
-- These nodes are only present to work around https://github.com/minetest/minetest/issues/7864
-- somehow, placing lava that's a light source is sometimes killing the game.
-- This causes the mod to place non-glowing lava on mapgen that is immediately replaced with
-- the regular stuff as soon as the chunk is loaded.
-- Once that issue is resolved, this should be got rid of.
local simple_copy
simple_copy = function(t)
local r = {}
for k, v in pairs(t) do
if type(v) == "table" then
r[k] = simple_copy(v)
else
r[k] = v
end
end
return r
end
local source_def = simple_copy(minetest.registered_nodes["default:lava_source"])
source_def.light_source = nil
source_def.liquid_alternative_flowing = "magma_conduits:lava_flowing"
source_def.liquid_alternative_source = "magma_conduits:lava_source"
source_def.groups.not_in_creative_inventory = 1
minetest.register_node("magma_conduits:lava_source", source_def)
local flowing_def = simple_copy(minetest.registered_nodes["default:lava_flowing"])
flowing_def.light_source = nil
flowing_def.liquid_alternative_flowing = "magma_conduits:lava_flowing"
flowing_def.liquid_alternative_source = "magma_conduits:lava_source"
minetest.register_node("magma_conduits:lava_flowing", flowing_def)
minetest.register_lbm({
label = "convert magma_conduits lava",
name = "magma_conduits:convert_lava",
nodenames = {"magma_conduits:lava_source"},
run_at_every_load = true,
action = function(pos, node)
minetest.set_node(pos, {name="default:lava_source"})
end,
})
minetest.register_lbm({
label = "convert magma_conduits flowing lava",
name = "magma_conduits:convert_flowing_lava",
nodenames = {"magma_conduits:lava_flowing"},
run_at_every_load = true,
action = function(pos, node)
minetest.set_node(pos, {name="default:lava_flowing"})
end,
})
-- Mapgen v7 has a glitch where it will sometimes cut slices out of default:stone placed by this mod
-- *after* mapgen is finished. The slices are taken at maxp.y or minp.y and match the
-- rivers formed by the "ridges" flag, if you disable "ridges" they don't occur.
-- Some annoying hackery is needed to patch those slices back up
-- again, and I only want to do that hackery if we're actually in mapgen v7.
-- https://github.com/minetest/minetest/issues/7878
if minetest.get_mapgen_setting("mg_name") == "v7" then
local stone_def = simple_copy(minetest.registered_nodes["default:stone"])
stone_def.is_ground_content = false
minetest.register_node("magma_conduits:stone", stone_def)
minetest.register_lbm({
label = "convert magma_conduits stone",
name = "magma_conduits:convert_stone",
nodenames = {"magma_conduits:stone"},
run_at_every_load = true,
action = function(pos, node)
minetest.set_node(pos, {name="default:stone"})
end,
})
end

399
volcanoes.lua Normal file

@ -0,0 +1,399 @@
-- NOTE: This code contains some hacks to work around a number of bugs in mapgen v7 and in Minetest's core mapgen code.
-- The issue URLs for those bugs are included in comments wherever those hacks are used, if the issues get resolved
-- then the associated hacks should be removed.
-- https://github.com/minetest/minetest/issues/7878
-- https://github.com/minetest/minetest/issues/7864
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath .. "/volcano_lava.lua") -- https://github.com/minetest/minetest/issues/7864, https://github.com/minetest/minetest/issues/7878
local depth_root = magma_conduits.config.volcano_min_depth
local depth_base = -50 -- point where the mountain root starts expanding
local depth_maxwidth = -30 -- point of maximum width
local radius_vent = 3 -- approximate minimum radius of vent - noise adds a lot to this
local radius_lining = 5 -- the difference between this and the vent radius is about how thick the layer of lining nodes is, though noise will affect it
local caldera_min = 5 -- minimum radius of caldera
local caldera_max = 20 -- maximum radius of caldera
local snow_line = 120 -- above this elevation snow is added to the dirt type
local snow_border = 15 -- transitional zone where there's dirt with snow on it
local depth_maxpeak = magma_conduits.config.volcano_max_height
local depth_minpeak = magma_conduits.config.volcano_min_height
local slope_min = magma_conduits.config.volcano_min_slope
local slope_max = magma_conduits.config.volcano_max_slope
local region_mapblocks = magma_conduits.config.volcano_region_mapblocks
local mapgen_chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
local volcano_region_size = region_mapblocks * mapgen_chunksize * 16
local magma_chambers_enabled = magma_conduits.config.volcano_magma_chambers
local chamber_radius_multiplier = magma_conduits.config.volcano_magma_chamber_radius_multiplier
local p_active = magma_conduits.config.volcano_probability_active
local p_dormant = magma_conduits.config.volcano_probability_dormant
local p_extinct = magma_conduits.config.volcano_probability_extinct
if p_active + p_dormant + p_extinct > 1.0 then
minetest.log("error", "[magma_conduits] probabilities of various volcano types adds up to more than 1")
end
local state_dormant = 1 - p_active
local state_extinct = 1 - p_active - p_dormant
local state_none = 1 - p_active - p_dormant - p_extinct
local c_air = minetest.get_content_id("air")
local c_lava = minetest.get_content_id("magma_conduits:lava_source") -- https://github.com/minetest/minetest/issues/7864
local c_water = minetest.get_content_id("default:water_source")
local c_lining = minetest.get_content_id("default:obsidian")
local c_hot_lining = minetest.get_content_id("default:obsidian")
local c_cone
if minetest.get_mapgen_setting("mg_name") == "v7" then
c_cone = minetest.get_content_id("magma_conduits:stone") -- https://github.com/minetest/minetest/issues/7878
else
c_cone = minetest.get_content_id("default:stone")
end
local c_ash = minetest.get_content_id("default:gravel")
local c_soil = minetest.get_content_id("default:dirt")
local c_soil_snow = minetest.get_content_id("default:dirt_with_snow")
local c_snow = minetest.get_content_id("default:snow")
local c_snow_block = minetest.get_content_id("default:snowblock")
local c_sand = minetest.get_content_id("default:sand")
local c_underwater_soil = c_sand
local c_plug = minetest.get_content_id("default:obsidian")
if magma_conduits.config.glowing_rock then
c_hot_lining = minetest.get_content_id("magma_conduits:glow_obsidian")
end
local water_level = tonumber(minetest.get_mapgen_setting("water_level"))
local mapgen_seed = tonumber(minetest.get_mapgen_setting("seed"))
-- derived values
local radius_cone_max = (depth_maxpeak - depth_maxwidth) * slope_max + radius_lining + 20
local depth_maxwidth_dist = depth_maxwidth-depth_base
local depth_maxpeak_dist = depth_maxpeak-depth_maxwidth
local scatter_2d = function(min_xz, gridscale, border_width)
local bordered_scale = gridscale - 2 * border_width
local point = {}
point.x = math.random() * bordered_scale + min_xz.x + border_width
point.y = 0
point.z = math.random() * bordered_scale + min_xz.z + border_width
return point
end
-- For some reason, map chunks generate with -32, -32, -32 as the "origin" minp. To make the large-scale grid align with map chunks it needs to be offset like this.
local get_corner = function(pos)
return {x = math.floor((pos.x+32) / volcano_region_size) * volcano_region_size - 32, z = math.floor((pos.z+32) / volcano_region_size) * volcano_region_size - 32}
end
local get_volcano = function(pos)
local corner_xz = get_corner(pos)
local next_seed = math.random(1, 1000000000)
math.randomseed(corner_xz.x + corner_xz.z * 2 ^ 8 + mapgen_seed)
local state = math.random()
if state < state_none then
math.randomseed(next_seed)
return nil
end
local location = scatter_2d(corner_xz, volcano_region_size, radius_cone_max)
local depth_peak = math.random(depth_minpeak, depth_maxpeak)
local depth_lava
if state < state_extinct then
depth_lava = - math.random(1, math.abs(depth_root)) -- extinct, put the lava somewhere deep.
elseif state < state_dormant then
depth_lava = depth_peak - math.random(5, 50) -- dormant
else
depth_lava = depth_peak - math.random(1, 25) -- active, put the lava near the top
end
local slope = math.random() * (slope_max - slope_min) + slope_min
local caldera = math.random() * (caldera_max - caldera_min) + caldera_min
math.randomseed(next_seed)
return {location = location, depth_peak = depth_peak, depth_lava = depth_lava, slope = slope, state = state, caldera = caldera}
end
local perlin_params = {
offset = 0,
scale = 1,
spread = {x=30, y=30, z=30},
seed = -40901,
octaves = 3,
persist = 0.67
}
local nvals_perlin_buffer = {}
local nobj_perlin = nil
local data = {}
minetest.register_on_generated(function(minp, maxp, seed)
if minp.y > depth_maxpeak or maxp.y < depth_root then
return
end
local volcano = get_volcano(minp)
if volcano == nil then
return -- no volcano in this map region
end
local depth_peak = volcano.depth_peak
local base_radius = (depth_peak - depth_maxwidth) * volcano.slope + radius_lining
local chamber_radius = (base_radius / volcano.slope) * chamber_radius_multiplier
-- early out if the volcano is too far away to matter
-- The plus 20 is because the noise being added will generally be in the 0-20 range, see the "distance" calculation below
if volcano.location.x - base_radius - 20 > maxp.x or
volcano.location.x + base_radius + 20 < minp.x or
volcano.location.z - base_radius - 20 > maxp.z or
volcano.location.z + base_radius + 20 < minp.z
then
return
end
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
vm:get_data(data)
local sidelen = mapgen_chunksize * 16 --length of a mapblock
local chunk_lengths = {x = sidelen, y = sidelen, z = sidelen} --table of chunk edges
nobj_perlin = nobj_perlin or minetest.get_perlin_map(perlin_params, chunk_lengths)
local nvals_perlin = nobj_perlin:get3dMap_flat(minp, nvals_perlin_buffer) -- switch to get_3d_map_flat for minetest v0.5
local noise_area = VoxelArea:new{MinEdge=minp, MaxEdge=maxp}
local noise_iterator = noise_area:iterp(minp, maxp)
local x_coord = volcano.location.x
local z_coord = volcano.location.z
local depth_lava = volcano.depth_lava
local caldera = volcano.caldera
local state = volcano.state
-- Create a table of biome data for use with the biomemap.
if not magma_conduits.biome_ids then
magma_conduits.biome_ids = {}
for name, desc in pairs(minetest.registered_biomes) do
local i = minetest.get_biome_id(desc.name)
local biome_data = {}
--biome_data.name = desc.name
if desc.node_dust ~= nil and desc.node_dust ~= "" then
biome_data.c_dust = minetest.get_content_id(desc.node_dust)
end
if desc.node_top ~= nil and desc.node_top ~= "" then
biome_data.c_top = minetest.get_content_id(desc.node_top)
if biome_data.c_top == c_sand then
biome_data.c_top = c_ash -- beach sand just doesn't look nice on the side of a volcano, replace it with ash
end
end
if desc.node_filler ~= nil and desc.node_filler ~= "" then
biome_data.c_filler = minetest.get_content_id(desc.node_filler)
if biome_data.c_filler == c_sand then
biome_data.c_filler = c_ash -- beach sand just doesn't look nice on the side of a volcano, replace it with ash
end
end
magma_conduits.biome_ids[i] = biome_data
end
end
local biomemap = minetest.get_mapgen_object("biomemap")
local minx = minp.x
local minz = minp.z
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
local vi3d = noise_iterator()
local distance_perturbation = (nvals_perlin[vi3d]+1)*10
local distance = vector.distance({x=x, y=y, z=z}, {x=x_coord, y=y, z=z_coord}) - distance_perturbation
local biome_data = magma_conduits.biome_ids[biomemap[(z-minz) * sidelen + (x-minx) + 1]]
-- Determine what materials to use at this y level
-------------------------------------------------------------------------------------------------
local c_top
local c_filler
local c_dust
if state < state_dormant then
if y < water_level then
c_top = c_underwater_soil
c_filler = c_underwater_soil
elseif y < snow_line and biome_data ~= nil then
c_top = biome_data.c_top
c_filler = biome_data.c_filler
c_dust = biome_data.c_dust
elseif y < snow_line + snow_border then
c_top = c_soil_snow
c_filler = c_soil
c_dust = c_snow
else
c_top = c_snow_block
c_filler = c_snow_block
c_dust = c_snow
end
else
c_top = c_ash
c_filler = c_ash
end
local pipestuff
local liningstuff
if y < depth_lava + math.random() * 1.1 then
pipestuff = c_lava
liningstuff = c_hot_lining
else
if state < state_dormant then
pipestuff = c_plug -- dormant volcano
liningstuff = c_lining
else
pipestuff = c_air -- active volcano
liningstuff = c_lining
end
end
-- Actually create the volcano
-------------------------------------------------------------------------------------------
if y < depth_base then
if magma_chambers_enabled and y < depth_root + chamber_radius then -- Magma chamber lower half
local lower_half = ((y - depth_root) / chamber_radius) * chamber_radius
if distance < lower_half + radius_vent then
data[vi] = c_lava -- Put lava in the magma chamber even for extinct volcanoes, if someone really wants to dig for it it's down there.
elseif distance < lower_half + radius_lining and data[vi] ~= c_air and data[vi] ~= c_lava then -- leave holes into caves and into existing lava
data[vi] = liningstuff
end
elseif magma_chambers_enabled and y < depth_root + chamber_radius * 2 then -- Magma chamber upper half
local upper_half = (1 - (y - depth_root - chamber_radius) / chamber_radius) * chamber_radius
if distance < upper_half + radius_vent then
data[vi] = c_lava
elseif distance < upper_half + radius_lining and data[vi] ~= c_air and data[vi] ~= c_lava then -- leave holes into caves and into existing lava
data[vi] = liningstuff
end
else -- pipe
if distance < radius_vent then
data[vi] = pipestuff
elseif distance < radius_lining and data[vi] ~= c_air and data[vi] ~= c_lava then -- leave holes into caves and into existing lava
data[vi] = liningstuff
end
end
elseif y < depth_maxwidth then -- root
if distance < radius_vent then
data[vi] = pipestuff
elseif distance < radius_lining then
data[vi] = liningstuff
elseif distance < radius_lining + ((y - depth_base)/depth_maxwidth_dist) * base_radius then
data[vi] = c_cone
end
elseif y < depth_peak + 5 then -- cone
local current_elevation = y - depth_maxwidth
local peak_elevation = depth_peak - depth_maxwidth
if current_elevation > peak_elevation - caldera and distance < current_elevation - peak_elevation + caldera then
data[vi] = c_air -- caldera
elseif distance < radius_vent then
data[vi] = pipestuff
elseif distance < radius_lining then
data[vi] = liningstuff
elseif distance < current_elevation * -volcano.slope + base_radius then
data[vi] = c_cone
if data[vi + area.ystride] == c_air and c_dust ~= nil then
data[vi + area.ystride] = c_dust
end
elseif c_top ~= nil and c_filler ~= nil and distance < current_elevation * -volcano.slope + base_radius + nvals_perlin[vi3d]*-4 then
data[vi] = c_top
if data[vi - area.ystride] == c_top then
data[vi - area.ystride] = c_filler
end
if data[vi + area.ystride] == c_air and c_dust ~= nil then
data[vi + area.ystride] = c_dust
end
end
end
end
--send data back to voxelmanip
vm:set_data(data)
--calc lighting
vm:set_lighting({day = 0, night = 0})
vm:calc_lighting()
vm:update_liquids()
--write it to world
vm:write_to_map()
end)
----------------------------------------------------------------------------------------------
-- Debugging and sightseeing commands
minetest.register_privilege("findvolcano", { description = "Allows players to use a console command to find volcanoes", give_to_singleplayer = false})
function round(val, decimal)
if (decimal) then
return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
else
return math.floor(val+0.5)
end
end
local send_volcano_state = function(pos, name)
local corner_xz = get_corner(pos)
local volcano = get_volcano(pos)
if volcano == nil then
return
end
local location = {x=math.floor(volcano.location.x), y=volcano.depth_peak, z=math.floor(volcano.location.z)}
local text = "Peak at " .. minetest.pos_to_string(location)
.. ", Slope: " .. tostring(round(volcano.slope, 2))
.. ", State: "
if volcano.state < state_extinct then
text = text .. "Extinct"
elseif volcano.state < state_dormant then
text = text .. "Dormant"
else
text = text .. "Active"
end
minetest.chat_send_player(name, text)
end
local send_nearby_states = function(pos, name)
send_volcano_state({x=pos.x-volcano_region_size, y=0, z=pos.z+volcano_region_size}, name)
send_volcano_state({x=pos.x, y=0, z=pos.z+volcano_region_size}, name)
send_volcano_state({x=pos.x+volcano_region_size, y=0, z=pos.z+volcano_region_size}, name)
send_volcano_state({x=pos.x-volcano_region_size, y=0, z=pos.z}, name)
send_volcano_state(pos, name)
send_volcano_state({x=pos.x+volcano_region_size, y=0, z=pos.z}, name)
send_volcano_state({x=pos.x-volcano_region_size, y=0, z=pos.z-volcano_region_size}, name)
send_volcano_state({x=pos.x, y=0, z=pos.z-volcano_region_size}, name)
send_volcano_state({x=pos.x+volcano_region_size, y=0, z=pos.z-volcano_region_size}, name)
end
minetest.register_chatcommand("findvolcano", {
params = "pos", -- Short parameter description
description = "find the volcanoes near the player's map region, or in the map region containing pos if provided",
func = function(name, param)
if minetest.check_player_privs(name, {findvolcano = true}) then
local pos = {}
pos.x, pos.y, pos.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
pos.x = tonumber(pos.x)
pos.y = tonumber(pos.y)
pos.z = tonumber(pos.z)
if pos.x and pos.y and pos.z then
send_nearby_states(pos, name)
return true
else
local playerobj = minetest.get_player_by_name(name)
send_nearby_states(playerobj:get_pos(), name)
return true
end
else
return false, "You need the findvolcano privilege to use this command."
end
end,
})