2018-11-07 08:11:41 +01:00
local depth_root = - 3000 -- TODO: should add some kind of optional magma chamber down there
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-03 22:38:55 +01:00
local chunk_size = 1000
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 state_extinct = 0.25
local state_dormant = 0.5
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-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 " )
local c_soil_grass = minetest.get_content_id ( " default:dirt_with_grass " )
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-05 15:43:38 +01:00
local c_underwater_soil = minetest.get_content_id ( " default: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 " ) )
-- derived values
local radius_cone_max = ( depth_maxpeak - depth_maxwidth ) / ( 2 * slope_min )
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
local get_volcano = function ( pos )
local corner_xz = { x = math.floor ( pos.x / chunk_size ) * chunk_size , z = math.floor ( pos.z / chunk_size ) * chunk_size }
local next_seed = math.random ( 1 , 1000000000 )
math.randomseed ( corner_xz.x + corner_xz.z * 2 ^ 8 + mapgen_seed )
local location = scatter_2d ( corner_xz , chunk_size , radius_cone_max )
2018-11-07 08:11:41 +01:00
--local location = {x=corner_xz.x+chunk_size/2, z = corner_xz.z+chunk_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-08 21:13:31 +01:00
local depth_lava = math.random ( depth_peak - 25 , depth_peak )
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-03 22:38:55 +01:00
2018-11-07 08:11:41 +01:00
local state = math.random ( ) -- 0-0.25 = extinct, 0.25-0.5 = dormant, 0.5-1.0 active
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 = { }
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
local volcano = get_volcano ( minp )
local depth_peak = volcano.depth_peak
local base_radius = ( depth_peak - depth_maxwidth ) * volcano.slope + radius_lining
local sidelen = maxp.x - minp.x + 1 --length of a mapblock
-- early out if the volcano is too far away to matter
if vector.distance ( volcano.location , { x = minp.x + sidelen / 2 , y = 0 , z = minp.z + sidelen / 2 } ) > base_radius * 2.5 then
return
end
2018-11-05 00:44:04 +01:00
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 )
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
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
2018-11-05 15:43:38 +01:00
local dirtstuff
2018-11-08 09:19:26 +01:00
local replace_soil = false -- determines if the soil type should be replaced with c_soil if there's layers on top of it
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
dirtstuff = c_underwater_soil
2018-11-07 08:11:41 +01:00
elseif y < snow_line then
2018-11-08 09:19:26 +01:00
dirtstuff = c_soil_grass
replace_soil = true
elseif y < snow_line + snow_border then
2018-11-07 08:11:41 +01:00
dirtstuff = c_soil_snow
2018-11-08 09:19:26 +01:00
replace_soil = true
else
dirtstuff = c_snow_block
2018-11-05 15:43:38 +01:00
end
else
dirtstuff = c_ash
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
if y < depth_base then -- pipe
if distance < radius_vent then
data [ vi ] = pipestuff
elseif distance < radius_lining then
2018-11-05 15:43:38 +01:00
if 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-07 08:11:41 +01:00
elseif distance < current_elevation * - volcano.slope + base_radius + nvals_perlin [ vi3d ] *- 4 then
2018-11-03 22:38:55 +01:00
data [ vi ] = dirtstuff
2018-11-08 09:19:26 +01:00
if replace_soil and data [ vi - area.ystride ] == dirtstuff then
data [ vi - area.ystride ] = c_soil -- soil underneath a layer of other soil shouldn't have grass on top
end
2018-11-07 08:11:41 +01:00
if y >= snow_line then
2018-11-08 09:19:26 +01:00
if data [ vi + area.ystride ] == c_air then
data [ vi + area.ystride ] = c_snow -- generation advances in a positive y direction so this will be overwritten if more solid stuff is placed above
end
2018-11-07 08:11:41 +01:00
end
2018-11-03 22:38:55 +01:00
end
2018-11-07 08:11:41 +01:00
end
2018-11-03 22:38:55 +01:00
end
2018-11-07 08:11:41 +01:00
-- These don't seem to work?
--minetest.generate_decorations(vm, minp, maxp)
--minetest.generate_ores(vm, minp, maxp)
2018-11-03 22:38:55 +01:00
--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 )
minetest.register_privilege ( " findvolcano " , { description = " Allows players to use a console command to find the nearest volcano " , give_to_singleplayer = false } )
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
pos = tonumber ( param )
if pos ~= nil then
--TODO
return true
else
playerobj = minetest.get_player_by_name ( name )
volcano = get_volcano ( playerobj : get_pos ( ) )
text = " Nearest volcano is at " .. minetest.pos_to_string ( volcano.location , 0 )
.. " \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
minetest.chat_send_player ( name , text )
return true
end
else
return false , " You need the findvolcano privilege to use this command. "
end
end ,
} )