2018-11-16 05:21:05 +01:00
local depth_root = magma_conduits.config . volcano_min_depth
2018-11-07 08:11:41 +01:00
local depth_base = - 50 -- point where the mountain root starts expanding
local depth_maxwidth = - 30 -- point of maximum width
2018-11-08 21:13:31 +01:00
2018-11-07 08:11:41 +01:00
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
2018-11-05 00:44:04 +01:00
2018-11-07 08:11:41 +01:00
local snow_line = 120 -- above this elevation snow is added to the dirt type
2018-11-08 09:19:26 +01:00
local snow_border = 15 -- transitional zone where there's dirt with snow on it
2018-11-07 08:11:41 +01:00
2018-11-08 21:13:31 +01:00
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
2018-11-09 07:08:48 +01:00
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
2018-11-16 05:21:05 +01:00
local magma_chambers_enabled = magma_conduits.config . volcano_magma_chambers
2018-11-09 07:08:48 +01:00
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
2018-11-08 21:13:31 +01:00
2018-11-03 22:38:55 +01:00
local c_air = minetest.get_content_id ( " air " )
local c_lava = minetest.get_content_id ( " default:lava_source " )
2018-11-05 15:43:38 +01:00
local c_water = minetest.get_content_id ( " default:water_source " )
2018-11-03 22:38:55 +01:00
local c_lining = minetest.get_content_id ( " default:obsidian " )
local c_hot_lining = minetest.get_content_id ( " default:obsidian " )
local c_cone = minetest.get_content_id ( " default:stone " )
2018-11-07 08:11:41 +01:00
2018-11-03 22:38:55 +01:00
local c_ash = minetest.get_content_id ( " default:gravel " )
2018-11-08 09:19:26 +01:00
local c_soil = minetest.get_content_id ( " default:dirt " )
2018-11-07 08:11:41 +01:00
local c_soil_snow = minetest.get_content_id ( " default:dirt_with_snow " )
local c_snow = minetest.get_content_id ( " default:snow " )
2018-11-08 09:19:26 +01:00
local c_snow_block = minetest.get_content_id ( " default:snowblock " )
2018-11-07 08:11:41 +01:00
2018-11-13 10:56:40 +01:00
local c_sand = minetest.get_content_id ( " default:sand " )
local c_underwater_soil = c_sand
2018-11-03 22:38:55 +01:00
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
2018-11-05 15:43:38 +01:00
local water_level = tonumber ( minetest.get_mapgen_setting ( " water_level " ) )
2018-11-03 22:38:55 +01:00
local mapgen_seed = tonumber ( minetest.get_mapgen_setting ( " seed " ) )
2018-11-11 09:34:32 +01:00
-- Mapgen v7 has a bizzare glitch where it will sometimes cut slices out of the generated volcano
-- cone *after* mapgen is finished. The slices are taken at maxp.y or minp.y and resemble
-- the curvy form of a vein or a cave that's one node tall.
-- 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.
local mg_name = minetest.get_mapgen_setting ( " mg_name " )
2018-11-03 22:38:55 +01:00
-- derived values
2018-11-16 08:05:49 +01:00
local radius_cone_max = ( depth_maxpeak - depth_maxwidth ) * slope_max + radius_lining + 20
2018-11-03 22:38:55 +01:00
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
2018-11-16 08:05:49 +01:00
-- 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
2018-11-03 22:38:55 +01:00
2018-11-16 08:05:49 +01:00
local get_volcano = function ( pos )
local corner_xz = get_corner ( pos )
2018-11-03 22:38:55 +01:00
local next_seed = math.random ( 1 , 1000000000 )
math.randomseed ( corner_xz.x + corner_xz.z * 2 ^ 8 + mapgen_seed )
2018-11-09 07:08:48 +01:00
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 location = {x=corner_xz.x+volcano_region_size/2, z = corner_xz.z+volcano_region_size/2} -- For testing, puts volcanoes in a consistent grid
2018-11-03 22:38:55 +01:00
local depth_peak = math.random ( depth_minpeak , depth_maxpeak )
2018-11-16 05:21:05 +01:00
local depth_lava
if state < state_extinct then
depth_lava = math.random ( depth_root , depth_base ) -- extinct, put the lava somewhere deep.
else
depth_lava = math.random ( depth_peak - 25 , depth_peak ) -- dormant or active, put the lava somewhere in the volcano's throat
end
2018-11-03 22:38:55 +01:00
local slope = math.random ( ) * ( slope_max - slope_min ) + slope_min
2018-11-05 00:44:04 +01:00
local caldera = math.random ( ) * ( caldera_max - caldera_min ) + caldera_min
2018-11-09 07:08:48 +01:00
2018-11-03 22:38:55 +01:00
math.randomseed ( next_seed )
2018-11-05 00:44:04 +01:00
return { location = location , depth_peak = depth_peak , depth_lava = depth_lava , slope = slope , state = state , caldera = caldera }
2018-11-03 22:38:55 +01:00
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 = { }
2018-11-11 09:34:32 +01:00
-- Used as part of the post-mapgen hackery to fix the weird slices mapgen v7 sometimes takes out of volcano cones
2018-11-13 10:56:40 +01:00
local patch_data = { }
2018-11-11 09:34:32 +01:00
local patch_func = function ( patch_area , patch_content )
local minp = patch_area.MinEdge
local maxp = patch_area.MaxEdge
local map_vm = minetest.get_voxel_manip ( minp , maxp )
local emin , emax = map_vm : get_emerged_area ( )
map_vm : get_data ( patch_data )
local map_area = VoxelArea : new { MinEdge = emin , MaxEdge = emax }
local map_iterator = map_area : iterp ( minp , maxp )
2018-11-13 10:56:40 +01:00
local changes_made = false
2018-11-11 09:34:32 +01:00
for vi in patch_area : iterp ( minp , maxp ) do
local mi = map_iterator ( )
2018-11-11 09:40:22 +01:00
if patch_content [ vi ] ~= nil and patch_data [ mi ] ~= patch_content [ vi ] then
2018-11-11 09:34:32 +01:00
patch_data [ mi ] = patch_content [ vi ]
changes_made = true
end
end
if changes_made then
--send data back to voxelmanip
map_vm : set_data ( patch_data )
--write it to world
map_vm : write_to_map ( )
end
end
2018-11-03 22:38:55 +01:00
minetest.register_on_generated ( function ( minp , maxp , seed )
2018-11-07 08:11:41 +01:00
if minp.y > depth_maxpeak or maxp.y < depth_root then
2018-11-03 22:38:55 +01:00
return
end
2018-11-08 09:19:26 +01:00
2018-11-09 07:08:48 +01:00
local volcano = get_volcano ( minp )
2018-11-09 01:42:40 +01:00
2018-11-09 07:08:48 +01:00
if volcano == nil then
return -- no volcano in this map region
2018-11-09 01:42:40 +01:00
end
2018-11-08 09:19:26 +01:00
local depth_peak = volcano.depth_peak
local base_radius = ( depth_peak - depth_maxwidth ) * volcano.slope + radius_lining
-- early out if the volcano is too far away to matter
2018-11-16 08:05:49 +01:00
-- 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
2018-11-09 01:42:40 +01:00
then
2018-11-08 09:19:26 +01:00
return
end
2018-11-05 00:44:04 +01:00
2018-11-11 09:34:32 +01:00
local patch_area_max
local patch_content_max
local patch_area_min
local patch_content_min
if mg_name == " v7 " then
patch_area_max = VoxelArea : new { MinEdge = { x = minp.x , y = maxp.y , z = minp.z } , MaxEdge = maxp }
patch_content_max = { }
patch_area_min = VoxelArea : new { MinEdge = minp , MaxEdge = { x = maxp.x , y = minp.y , z = maxp.z } }
patch_content_min = { }
end
2018-11-03 22:38:55 +01:00
local vm , emin , emax = minetest.get_mapgen_object ( " voxelmanip " )
local area = VoxelArea : new { MinEdge = emin , MaxEdge = emax }
vm : get_data ( data )
2018-11-16 08:05:49 +01:00
local sidelen = mapgen_chunksize * 16 --length of a mapblock
2018-11-03 22:38:55 +01:00
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
2018-11-05 00:44:04 +01:00
local caldera = volcano.caldera
2018-11-03 22:38:55 +01:00
local state = volcano.state
2018-11-13 10:56:40 +01:00
-- 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
2018-11-03 22:38:55 +01:00
for vi , x , y , z in area : iterp_xyz ( minp , maxp ) do
2018-11-13 10:56:40 +01:00
2018-11-03 22:38:55 +01:00
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
2018-11-13 10:56:40 +01:00
local biome_data = magma_conduits.biome_ids [ biomemap [ ( z - minz ) * sidelen + ( x - minx ) + 1 ] ]
2018-11-16 05:21:05 +01:00
-- Determine what materials to use at this y level
-------------------------------------------------------------------------------------------------
2018-11-13 10:56:40 +01:00
local c_top
local c_filler
local c_dust
2018-11-08 21:13:31 +01:00
if state < state_dormant then
2018-11-05 15:43:38 +01:00
if y < water_level then
2018-11-13 10:56:40 +01:00
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
2018-11-08 09:19:26 +01:00
elseif y < snow_line + snow_border then
2018-11-13 10:56:40 +01:00
c_top = c_soil_snow
c_filler = c_soil
c_dust = c_snow
2018-11-08 09:19:26 +01:00
else
2018-11-13 10:56:40 +01:00
c_top = c_snow_block
c_filler = c_snow_block
c_dust = c_snow
2018-11-05 15:43:38 +01:00
end
else
2018-11-13 10:56:40 +01:00
c_top = c_ash
c_filler = c_ash
2018-11-05 15:43:38 +01:00
end
2018-11-03 22:38:55 +01:00
local pipestuff
local liningstuff
if y < depth_lava + math.random ( ) * 1.1 then
2018-11-08 21:13:31 +01:00
if state < state_extinct then
2018-11-03 22:38:55 +01:00
pipestuff = c_plug -- extinct volcano
liningstuff = c_lining
else
pipestuff = c_lava
liningstuff = c_hot_lining
end
else
2018-11-08 21:13:31 +01:00
if state < state_dormant then
2018-11-03 22:38:55 +01:00
pipestuff = c_plug -- dormant volcano
liningstuff = c_lining
else
pipestuff = c_air -- active volcano
liningstuff = c_lining
end
end
2018-11-16 05:21:05 +01:00
-- Actually create the volcano
-------------------------------------------------------------------------------------------
2018-11-03 22:38:55 +01:00
2018-11-16 05:21:05 +01:00
if y < depth_base then
local chamber_radius = base_radius / volcano.slope
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
2018-11-03 22:38:55 +01:00
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
2018-11-05 00:44:04 +01:00
end
elseif y < depth_peak + 5 then -- cone
2018-11-07 08:11:41 +01:00
local current_elevation = y - depth_maxwidth
local peak_elevation = depth_peak - depth_maxwidth
2018-11-08 21:13:31 +01:00
if current_elevation > peak_elevation - caldera and distance < current_elevation - peak_elevation + caldera then
2018-11-03 22:38:55 +01:00
data [ vi ] = c_air -- caldera
elseif distance < radius_vent then
data [ vi ] = pipestuff
elseif distance < radius_lining then
data [ vi ] = liningstuff
2018-11-07 08:11:41 +01:00
elseif distance < current_elevation * - volcano.slope + base_radius then
2018-11-03 22:38:55 +01:00
data [ vi ] = c_cone
2018-11-13 10:56:40 +01:00
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
2018-11-08 09:19:26 +01:00
end
2018-11-13 10:56:40 +01:00
if data [ vi + area.ystride ] == c_air and c_dust ~= nil then
data [ vi + area.ystride ] = c_dust
2018-11-07 08:11:41 +01:00
end
2018-11-03 22:38:55 +01:00
end
2018-11-11 09:34:32 +01:00
-- Used as part of the post-mapgen hackery to fix the weird slices mapgen v7 sometimes takes out of volcano cones
if mg_name == " v7 " then
if y == maxp.y then
patch_content_max [ patch_area_max : index ( x , y , z ) ] = data [ vi ]
elseif y == minp.y then
patch_content_min [ patch_area_min : index ( x , y , z ) ] = data [ vi ]
end
end
2018-11-07 08:11:41 +01:00
end
2018-11-11 09:34:32 +01:00
end
-- Used as part of the post-mapgen hackery to fix the weird slices mapgen v7 sometimes takes out of volcano cones
if mg_name == " v7 " then
minetest.after ( 2 , patch_func , patch_area_max , patch_content_max )
minetest.after ( 2 , patch_func , patch_area_min , patch_content_min )
2018-11-03 22:38:55 +01:00
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 ( )
2018-11-08 21:13:31 +01:00
end )
2018-11-09 01:42:40 +01:00
----------------------------------------------------------------------------------------------
-- Debugging and sightseeing commands
minetest.register_privilege ( " findvolcano " , { description = " Allows players to use a console command to find volcanoes " , give_to_singleplayer = false } )
local send_volcano_state = function ( pos , name )
2018-11-09 07:08:48 +01:00
2018-11-16 08:05:49 +01:00
local corner_xz = get_corner ( pos )
2018-11-09 07:08:48 +01:00
2018-11-16 08:05:49 +01:00
local text = " In region ( " .. tostring ( corner_xz.x ) .. " , 0, " .. tostring ( corner_xz.z ) .. " ) to ( "
.. tostring ( corner_xz.x + volcano_region_size ) .. " , 0, " .. tostring ( corner_xz.z + volcano_region_size ) .. " ) \n "
2018-11-09 07:08:48 +01:00
2018-11-13 10:56:40 +01:00
local volcano = get_volcano ( pos )
2018-11-09 07:08:48 +01:00
if volcano == nil then
minetest.chat_send_player ( name , text .. " No volcano present " )
return
end
text = text .. " Nearest volcano is at " .. minetest.pos_to_string ( volcano.location , 0 )
2018-11-09 01:42:40 +01:00
.. " \n Height: " .. tostring ( volcano.depth_peak ) .. " Slope: " .. tostring ( volcano.slope )
.. " \n State: "
if volcano.state < state_extinct then
text = text .. " Extinct "
elseif volcano.state < state_dormant then
text = text .. " Dormant "
else
text = text .. " Active "
end
2018-11-16 08:05:49 +01:00
local base_radius = ( volcano.depth_peak - depth_maxwidth ) * volcano.slope + radius_lining
text = text .. " \n Base radius: " .. tostring ( base_radius )
2018-11-09 01:42:40 +01:00
minetest.chat_send_player ( name , text )
end
2018-11-08 21:13:31 +01:00
minetest.register_chatcommand ( " findvolcano " , {
params = " pos " , -- Short parameter description
description = " find the volcano in 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
2018-11-09 01:42:40 +01:00
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_volcano_state ( pos , name )
2018-11-08 21:13:31 +01:00
return true
else
2018-11-13 10:56:40 +01:00
local playerobj = minetest.get_player_by_name ( name )
2018-11-09 01:42:40 +01:00
send_volcano_state ( playerobj : get_pos ( ) , name )
2018-11-08 21:13:31 +01:00
return true
end
else
return false , " You need the findvolcano privilege to use this command. "
end
end ,
} )