Added comments

This commit is contained in:
Gael-de-Sailly 2015-08-31 11:15:53 +02:00
parent 62599627a4
commit 5f02b1b943
5 changed files with 205 additions and 141 deletions

View File

@ -9,10 +9,12 @@ if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Loading basic functions ...")
end
-- Set mapgen parameters to singlenode
minetest.register_on_mapgen_init(function(mgparams)
minetest.set_mapgen_params({mgname="singlenode", flags="nolight"})
end)
-- public function made by the default mod, to register ores and blobs
if default then
if default.register_ores then
default.register_ores()
@ -22,6 +24,7 @@ if default then
end
end
-- useful function to convert a 3D pos to 2D
function pos2d(pos)
if type(pos) == "number" then
return {x = pos, y = pos}
@ -32,6 +35,7 @@ function pos2d(pos)
end
end
-- useful function to convert a 2D pos to 3D
function pos3d(pos, alt)
alt = alt or 0
if type(pos) == "number" then
@ -43,6 +47,7 @@ function pos3d(pos, alt)
end
end
-- Modify a node to add a group
function minetest.add_group(node, groups)
local def = minetest.registered_items[node]
if not def then
@ -68,8 +73,11 @@ if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Loading settings API ...")
end
-- Settings are handled by a separate file, settings.lua
-- This file will also run the appropriate mapgen file, according to the vmg_version setting
dofile(vmg.path .. "/settings.lua")
-- The mapgen file contains a mapgen function and a spawnplayer function. So, set the spawnplayer function on newplayer and on respawnplayer.
if vmg.define("spawn", true) then
minetest.register_on_newplayer(vmg.spawnplayer)
end
@ -78,12 +86,14 @@ if vmg.define("respawn", true) then
minetest.register_on_respawnplayer(vmg.spawnplayer)
end
-- Call the mapgen function vmg.generate on mapgen.
minetest.register_on_generated(vmg.generate)
if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Loading nodes ...")
end
-- Node definitions
dofile(vmg.path .. "/nodes.lua")
if vmg.loglevel >= 1 then

View File

@ -1,6 +1,7 @@
-- Mapgen 2.2
-- Monday July 6, 2015
-- Define perlin noises used in this mapgen by default
vmg.noises = {
-- Noise 1 : Base Ground Height 2D
@ -59,6 +60,7 @@ vmg.noises = {
}
-- function to get noisemaps
function vmg.noisemap(i, minp, chulens)
local obj = minetest.get_perlin_map(vmg.noises[i], chulens)
if minp.z then
@ -68,10 +70,12 @@ function vmg.noisemap(i, minp, chulens)
end
end
-- If the noises are already defined in settings, use it instead of the noise parameters above.
for i, n in ipairs(vmg.noises) do
vmg.noises[i] = vmg.define("noise_" .. i, n)
end
-- List of functions to run at the end of the mapgen procedure, used especially by jungle tree roots
vmg.after_mapgen = {}
function vmg.register_after_mapgen(f, ...)
@ -85,6 +89,7 @@ function vmg.execute_after_mapgen()
vmg.after_mapgen = {}
end
-- Define parameters
local river_depth = vmg.define("river_depth", 3) + 1
local river_size = vmg.define("river_size", 5) / 100
local caves_size = vmg.define("caves_size", 7) / 100
@ -112,15 +117,24 @@ local plants = vmg.define("plants", true)
local water_level = vmg.define("water_level", 1)
local river_water = vmg.define("river_water", true)
-- THE MAPGEN FUNCTION
function vmg.generate(minp, maxp, seed)
-- minp and maxp strings, used by logs
local minps, maxps = minetest.pos_to_string(minp), minetest.pos_to_string(maxp)
if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Preparing to generate map from " .. minps .. " to " .. maxps .. " ...")
elseif vmg.loglevel == 1 then
print("[Valleys Mapgen] Generating map from " .. minps .. " to " .. maxps .. " ...")
end
-- start the timer
local t0 = os.clock()
-- Define content IDs
-- A content ID is a number that represents a node in the core of Minetest.
-- Every nodename has its ID.
-- The VoxelManipulator uses content IDs instead of nodenames.
-- Ground nodes
local c_stone = minetest.get_content_id("default:stone")
local c_dirt = minetest.get_content_id("default:dirt")
local c_lawn = minetest.get_content_id("default:dirt_with_grass")
@ -144,6 +158,7 @@ function vmg.generate(minp, maxp, seed)
local c_lava = minetest.get_content_id("default:lava_source")
local c_snow_layer = minetest.get_content_id("default:snow")
-- Tree nodes
local c_tree = minetest.get_content_id("default:tree")
local c_leaves = minetest.get_content_id("default:leaves")
local c_apple = minetest.get_content_id("default:apple")
@ -154,7 +169,8 @@ function vmg.generate(minp, maxp, seed)
local c_firtree = minetest.get_content_id("valleys_mapgen:fir_tree")
local c_firleaves = minetest.get_content_id("valleys_mapgen:fir_needles")
local c_grass = {
-- Plants
local c_grass = { -- Use an array instead of defining 5 variables. More useful: c_grass[i] is the content ID of default:grass_i.
minetest.get_content_id("default:grass_1"),
minetest.get_content_id("default:grass_2"),
minetest.get_content_id("default:grass_3"),
@ -172,24 +188,32 @@ function vmg.generate(minp, maxp, seed)
local c_dandelion_white = minetest.get_content_id("flowers:dandelion_white")
local c_dandelion_yellow = minetest.get_content_id("flowers:dandelion_yellow")
-- Air and Ignore
local c_air = minetest.get_content_id("air")
local c_ignore = minetest.get_content_id("ignore")
-- The VoxelManipulator, a complicated but speedy method to set many nodes at the same time
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local data = vm:get_data()
local data = vm:get_data() -- data is the original array of content IDs (solely or mostly air)
-- Be careful: emin ≠ minp and emax ≠ maxp !
-- The data array is not limited by minp and maxp. It exceeds it by 16 nodes in the 6 directions.
-- The real limits of data array are emin and emax.
-- The VoxelArea is used to convert a position into an index for the array.
local a = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
local ystride = a.ystride
local ystride = a.ystride -- Tip : the ystride of a VoxelArea is the number to add to the array index to get the index of the position above. It's faster because it avoids to completely recalculate the index.
local chulens = vector.add(vector.subtract(maxp, minp), 1)
local chulens_sup = {x = chulens.x, y = chulens.y + 6, z = chulens.z}
local chulens = vector.add(vector.subtract(maxp, minp), 1) -- Size of the generated area, used by noisemaps
local chulens_sup = {x = chulens.x, y = chulens.y + 6, z = chulens.z} -- for the noise #6 that needs extra values
local minp2d = pos2d(minp)
-- Mapgen preparation is now finished. Check the timer to know the elapsed time.
local t1 = os.clock()
if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Mapgen preparation finished in " .. displaytime(t1-t0))
print("[Valleys Mapgen] Calculating noises ...")
end
-- Calculate the noise values
local n1 = vmg.noisemap(1, minp2d, chulens)
local n2 = vmg.noisemap(2, minp2d, chulens)
local n3 = vmg.noisemap(3, minp2d, chulens)
@ -206,16 +230,21 @@ function vmg.generate(minp, maxp, seed)
local n14 = vmg.noisemap(14, minp2d, chulens)
local n15 = vmg.noisemap(15, minp2d, chulens)
local n16 = vmg.noisemap(16, minp2d, chulens)
-- Noise #17 is not used this way
local n18 = vmg.noisemap(18, minp2d, chulens)
-- After noise calculation, check the timer
local t2 = os.clock()
if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Noises calculation finished in " .. displaytime(t2-t1))
print("[Valleys Mapgen] Collecting data ...")
end
-- THE CORE OF THE MOD: THE MAPGEN ALGORITHM ITSELF
-- indexes for noise arrays
local i2d = 1 -- index for 2D noises
local i3d_sup = 1 -- index for noise 6 which has a special size
local i3d_sup = 1 -- index for noise #6 which has a special size
local i3d = 1 -- index for 3D noises
-- Calculate increments
@ -228,27 +257,29 @@ function vmg.generate(minp, maxp, seed)
for x = minp.x, maxp.x do -- for each YZ plane
for z = minp.z, maxp.z do -- for each vertical line in this plane
local v1, v2, v3, v4, v5, v7, v13, v14, v15, v16, v18 = n1[i2d], n2[i2d], n3[i2d], n4[i2d], n5[i2d], n7[i2d], n13[i2d], n14[i2d], n15[i2d], n16[i2d], n18[i2d] -- n for noise, v for value
v3 = v3 ^ 2 -- v3 must be > 0 and by the square there are high mountains but the median valleys depth is small.
local base_ground = v1 + v3 -- v3 is here because terrain is generally higher when valleys are deep (mountains)
v2 = math.abs(v2) - river_size
local river = v2 < 0
local valleys = v3 * (1 - math.exp(- (v2 / v4) ^ 2)) -- use the curve of the function 1exp((x/a)²) to modelise valleys. Making "a" varying 0 < a ≤ 1 will change the shape of the valleys. Try it with a geometry software ! (here x = v2 and a = v4)
local mountain_ground = base_ground + valleys
local slopes = v5 * valleys
local v1, v2, v3, v4, v5, v7, v13, v14, v15, v16, v18 = n1[i2d], n2[i2d], n3[i2d], n4[i2d], n5[i2d], n7[i2d], n13[i2d], n14[i2d], n15[i2d], n16[i2d], n18[i2d] -- take the noise values for 2D noises
v3 = v3 ^ 2 -- The square function changes the behaviour of this noise : very often small, and sometimes very high.
local base_ground = v1 + v3 -- v3 is here because terrain is generally higher where valleys are deep (mountains). base_ground represents the height of the rivers, most of the surface is above.
v2 = math.abs(v2) - river_size -- v2 represents the distance from the river, in arbitrary units.
local river = v2 < 0 -- the rivers are placed where v2 is negative, so where the original v2 value is close to zero.
local valleys = v3 * (1 - math.exp(- (v2 / v4) ^ 2)) -- use the curve of the function 1exp((x/a)²) to modelise valleys. Making "a" varying 0 < a ≤ 1 changes the shape of the valleys. Try it with a geometry software ! (here x = v2 and a = v4). This variable represents the height of the terrain, from the rivers.
local mountain_ground = base_ground + valleys -- approximate height of the terrain at this point (could be slightly modified by the 3D noise #6)
local slopes = v5 * valleys -- This variable represents the maximal influence of the noise #6 on the elevation. v5 is the rate of the height from rivers (variable "valleys") that is concerned.
if river then
local depth = river_depth * math.sqrt(1 - (v2 / river_size + 1) ^ 2) -- use the curve of the function sqrt(1-x²) which modelizes a circle.
local depth = river_depth * math.sqrt(1 - (v2 / river_size + 1) ^ 2) -- use the curve of the function sqrt(1x²) which modelizes a circle.
mountain_ground = math.min(math.max(base_ground - depth, water_level - 6), mountain_ground)
slopes = 0
-- base_ground - depth : height of the bottom of the river
-- water_level - 6 : don't make rivers below 6 nodes under the surface
slopes = 0 -- noise #6 has not any influence on rivers
end
-- Choose biome
-- Choose biome, by default normal dirt
local dirt = c_dirt
local lawn = c_lawn
local snow = c_snow
local max = math.max(v13, v14, v15) -- the biome is the maximal of these 3 values, if bigger than 0.5. Else, make normal dirt.
if max > dirt_threshold then
local max = math.max(v13, v14, v15) -- the biome is the maximal of these 3 values.
if max > dirt_threshold then -- if one of these values is bigger than dirt_threshold, make clayey, silty or sandy dirt, depending on the case. If none of clay, silt or sand is predominant, make normal dirt.
if v13 == max then
if v13 > clay_threshold then
dirt = c_clay
@ -281,53 +312,53 @@ function vmg.generate(minp, maxp, seed)
end
end
end
local is_beach = v15 > 0 and v16 > 0
local beach = v15 * v16 + water_level -- the y coordinate below which dirt is replaced by beach sand
local is_beach = v15 > 0 and v16 > 0 -- 2 conditions must been met to make possible the beach.
local beach = v15 * v16 + water_level -- the y coordinate below which dirt is replaced by beach sand. So if the terrain is higher, there is no beach.
-- raw humidity
-- raw humidity, see below at vmg.get_humidity
local hraw = 2 ^ (v13 - v15 + v18 * 2)
for y = minp.y, maxp.y do -- for each node in vertical line
local ivm = a:index(x, y, z)
local v6, v8, v9, v10, v11, v12 = n6[i3d_sup], n8[i3d], n9[i3d], n10[i3d], n11[i3d], n12[i3d]
local is_cave = v8 ^ 2 + v9 ^ 2 + v10 ^ 2 + v11 ^ 2 < caves_size
local ivm = a:index(x, y, z) -- index of the data array, matching the position {x, y, z}
local v6, v8, v9, v10, v11, v12 = n6[i3d_sup], n8[i3d], n9[i3d], n10[i3d], n11[i3d], n12[i3d] -- take the noise values for 3D noises
local is_cave = v8 ^ 2 + v9 ^ 2 + v10 ^ 2 + v11 ^ 2 < caves_size -- The 4 cave noises must be close to zero to produce a cave. The square is used for 2 reasons : we need positive values, and, for mathematical reasons, it results in more circular caves.
if v6 * slopes > y - mountain_ground then -- if pos is in the ground
if not is_cave then
local thickness = v7 - math.sqrt(math.abs(y)) / dirt_thickness
local above = math.ceil(thickness + math.random()) -- The following code will look for air at this many nodes up. If any, make dirt, else, make stone. So, it's the dirt layer thickness.
if not is_cave then -- if pos is not inside a cave
local thickness = v7 - math.sqrt(math.abs(y)) / dirt_thickness -- Calculate dirt thickness, according to noise #7, dirt thickness parameter, and elevation (y coordinate)
local above = math.ceil(thickness + math.random()) -- The following code will look for air at this many nodes up. If any, make dirt, else, make stone. So, it's the dirt layer thickness. An "above" of zero = bare stone.
if y >= water_level and n6[i3d_sup+i3d_incrY] * slopes <= y + 1 - mountain_ground and not river then
if is_beach and y < beach then
if y >= water_level and n6[i3d_sup+i3d_incrY] * slopes <= y + 1 - mountain_ground and not river then -- If node above is in the ground
if is_beach and y < beach then -- if beach, make sand
data[ivm] = c_sand
else -- if node above is not in the ground, place lawn
else -- place lawn
-- calculate humidity
-- calculate humidity, see below at vmg.get_humidity
local sea_water = 0.5 ^ math.max((y - water_level) / 6, 0)
local river_water = 0.5 ^ math.max((y - base_ground) / 3, 0)
local water = sea_water + (1 - sea_water) * river_water
local humidity = hraw + water
local ivm2 = ivm + ystride
local ivm2 = ivm + ystride -- index of the node above
y = y + 1
local pos = {x = x, y = y, z = z}
local v17 = vmg.get_noise(pos, 17)
local temp -- calculate_temperature for node above
local v17 = vmg.get_noise(pos, 17) -- Noise #17 is used this way : that's a 3D noise, so a noisemap would be heavy, and less than 2% would be used, contrary to other 3D noises. So it's faster to calculate it node per node, only when needed.
local temp -- calculate_temperature for node above, see below at vmg.get_temperature
if y > 0 then
temp = v17 * 0.5 ^ (y / altitude_chill)
temp = v17 * 0.5 ^ (y / altitude_chill) -- Divide temperature noise by 2 by climbing altitude_chill
else
temp = v17 * 0.5 ^ (-y / altitude_chill) + 20 * (v12 + 1) * (1 - 2 ^ (y / lava_depth))
end
if temp > snow_threshold then
if temp > snow_threshold then -- If temperature is too high for snow
if above > 0 then
data[ivm] = lawn
else
data[ivm] = c_stone
end
else
else -- Snow
if above > 0 then
data[ivm] = snow
data[ivm] = snow -- dirt with snow
else
data[ivm] = c_stone
end
@ -434,13 +465,14 @@ function vmg.generate(minp, maxp, seed)
end
vmg.execute_after_mapgen() -- needed for jungletree roots
-- After data collecting, check timer
local t3 = os.clock()
if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Data collecting finished in " .. displaytime(t3-t2))
print("[Valleys Mapgen] Writing data ...")
end
-- execute voxelmanip boring stuff to write to the map
-- execute voxelmanip boring stuff to write to the map...
vm:set_data(data)
minetest.generate_ores(vm, minp, maxp)
vm:set_lighting({day = 0, night = 0})
@ -448,6 +480,7 @@ function vmg.generate(minp, maxp, seed)
vm:update_liquids()
vm:write_to_map()
-- Now mapgen is finished. What an adventure for just generating a chunk ! I hope your processor is speedy and you have enough RAM !
local t4 = os.clock()
if vmg.loglevel >= 2 then
print("[Valleys Mapgen] Data writing finished in " .. displaytime(t4-t3))
@ -457,13 +490,14 @@ function vmg.generate(minp, maxp, seed)
end
end
-- Trees are registered in a separate file
dofile(vmg.path .. "/trees.lua")
function vmg.get_humidity_raw(pos)
local v13 = vmg.get_noise(pos, 13)
local v15 = vmg.get_noise(pos, 15)
local v18 = vmg.get_noise(pos, 18)
return 2 ^ (v13 - v15 + v18 * 2)
local v13 = vmg.get_noise(pos, 13) -- Clayey soil : wetter
local v15 = vmg.get_noise(pos, 15) -- Sandy soil : drier
local v18 = vmg.get_noise(pos, 18) -- Humidity noise
return 2 ^ (v13 - v15 + v18 * 2) -- Make sure that humidity is positive. Humidity is between 0.25 and 16.
end
function vmg.get_humidity(pos)
@ -471,32 +505,33 @@ function vmg.get_humidity(pos)
local flatpos = pos2d(pos)
local hraw = vmg.get_humidity_raw(flatpos)
-- Get base ground level to know the river level that influences humidity
local v1 = vmg.get_noise(flatpos, 1)
local v3 = vmg.get_noise(flatpos, 3) ^ 2
local base_ground = v1 + v3
local sea_water = 0.5 ^ math.max((y - water_level) / 6, 0)
local river_water = 0.5 ^ math.max((y - base_ground) / 3, 0)
local water = sea_water + (1 - sea_water) * river_water
local sea_water = 0.5 ^ math.max((y - water_level) / 6, 0) -- At the sea level, sea_water is 1. Every 6 nodes height divide it by 2.
local river_water = 0.5 ^ math.max((y - base_ground) / 3, 0) -- At the river level, river_water is 1. Every 3 nodes height divide it by 2.
local water = sea_water + (1 - sea_water) * river_water -- A simple sum is not satisfactory, because it may be bigger than 1.
return hraw + water
end
function vmg.get_temperature(pos)
local v12 = vmg.get_noise(pos, 12) + 1
local v17 = vmg.get_noise(pos, 17)
local v12 = vmg.get_noise(pos, 12) + 1 -- Lava noise for underground
local v17 = vmg.get_noise(pos, 17) -- Climate noise
local y = pos.y
if y > 0 then
return v17 * 0.5 ^ (y / altitude_chill)
return v17 * 0.5 ^ (y / altitude_chill) -- Divide v17 by 2 by climbing "altitude_chill" nodes
else
return v17 * 0.5 ^ (-y / altitude_chill) + 20 * v12 * (1 - 2 ^ (y / lava_depth))
return v17 * 0.5 ^ (-y / altitude_chill) + 20 * v12 * (1 - 2 ^ (y / lava_depth)) -- Underground, v17 less and less matter. So, gradually replace it by another calculation method, based on lava. Sorry: I don't remember the sense ofthis code :/
end
end
function vmg.get_noise(pos, i)
local n = vmg.noises[i]
local noise = minetest.get_perlin(n.seed, n.octaves, n.persist, 1)
if not pos.z then
if not pos.z then -- 2D noise
return noise:get2d({x = pos.x / n.spread.x, y = pos.y / n.spread.y}) * n.scale + n.offset
else
else -- 3D noise
return noise:get3d({x = pos.x / n.spread.x, y = pos.y / n.spread.y, z = pos.z / n.spread.z}) * n.scale + n.offset
end
end
@ -506,47 +541,52 @@ local function round(n)
end
function vmg.get_elevation(pos)
local v1 = vmg.get_noise(pos, 1)
local v2 = math.abs(vmg.get_noise(pos, 2)) - river_size
local v3 = vmg.get_noise(pos, 3) ^ 2
local v1 = vmg.get_noise(pos, 1) -- base ground
local v2 = math.abs(vmg.get_noise(pos, 2)) - river_size -- valleys
local v3 = vmg.get_noise(pos, 3) ^ 2 -- valleys depth
local base_ground = v1 + v3
if v2 < 0 then
if v2 < 0 then -- river
return math.ceil(base_ground), true
end
local v4 = vmg.get_noise(pos, 4)
local v5 = vmg.get_noise(pos, 5)
local v4 = vmg.get_noise(pos, 4) -- valleys profile
local v5 = vmg.get_noise(pos, 5) -- inter-valleys slopes
-- Same calculation than in vmg.generate
local base_ground = v1 + v3
local valleys = v3 * (1 - math.exp(- (v2 / v4) ^ 2))
local mountain_ground = base_ground + valleys
local pos = pos3d(pos, round(mountain_ground))
local pos = pos3d(pos, round(mountain_ground)) -- For now we don't know the elevation. We will test some y values. Set the position to montain_ground which is the most probable value.
local slopes = v5 * valleys
if vmg.get_noise(pos, 6) * slopes > pos.y - mountain_ground then
if vmg.get_noise(pos, 6) * slopes > pos.y - mountain_ground then -- Position is in the ground, so look for air higher
pos.y = pos.y + 1
while vmg.get_noise(pos, 6) * slopes > pos.y - mountain_ground do
pos.y = pos.y + 1
end
return pos.y, false
else
end -- End of the loop when there is air
return pos.y, false -- Return position of the first air node, and false because that's not a river
else -- Position is not in the ground, so look for dirt lower
pos.y = pos.y - 1
while vmg.get_noise(pos, 6) * slopes <= pos.y - mountain_ground do
pos.y = pos.y - 1
end
return pos.y, false
end -- End of the loop when there is dirt (or any ground)
pos.y = pos.y + 1 -- We have the latest dirt node and we want the first air node that is just above
return pos.y, false -- Return position of the first air node, and false because that's not a river
end
end
function vmg.spawnplayer(player)
-- Choose a point to spawn the player, from an angle, and a distance from (0;0)
local angle = math.random() * math.pi * 2
local distance = math.random() * player_max_distance
local p_angle = {x = math.cos(angle), y = math.sin(angle)}
local pos = {x = -p_angle.x * distance, y = -p_angle.y * distance}
local elevation, river = vmg.get_elevation(pos)
while elevation < water_level + 2 or river do
pos.x = pos.x + p_angle.x
pos.y = pos.y + p_angle.y
local p_angle = {x = math.cos(angle), y = math.sin(angle)} -- Get a position on the trigonometric circle. This position is exactely at 1 unit from (0;0)
local pos = {x = p_angle.x * distance, y = p_angle.y * distance} -- Multiply it by distance, to get the position that meets angle and distance
local elevation, river = vmg.get_elevation(pos) -- get elevation from the previous function
while elevation < water_level + 2 or river do -- If there is water, choose another point
-- Move the position by one unit, to (0;0) to avoid spawning farther than player_max_distance.
pos.x = pos.x - p_angle.x
pos.y = pos.y - p_angle.y
-- and check again
elevation, river = vmg.get_elevation({x = round(pos.x), y = round(pos.y)})
end
pos = {x = round(pos.x), y = round(elevation + 1), z = round(pos.y)}
end -- end of the loop when pos is not in the water
pos = {x = round(pos.x), y = round(elevation + 1), z = round(pos.y)} -- Round position and add elevation
player:setpos(pos)
return true
return true -- Disable default player spawner
end

View File

@ -1,8 +1,10 @@
-- Set the liquid range according to settings (by default 3)
local waterflow = vmg.define("waterflow", 3)
minetest.override_item("default:river_water_source", {liquid_range = waterflow})
minetest.override_item("default:river_water_flowing", {liquid_range = waterflow})
-- Add silt
minetest.register_node("valleys_mapgen:silt", {
description = "Silt",
tiles = {"vmg_silt.png"},
@ -11,6 +13,7 @@ minetest.register_node("valleys_mapgen:silt", {
sounds = default.node_sound_dirt_defaults(),
})
-- I don't like the default:clay, this does not look like clay. So add red clay.
minetest.register_node("valleys_mapgen:red_clay", {
description = "Red Clay",
tiles = {"vmg_red_clay.png"},
@ -21,6 +24,7 @@ minetest.register_node("valleys_mapgen:red_clay", {
minetest.override_item("default:clay", {description = "White Clay"})
-- Add dirts
local function register_dirts(readname)
local name = readname:lower()
local itemstr_dirt = "valleys_mapgen:dirt_" .. name
@ -95,14 +99,20 @@ local function register_dirts(readname)
})
end
-- 3 types of dirt :
-- Clayey dirt is a dirt that contains clay, but is not pure clay
register_dirts("Clayey")
-- Idem for silty dirt that contains silt without beeing pure silt
register_dirts("Silty")
-- And sandy dirt
register_dirts("Sandy")
-----------
-- Trees --
-----------
-- Fir tree don't exist in the default game.
-- Textures from Forest mod (Gael-de-Sailly)
minetest.register_node("valleys_mapgen:fir_tree", {
description = "Fir Tree",
tiles = {"vmg_fir_tree_top.png", "vmg_fir_tree_top.png", "vmg_fir_tree.png"},
@ -176,6 +186,7 @@ minetest.register_craft({
}
})
-- Change leafdecay ratings
minetest.add_group("default:leaves", {leafdecay = 5})
minetest.add_group("default:jungleleaves", {leafdecay = 8})
minetest.add_group("default:pine_needles", {leafdecay = 7})

View File

@ -1,84 +1,84 @@
vmg.settings = Settings(minetest.get_worldpath() .. "/vmg.conf")
vmg.settings = Settings(minetest.get_worldpath() .. "/vmg.conf") -- Create settings object
local function define_str(flag, default, write_to_config)
local value = vmg.settings:get(flag)
if value then
if value then -- This flag exists in vmg.conf, return its value
return value, true
else
local on_config = minetest.setting_get("vmg_" .. flag)
if on_config then
local on_config = minetest.setting_get("vmg_" .. flag) -- get this flag in minetest.conf
if on_config then -- This flag exists in minetest.conf, so return its value
vmg.settings:set(flag, on_config)
return on_config, false
else
else -- Flag don't exist anywhere, so the default value will be written in settings and returned
if write_to_config then
minetest.setting_set("vmg_" .. flag, default)
minetest.setting_set("vmg_" .. flag, default) -- write to minetest.conf if write_to_config is enabled (usually disabled)
end
vmg.settings:set(flag, default)
return default, false
vmg.settings:set(flag, default) -- write to vmg.conf
return default, false -- return default value
end
end
end
local function define_num(flag, default, write_to_config)
local value = vmg.settings:get(flag)
if value then
if value then -- This flag exists in vmg.conf, return its value
return tonumber(value), true
else
local on_config = minetest.setting_get("vmg_" .. flag)
if on_config then
local on_config = minetest.setting_get("vmg_" .. flag) -- get this flag in minetest.conf
if on_config then -- This flag exists in minetest.conf, so return its value
vmg.settings:set(flag, on_config)
return tonumber(on_config), false
else
else -- Flag don't exist anywhere, so the default value will be written in settings and returned
if write_to_config then
minetest.setting_set("vmg_" .. flag, default)
minetest.setting_set("vmg_" .. flag, default) -- write to minetest.conf if write_to_config is enabled (usually disabled)
end
vmg.settings:set(flag, default)
return default, false
vmg.settings:set(flag, default) -- write to vmg.conf
return default, false -- return default value
end
end
end
local function define_bool(flag, default, write_to_config)
local value = vmg.settings:get_bool(flag)
if value ~= nil then
if value ~= nil then -- This flag exists in vmg.conf, return its value
return value, true
else
local on_config = minetest.setting_getbool("vmg_" .. flag)
if on_config ~= nil then
local on_config = minetest.setting_getbool("vmg_" .. flag) -- get this flag in minetest.conf
if on_config ~= nil then -- This flag exists in minetest.conf, so return its value
vmg.settings:set(flag, tostring(on_config))
return on_config, false
else
else -- Flag don't exist anywhere, so the default value will be written in settings and returned
if write_to_config then
minetest.setting_setbool("vmg_" .. flag, default)
minetest.setting_setbool("vmg_" .. flag, default) -- write to minetest.conf if write_to_config is enabled (usually disabled)
end
vmg.settings:set(flag, tostring(default))
return default, false
vmg.settings:set(flag, tostring(default)) -- write to vmg.conf
return default, false -- return default value
end
end
end
local function define_noise(flag, default, write_to_config)
local value = vmg.settings:get(flag)
if value then
if value then -- This flag exists in vmg.conf, return its value
return vmg.string_to_noise(value), true
else
local on_config = minetest.setting_get("vmg_" .. flag)
if on_config then
local on_config = minetest.setting_get("vmg_" .. flag) -- get this flag in minetest.conf
if on_config then -- This flag exists in minetest.conf, so return its value
vmg.settings:set(flag, on_config)
return vmg.string_to_noise(on_config), false
else
else -- Flag don't exist anywhere, so the default value will be written in settings and returned
local str_default = vmg.noise_to_string(default)
if write_to_config then
minetest.setting_set("vmg_" .. flag, str_default)
minetest.setting_set("vmg_" .. flag, str_default) -- write to minetest.conf if write_to_config is enabled (usually disabled)
end
vmg.settings:set(flag, str_default)
return default, false
vmg.settings:set(flag, str_default) -- write to vmg.conf
return default, false -- return default value
end
end
end
function vmg.define(flag, default, write_to_config)
local typeval = type(default)
local typeval = type(default) -- Select function from the type of the default value
if typeval == "string" then
return define_str(flag, default, write_to_config)
elseif typeval == "number" then

View File

@ -11,6 +11,7 @@ local function can_grow(pos) -- from default mod
return true
end
-- Fir sapling growth
minetest.register_abm({
nodenames = {"valleys_mapgen:fir_sapling"},
interval = 14,
@ -26,11 +27,13 @@ minetest.register_abm({
end
})
function default.grow_tree(pos, is_apple_tree)
function default.grow_tree(pos, is_apple_tree) -- Override default function to generate VMG trees
-- individual parameters
local rand = math.random()
local height = math.floor(4 + 2.5 * rand)
local radius = 3 + rand
-- VoxelManip stuff
local leaves = minetest.get_content_id("default:leaves")
local trunk = minetest.get_content_id("default:tree")
local air = minetest.get_content_id("air")
@ -111,26 +114,26 @@ function vmg.make_tree(pos, data, area, height, radius, trunk, leaves, air, igno
if vmg.loglevel >= 3 then
print("[Valleys Mapgen] Generating tree at " .. minetest.pos_to_string(pos) .. " ...")
end
local ystride = area.ystride
local ystride = area.ystride -- Useful to get the index above
local iv = area:indexp(pos)
for i = 1, height do
for i = 1, height do -- Build the trunk
data[iv] = trunk
iv = iv + ystride
iv = iv + ystride -- increment by one node up
end
local np = {offset = 0.8, scale = 0.4, spread = {x = 8, y = 4, z = 8}, octaves = 3, persist = 0.5}
pos.y = pos.y + height - 1
vmg.make_leavesblob(pos, data, area, leaves, air, ignore, {x = radius, y = radius, z = radius}, np)
local np = {offset = 0.8, scale = 0.4, spread = {x = 8, y = 4, z = 8}, octaves = 3, persist = 0.5} -- VMG trees use a PerlinNoise to place leaves
pos.y = pos.y + height - 1 -- pos was at the sapling position. By adding height we have the first air node above the trunk, so subtract 1 to get the highest trunk node.
vmg.make_leavesblob(pos, data, area, leaves, air, ignore, {x = radius, y = radius, z = radius}, np) -- Generate leaves
end
function vmg.make_apple_tree(pos, data, area, height, radius, trunk, leaves, fruit, air, ignore)
function vmg.make_apple_tree(pos, data, area, height, radius, trunk, leaves, fruit, air, ignore) -- Same code but with apples
if vmg.loglevel >= 3 then
print("[Valleys Mapgen] Generating apple tree at " .. minetest.pos_to_string(pos) .. " ...")
end
local ystride = area.ystride
local ystride = area.ystride -- Useful to get the index above
local iv = area:indexp(pos)
for i = 1, height do
for i = 1, height do -- Build the trunk
data[iv] = trunk
iv = iv + ystride
iv = iv + ystride -- increment by one node up
end
local np = {offset = 0.8, scale = 0.4, spread = {x = 8, y = 4, z = 8}, octaves = 3, persist = 0.5}
pos.y = pos.y + height - 1
@ -141,10 +144,10 @@ local function make_jungle_root(x0, y0, z0, data, area, tree, air)
local ystride = area.ystride
local ybot = y0 - 1
for x = x0 - 1, x0 + 1 do
for z = z0 - 1, z0 + 1 do
for z = z0 - 1, z0 + 1 do -- iterate in a 3x3 square around the trunk
local iv = area:index(x, ybot, z)
for i = 0, 5 do
if data[iv] == air then
if data[iv] == air then -- find the ground level
if math.random() < 0.6 then
data[iv-ystride] = tree -- make jungle tree below
if math.random() < 0.6 then
@ -153,7 +156,7 @@ local function make_jungle_root(x0, y0, z0, data, area, tree, air)
end
break
end
iv = iv + ystride
iv = iv + ystride -- increment by one node up
end
end
end
@ -163,11 +166,11 @@ function vmg.make_jungle_tree(pos, data, area, height, radius, trunk, leaves, ai
if vmg.loglevel >= 3 then
print("[Valleys Mapgen] Generating jungle tree at " .. minetest.pos_to_string(pos) .. " ...")
end
local ystride = area.ystride
local ystride = area.ystride -- Useful to get the index above
local iv = area:indexp(pos)
for i = 1, height do
for i = 1, height do -- Build the trunk
data[iv] = trunk
iv = iv + ystride
iv = iv + ystride -- increment by one node up
end
vmg.register_after_mapgen(make_jungle_root, pos.x, pos.y, pos.z, data, area, trunk, air)
local np = {offset = 0.8, scale = 0.4, spread = {x = 8, y = 4, z = 8}, octaves = 3, persist = 0.8}
@ -179,11 +182,11 @@ function vmg.make_fir_tree(pos, data, area, height, radius, trunk, leaves, air,
if vmg.loglevel >= 3 then
print("[Valleys Mapgen] Generating fir tree at " .. minetest.pos_to_string(pos) .. " ...")
end
local ystride = area.ystride
local ystride = area.ystride -- Useful to get the index above
local iv = area:indexp(pos)
for i = 1, height do
for i = 1, height do -- Build the trunk
data[iv] = trunk
iv = iv + ystride
iv = iv + ystride -- increment by one node up
end
-- add leaves on the top (4% 0 ; 36% 1 ; 60% 2)
@ -214,9 +217,9 @@ function vmg.make_pine_tree(pos, data, area, height, radius, trunk, leaves, air,
if vmg.loglevel >= 3 then
print("[Valleys Mapgen] Generating pine tree at " .. minetest.pos_to_string(pos) .. " ...")
end
local ystride = area.ystride
local ystride = area.ystride -- Useful to get the index above
local iv = area:indexp(pos)
for i = 1, height do
for i = 1, height do -- Build the trunk
data[iv] = trunk
iv = iv + ystride
end
@ -236,9 +239,9 @@ function vmg.make_pine_tree(pos, data, area, height, radius, trunk, leaves, air,
local midradius = radius / 2
pos.y = pos.y + height - 1
vmg.make_leavesblob(pos, data, area, leaves, air, ignore, {x = radius, y = 1.5, z = radius}, np)
while pos.y >= min_height do
local angle, distance = math.random() * 2 * math.pi, math.random() * midradius
vmg.make_leavesblob(pos, data, area, leaves, air, ignore, {x = radius, y = 1.5, z = radius}, np) -- The first leavesblob at the top
while pos.y >= min_height do -- Lower leavesblobs
local angle, distance = math.random() * 2 * math.pi, math.random() * midradius -- For the pine tree, lower leavesblobs are smaller and shifted
local cos, sin = math.cos(angle) * distance, math.sin(angle) * distance
local bpos = {x = pos.x + cos, y = pos.y, z = pos.z + sin}
vmg.make_leavesblob(bpos, data, area, leaves, air, ignore, {x = midradius, y = 1.5, z = midradius}, np)
@ -247,13 +250,12 @@ function vmg.make_pine_tree(pos, data, area, height, radius, trunk, leaves, air,
end
function vmg.make_leavesblob(pos, data, area, leaves, air, ignore, radius, np, fruit_chance, fruit)
local count = 0
fruit_chance = fruit_chance or 0
np.seed = math.random(0, 16777215)
local minp = vector.subtract(pos, radius)
local maxp = vector.add(pos, radius)
local int_minp = {x = math.floor(minp.x), y = math.floor(minp.y), z = math.floor(minp.z)}
np.seed = math.random(0, 16777215) -- noise seed
local minp = vector.subtract(pos, radius) -- minimal corner of the leavesblob
local maxp = vector.add(pos, radius) -- maximal corner of the leavesblob
local int_minp = {x = math.floor(minp.x), y = math.floor(minp.y), z = math.floor(minp.z)} -- Same positions, but with integer coordinates
local int_maxp = {x = math.ceil(maxp.x), y = math.ceil(maxp.y), z = math.ceil(maxp.z)}
local length = vector.subtract(int_maxp, int_minp)
@ -261,18 +263,19 @@ function vmg.make_leavesblob(pos, data, area, leaves, air, ignore, radius, np, f
local obj = minetest.get_perlin_map(np, chulens)
local pmap = obj:get3dMap_flat(minp)
local i = 1
-- iterate for every position
-- calculate the distance from the center by the Pythagorean theorem: d = sqrt(x²+y²+z²)
for x = int_minp.x, int_maxp.x do
local xval = ((x - pos.x) / radius.x) ^ 2
local xval = ((x - pos.x) / radius.x) ^ 2 -- calculate x², y², z² separately, to avoid recalculating x² for every y or z iteration. Divided by the radius to scale it to 0…1
for y = int_minp.y, int_maxp.y do
local yval = ((y - pos.y) / radius.y) ^ 2
for z = int_minp.z, int_maxp.z do
local zval = ((z - pos.z) / radius.z) ^ 2
local dist = math.sqrt(xval + yval + zval)
local nval = pmap[i]
if nval > dist then
local dist = math.sqrt(xval + yval + zval) -- Calculate the distance
local nval = pmap[i] -- Get the noise value
if nval > dist then -- if the noise is bigger than the distance, make leaves
local iv = area:index(x, y, z)
if data[iv] == air or data[iv] == ignore then
count = count + 1
if math.random() < fruit_chance then
data[iv] = fruit
else
@ -280,7 +283,7 @@ function vmg.make_leavesblob(pos, data, area, leaves, air, ignore, radius, np, f
end
end
end
i = i + 1
i = i + 1 -- increment noise index
end
end
end