Update mapgen api to v3 from mcl5

This commit is contained in:
kay27 2022-02-18 03:48:03 +04:00
parent 229f821758
commit 54ea8ba6b1
45 changed files with 1423 additions and 1440 deletions

View File

@ -19,13 +19,21 @@ local minetest_log = minetest.log
local minetest_pos_to_string = minetest.pos_to_string local minetest_pos_to_string = minetest.pos_to_string
-- Calculate mapgen_edge_min/mapgen_edge_max -- Calculate mapgen_edge_min/mapgen_edge_max
mcl_mapgen.CS = math_max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5) mcl_mapgen.CS = math_max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
mcl_mapgen.BS = math_max(1, core.MAP_BLOCKSIZE or 16) mcl_mapgen.BS = math_max(1, core.MAP_BLOCKSIZE or 16)
mcl_mapgen.LIMIT = math_max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000) mcl_mapgen.LIMIT = math_max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_mapgen.MAX_LIMIT = math_max(1, core.MAX_MAP_GENERATION_LIMIT or 31000) -- might be set to 31000 or removed, see https://github.com/minetest/minetest/issues/10428 mcl_mapgen.MAX_LIMIT = 31000 -- MAX_MAP_GENERATION_LIMIT, https://github.com/minetest/minetest/issues/10428
mcl_mapgen.OFFSET = - math_floor(mcl_mapgen.CS / 2) mcl_mapgen.OFFSET = - math_floor(mcl_mapgen.CS / 2)
mcl_mapgen.OFFSET_NODES = mcl_mapgen.OFFSET * mcl_mapgen.BS mcl_mapgen.OFFSET_NODES = mcl_mapgen.OFFSET * mcl_mapgen.BS
mcl_mapgen.CS_NODES = mcl_mapgen.CS * mcl_mapgen.BS mcl_mapgen.CS_NODES = mcl_mapgen.CS * mcl_mapgen.BS
mcl_mapgen.LAST_BLOCK = mcl_mapgen.CS - 1
mcl_mapgen.LAST_NODE_IN_BLOCK = mcl_mapgen.BS - 1
mcl_mapgen.LAST_NODE_IN_CHUNK = mcl_mapgen.CS_NODES - 1
mcl_mapgen.HALF_CS_NODES = math_floor(mcl_mapgen.CS_NODES / 2)
mcl_mapgen.HALF_BS = math_floor(mcl_mapgen.BS / 2)
mcl_mapgen.CS_3D = mcl_mapgen.CS^3
mcl_mapgen.CHUNK_WITH_SHELL = mcl_mapgen.CS + 2
mcl_mapgen.CHUNK_WITH_SHELL_3D = mcl_mapgen.CHUNK_WITH_SHELL^3
local central_chunk_min_pos = mcl_mapgen.OFFSET * mcl_mapgen.BS local central_chunk_min_pos = mcl_mapgen.OFFSET * mcl_mapgen.BS
local central_chunk_max_pos = central_chunk_min_pos + mcl_mapgen.CS_NODES - 1 local central_chunk_max_pos = central_chunk_min_pos + mcl_mapgen.CS_NODES - 1
@ -52,13 +60,14 @@ local seed = minetest.get_mapgen_setting("seed")
mcl_mapgen.seed = seed mcl_mapgen.seed = seed
mcl_mapgen.name = minetest.get_mapgen_setting("mg_name") mcl_mapgen.name = minetest.get_mapgen_setting("mg_name")
mcl_mapgen.v6 = mcl_mapgen.name == "v6" mcl_mapgen.v6 = mcl_mapgen.name == "v6"
mcl_mapgen.superflat = mcl_mapgen.name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true" mcl_mapgen.flat = mcl_mapgen.name == "flat"
mcl_mapgen.superflat = mcl_mapgen.flat and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
mcl_mapgen.singlenode = mcl_mapgen.name == "singlenode" mcl_mapgen.singlenode = mcl_mapgen.name == "singlenode"
mcl_mapgen.normal = not mcl_mapgen.superflat and not mcl_mapgen.singlenode mcl_mapgen.normal = not mcl_mapgen.superflat and not mcl_mapgen.singlenode
local superflat, singlenode, normal = mcl_mapgen.superflat, mcl_mapgen.singlenode, mcl_mapgen.normal local flat, superflat, singlenode, normal = mcl_mapgen.flat, mcl_mapgen.superflat, mcl_mapgen.singlenode, mcl_mapgen.normal
minetest_log("action", "[mcl_mapgen] Mapgen mode: " .. (normal and "normal" or (superflat and "superflat" or "singlenode"))) minetest_log("action", "[mcl_mapgen] Mapgen mode: " .. (normal and "normal" or (superflat and "superflat" or (flat and "flat" or "singlenode"))))
---------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------
-- Generator queues -- Generator queues
local queue_unsafe_engine = {} local queue_unsafe_engine = {}
@ -76,12 +85,16 @@ local nodes_block = 0
local nodes_chunk = 0 local nodes_chunk = 0
local safe_functions = 0 local safe_functions = 0
local BS, CS = mcl_mapgen.BS, mcl_mapgen.CS -- Mapblock size (in nodes), Mapchunk size (in blocks) local BS, CS = mcl_mapgen.BS, mcl_mapgen.CS -- Mapblock size (in nodes), Mapchunk size (in blocks)
local LAST_BLOCK, LAST_NODE = CS - 1, BS - 1 -- First mapblock in chunk (node in mapblock) has number 0, last has THIS number. It's for runtime optimization local offset = mcl_mapgen.OFFSET -- Central mapchunk offset (in blocks)
local offset = mcl_mapgen.OFFSET -- Central mapchunk offset (in blocks) local CS_NODES = mcl_mapgen.CS_NODES
local CS_NODES = mcl_mapgen.CS_NODES -- 80 local LAST_BLOCK = mcl_mapgen.LAST_BLOCK
local LAST_NODE_IN_BLOCK = mcl_mapgen.LAST_NODE_IN_BLOCK
local CS_3D = CS * CS * CS local LAST_NODE_IN_CHUNK = mcl_mapgen.LAST_NODE_IN_CHUNK
local HALF_CS_NODES = mcl_mapgen.HALF_CS_NODES
local CS_3D = mcl_mapgen.CS_3D
local CHUNK_WITH_SHELL = mcl_mapgen.CHUNK_WITH_SHELL
local CHUNK_WITH_SHELL_3D = mcl_mapgen.CHUNK_WITH_SHELL_3D
local DEFAULT_ORDER = order.DEFAULT local DEFAULT_ORDER = order.DEFAULT
@ -89,21 +102,18 @@ function mcl_mapgen.register_on_generated(callback_function, order)
queue_unsafe_engine[#queue_unsafe_engine+1] = {i = order or DEFAULT_ORDER, f = callback_function} queue_unsafe_engine[#queue_unsafe_engine+1] = {i = order or DEFAULT_ORDER, f = callback_function}
table.sort(queue_unsafe_engine, function(a, b) return (a.i <= b.i) end) table.sort(queue_unsafe_engine, function(a, b) return (a.i <= b.i) end)
end end
function mcl_mapgen.register_mapgen(callback_function, order) function mcl_mapgen.register_mapgen(callback_function, order)
nodes_chunk = nodes_chunk + 1 nodes_chunk = nodes_chunk + 1
safe_functions = safe_functions + 1 safe_functions = safe_functions + 1
queue_chunks_nodes[nodes_chunk] = {i = order or DEFAULT_ORDER, f = callback_function} queue_chunks_nodes[nodes_chunk] = {i = order or DEFAULT_ORDER, f = callback_function}
table.sort(queue_chunks_nodes, function(a, b) return (a.i <= b.i) end) table.sort(queue_chunks_nodes, function(a, b) return (a.i <= b.i) end)
end end
function mcl_mapgen.register_mapgen_lvm(callback_function, order) function mcl_mapgen.register_mapgen_lvm(callback_function, order)
lvm_chunk = lvm_chunk + 1 lvm_chunk = lvm_chunk + 1
safe_functions = safe_functions + 1 safe_functions = safe_functions + 1
queue_chunks_lvm[lvm_chunk] = {i = order or DEFAULT_ORDER, f = callback_function} queue_chunks_lvm[lvm_chunk] = {i = order or DEFAULT_ORDER, f = callback_function}
table.sort(queue_chunks_lvm, function(a, b) return (a.i <= b.i) end) table.sort(queue_chunks_lvm, function(a, b) return (a.i <= b.i) end)
end end
function mcl_mapgen.register_mapgen_block(callback_function, order) function mcl_mapgen.register_mapgen_block(callback_function, order)
block = block + 1 block = block + 1
nodes_block = nodes_block + 1 nodes_block = nodes_block + 1
@ -111,7 +121,6 @@ function mcl_mapgen.register_mapgen_block(callback_function, order)
queue_blocks_nodes[nodes_block] = {i = order or DEFAULT_ORDER, f = callback_function} queue_blocks_nodes[nodes_block] = {i = order or DEFAULT_ORDER, f = callback_function}
table.sort(queue_blocks_nodes, function(a, b) return (a.i <= b.i) end) table.sort(queue_blocks_nodes, function(a, b) return (a.i <= b.i) end)
end end
function mcl_mapgen.register_mapgen_block_lvm(callback_function, order) function mcl_mapgen.register_mapgen_block_lvm(callback_function, order)
block = block + 1 block = block + 1
queue_blocks_lvm_counter = queue_blocks_lvm_counter + 1 queue_blocks_lvm_counter = queue_blocks_lvm_counter + 1
@ -120,25 +129,76 @@ function mcl_mapgen.register_mapgen_block_lvm(callback_function, order)
table.sort(queue_blocks_lvm, function(a, b) return (a.order <= b.order) end) table.sort(queue_blocks_lvm, function(a, b) return (a.order <= b.order) end)
end end
local storage = minetest.get_mod_storage()
local blocks = minetest.deserialize(storage:get_string("mapgen_blocks") or "return {}") or {}
local chunks = minetest.deserialize(storage:get_string("mapgen_chunks") or "return {}") or {}
minetest.register_on_shutdown(function()
storage:set_string("mapgen_chunks", minetest.serialize(chunks))
storage:set_string("mapgen_blocks", minetest.serialize(blocks))
end)
local vm_context -- here will be many references and flags, like: param2, light_data, heightmap, biomemap, heatmap, humiditymap, gennotify, write_lvm, write_param2, shadow local vm_context -- here will be many references and flags, like: param2, light_data, heightmap, biomemap, heatmap, humiditymap, gennotify, write_lvm, write_param2, shadow
local data, param2_data, light, area local data, param2_data, light, area
local current_blocks = {}
local current_chunks = {}
local lvm_buffer, lvm_param2_buffer, lvm_light_buffer = {}, {}, {} -- Static buffer pointers local lvm_buffer, lvm_param2_buffer, lvm_light_buffer = {}, {}, {} -- Static buffer pointers
local all_blocks_in_chunk = {}
for x = -1, LAST_BLOCK+1 do
for y = -1, LAST_BLOCK+1 do
for z = -1, LAST_BLOCK+1 do
all_blocks_in_chunk[CHUNK_WITH_SHELL * (CHUNK_WITH_SHELL * y + z) + x] = vector.new(x, y, z)
end
end
end
local chunk_scan_range = {
[-CS_NODES] = {-1 , -1 },
[ 0 ] = {-1 , LAST_BLOCK+1},
[ CS_NODES] = {LAST_BLOCK+1, LAST_BLOCK+1},
}
local function is_chunk_finished(minp)
local center = vector.add(minp, HALF_CS_NODES)
for check_x = center.x - CS_NODES, center.x + CS_NODES, CS_NODES do
for check_y = center.y - CS_NODES, center.y + CS_NODES, CS_NODES do
for check_z = center.z - CS_NODES, center.z + CS_NODES, CS_NODES do
local pos = vector.new(check_x, check_y, check_z)
if pos ~= center then
minetest_get_voxel_manip():read_from_map(pos, pos)
local node = minetest_get_node(pos)
if node.name == "ignore" then
return
end
end
end
end
end
return true
end
local function uint32_t(v)
if v >= 0 then
return v % 0x100000000
end
return 0x100000000 - (math.abs(v) % 0x100000000)
end
local function get_block_seed(pos, current_seed)
local current_seed = current_seed or uint32_t(tonumber(seed))
return uint32_t(uint32_t(23 * pos.x) + uint32_t(42123 * pos.y) + uint32_t(38134234 * pos.z) + current_seed)
end
local function get_block_seed2(pos, current_seed)
local current_seed = current_seed or uint32_t(tonumber(seed))
local n = uint32_t(uint32_t(1619 * pos.x) + uint32_t(31337 * pos.y) + uint32_t(52591 * pos.z) + uint32_t(1013 * current_seed))
n = bit.bxor(bit.rshift(n, 13), n)
local seed = uint32_t((n * uint32_t(n * n * 60493 + 19990303) + 1376312589))
return seed
end
local function get_block_seed3(pos, current_seed)
local current_seed = uint32_t(current_seed or uint32_t(tonumber(seed)))
local x = uint32_t((pos.x + 32768) * 13)
local y = uint32_t((pos.y + 32767) * 13873)
local z = uint32_t((pos.z + 76705) * 115249)
local seed = uint32_t(bit.bxor(current_seed, x, y, z))
return seed
end
minetest.register_on_generated(function(minp, maxp, chunkseed) minetest.register_on_generated(function(minp, maxp, chunkseed)
local minp, maxp, chunkseed = minp, maxp, chunkseed local minp, maxp, chunkseed = minp, maxp, chunkseed
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
minetest_log("warning", "[mcl_mapgen] New_chunk=" .. minetest_pos_to_string(minp) .. "..." .. minetest_pos_to_string(maxp) .. ", shell=" .. minetest_pos_to_string(emin) .. "..." .. minetest_pos_to_string(emax) .. ", chunkseed=" .. tostring(chunkseed))
data = vm:get_data(lvm_buffer) data = vm:get_data(lvm_buffer)
area = VoxelArea:new({MinEdge=emin, MaxEdge=emax}) area = VoxelArea:new({MinEdge=emin, MaxEdge=emax})
vm_context = { vm_context = {
@ -157,116 +217,71 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
chunkseed = chunkseed, chunkseed = chunkseed,
} }
local current_blocks = {}
local current_chunks = {}
if safe_functions > 0 then if safe_functions > 0 then
local x0, y0, z0 = minp.x, minp.y, minp.z local ready_blocks = table.copy(all_blocks_in_chunk)
local bx0, by0, bz0 = math_floor(x0/BS), math_floor(y0/BS), math_floor(z0/BS) local p0 = vector.new(minp)
local bx1, by1, bz1 = bx0 + LAST_BLOCK, by0 + LAST_BLOCK, bz0 + LAST_BLOCK -- only for entire chunk check local center = vector.add(p0, HALF_CS_NODES)
for x = -CS_NODES, CS_NODES, CS_NODES do
-- Keep `chunkseed` in `chunks[cx][cy][cz].seed` for further safe usage: for y = -CS_NODES, CS_NODES, CS_NODES do
local cx0, cy0, cz0 = math_floor((bx0-offset)/CS), math_floor((by0-offset)/CS), math_floor((bz0-offset)/CS) for z = -CS_NODES, CS_NODES, CS_NODES do
if not chunks[cx0] then chunks[cx0] = {} end if x ~= 0 or y ~= 0 or z ~= 0 then
if not chunks[cx0][cy0] then chunks[cx0][cy0] = {} end local offset = vector.new(x, y, z)
if not chunks[cx0][cy0][cz0] then local pos = center + offset
chunks[cx0][cy0][cz0] = {seed = chunkseed, counter = 0} minetest_get_voxel_manip():read_from_map(pos, pos)
else local node = minetest_get_node(pos)
chunks[cx0][cy0][cz0].seed = chunkseed local is_generated = node.name ~= "ignore"
end if is_generated then
local adjacent_chunk_pos = p0 + offset
local x1, y1, z1, x2, y2, z2 = emin.x, emin.y, emin.z, emax.x, emax.y, emax.z if is_chunk_finished(adjacent_chunk_pos) then
local x, y, z = x1, y1, z1 -- iterate 7x7x7 mapchunk, {x,y,z} - first node pos. of mapblock current_chunks[#current_chunks + 1] = adjacent_chunk_pos
local bx, by, bz -- block coords (in blocs) end
local box, boy, boz -- block offsets in chunks (in blocks)
while x < x2 do
bx = math_floor(x/BS)
local block_pos_offset_removed = bx - offset
local cx = math_floor(block_pos_offset_removed / CS)
box = block_pos_offset_removed % CS
if not blocks[bx] then blocks[bx]={} end
-- We don't know how many calls, including this one, will overwrite this block content!
-- Start calculating it with `total_mapgen_block_writes_through_x` variable.
-- It can be `8 or less`, if we (speaking of `x` axis) are on chunk edge now,
-- or it can be `4 or less` - if we are in the middle of the chunk by `x` axis:
local total_mapgen_block_writes_through_x = (box > 0 and box < LAST_BLOCK) and 4 or 8
while y < y2 do
by = math_floor(y/BS)
block_pos_offset_removed = by - offset
local cy = math_floor(block_pos_offset_removed / CS)
boy = block_pos_offset_removed % CS
if not blocks[bx][by] then blocks[bx][by]={} end
-- Here we just divide `total_mapgen_block_writes_through_x` by 2,
-- if we are (speaking of `y` axis now) in the middle of the chunk now.
-- Or we don't divide it, if not.
-- So, basing on `total_mapgen_block_writes_through_x`,
--- we calculate `total_mapgen_block_writes_through_y` this way:
local total_mapgen_block_writes_through_y = (boy > 0 and boy < LAST_BLOCK) and math_floor(total_mapgen_block_writes_through_x / 2) or total_mapgen_block_writes_through_x
while z < z2 do
bz = math_floor(z/BS)
block_pos_offset_removed = bz - offset
local cz = math_floor(block_pos_offset_removed / CS)
boz = block_pos_offset_removed % CS
-- Now we do absolutely the same for `z` axis, basing on our previous result
-- from `total_mapgen_block_writes_through_y` variable.
-- And our final result is in `total_mapgen_block_writes`.
-- It can be still 8, derived from `x` calculation, but it can be less!
-- It can be even 1, if we are in safe 3x3x3 area of mapchunk:
local total_mapgen_block_writes = (boz > 0 and boz < LAST_BLOCK) and math_floor(total_mapgen_block_writes_through_y / 2) or total_mapgen_block_writes_through_y
-- Get current number of writes from the table, or just set it to 1, if accessing first time:
local current_mapgen_block_writes = blocks[bx][by][bz] and (blocks[bx][by][bz] + 1) or 1
-- And compare:
if current_mapgen_block_writes == total_mapgen_block_writes then
-- this block shouldn't be overwritten anymore, no need to keep it in memory
blocks[bx][by][bz] = nil
if not chunks[cx] then chunks[cx] = {} end
if not chunks[cx][cy] then chunks[cx][cy] = {} end
if not chunks[cx][cy][cz] then
if not chunks[cx][cy][cz] then chunks[cx][cy][cz] = {counter = 1} end
else else
chunks[cx][cy][cz].counter = chunks[cx][cy][cz].counter + 1 local scan_range_x = chunk_scan_range[x]
if chunks[cx][cy][cz].counter >= CS_3D then for cut_x = scan_range_x[1], scan_range_x[2] do
current_chunks[#current_chunks+1] = { x = cx, y = cy, z = cz, s = chunks[cx][cy][cz].seed } local scan_range_y = chunk_scan_range[y]
-- this chunk shouldn't be overwritten anymore, no need to keep it in memory for cut_y = scan_range_y[1], scan_range_y[2] do
chunks[cx][cy][cz] = nil local scan_range_z = chunk_scan_range[z]
if next(chunks[cx][cy]) == nil then chunks[cx][cy] = nil end for cut_z = scan_range_z[1], scan_range_z[2] do
if next(chunks[cx]) == nil then chunks[cx] = nil end ready_blocks[CHUNK_WITH_SHELL * (CHUNK_WITH_SHELL * cut_y + cut_z) + cut_x] = nil
end
end
end end
end end
local blockseed = seed + bx * 7 + by * 243 + bz * 11931
if queue_blocks_lvm_counter > 0 then
vm_context.blockseed = blockseed
vm_context.minp, vm_context.maxp = {x=x, y=y, z=z}, {x=x+LAST_NODE, y=y+LAST_NODE, z=z+LAST_NODE}
for _, v in pairs(queue_blocks_lvm) do
v.callback_function(vm_context)
end
end
if nodes_block > 0 then
current_blocks[#current_blocks+1] = { minp = {x=x, y=y, z=z}, maxp = {x=x+LAST_NODE, y=y+LAST_NODE, z=z+LAST_NODE}, seed = blockseed }
end
else
blocks[bx][by][bz] = current_mapgen_block_writes
end end
z = z + BS
end end
if next(blocks[bx][by]) == nil then blocks[bx][by] = nil end
z = z1
y = y + BS
end end
if next(blocks[bx]) == nil then blocks[bx] = nil end end
y = y1 local number_of_blocks = 0
x = x + BS for k, offset in pairs(ready_blocks) do
if queue_blocks_lvm_counter > 0 or nodes_block > 0 then
local block_minp = p0 + vector.multiply(offset, BS)
local block_maxp = vector.add(block_minp, LAST_NODE_IN_BLOCK)
local blockseed = get_block_seed3(block_minp)
vm_context.minp, vm_context.maxp, vm_context.blockseed = block_minp, block_maxp, blockseed
-- --
-- mcl_mapgen.register_mapgen_block_lvm(function(vm_context), order_number) --
-- --
for _, v in pairs(queue_blocks_lvm) do
v.callback_function(vm_context)
end
if nodes_block > 0 then
current_blocks[#current_blocks + 1] = { minp = block_minp, maxp = block_maxp, blockseed = blockseed }
end
end
number_of_blocks = number_of_blocks + 1
end
if number_of_blocks == CHUNK_WITH_SHELL_3D then
current_chunks[#current_chunks + 1] = p0
end end
end end
if #queue_unsafe_engine > 0 then if #queue_unsafe_engine > 0 then
vm_context.minp, vm_context.maxp = minp, maxp
-- * U N S A F E --
-- mcl_mapgen.register_on_generated(function(vm_context), order_number) --
-- * U N S A F E --
for _, v in pairs(queue_unsafe_engine) do for _, v in pairs(queue_unsafe_engine) do
v.f(vm_context) v.f(vm_context)
end end
@ -280,7 +295,7 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
vm:set_light_data(light) vm:set_light_data(light)
end end
if vm_context.write or vm_context.write_param2 or vm_context.write_light then if vm_context.write or vm_context.write_param2 or vm_context.write_light then
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true) -- TODO: check boundaries vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
vm:write_to_map() vm:write_to_map()
vm:update_liquids() vm:update_liquids()
elseif vm_context.calc_lighting then elseif vm_context.calc_lighting then
@ -288,12 +303,9 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
end end
end end
for i, b in pairs(current_chunks) do for i, chunk_minp in pairs(current_chunks) do
local cx, cy, cz, seed = b.x, b.y, b.z, b.s local chunk_maxp = vector.add(chunk_minp, LAST_NODE_IN_CHUNK)
local bx, by, bz = cx * CS + offset, cy * CS + offset, cz * CS + offset local current_chunk_seed = get_block_seed3(vector.subtract(chunk_minp, BS))
local x, y, z = bx * BS, by * BS, bz * BS
local minp = {x = x, y = y, z = z}
local maxp = {x = x + CS_NODES - 1, y = y + CS_NODES - 1, z = z + CS_NODES - 1}
area = VoxelArea:new({MinEdge=minp, MaxEdge=maxp}) area = VoxelArea:new({MinEdge=minp, MaxEdge=maxp})
vm_context = { vm_context = {
data = data, data = data,
@ -303,17 +315,23 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
lvm_buffer = lvm_buffer, lvm_buffer = lvm_buffer,
lvm_param2_buffer = lvm_param2_buffer, lvm_param2_buffer = lvm_param2_buffer,
lvm_light_buffer = lvm_light_buffer, lvm_light_buffer = lvm_light_buffer,
emin = minp, emin = chunk_minp,
emax = maxp, emax = chunk_maxp,
minp = minp, minp = chunk_minp,
maxp = maxp, maxp = chunk_maxp,
chunkseed = seed, chunkseed = current_chunk_seed,
} }
-- --
-- mcl_mapgen.register_mapgen_lvm(function(vm_context), order_number) --
-- --
for _, v in pairs(queue_chunks_lvm) do for _, v in pairs(queue_chunks_lvm) do
vm_context = v.f(vm_context) vm_context = v.f(vm_context)
end end
-- --
-- mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed, vm_context), order_number) --
-- --
for _, v in pairs(queue_chunks_nodes) do for _, v in pairs(queue_chunks_nodes) do
v.f(minp, maxp, seed, vm_context) v.f(chunk_minp, chunk_maxp, current_chunk_seed, vm_context)
end end
if vm_context.write or vm_context.write_param2 or vm_context.write_light then if vm_context.write or vm_context.write_param2 or vm_context.write_light then
if vm_context.write then if vm_context.write then
@ -332,14 +350,15 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
elseif vm_context.calc_lighting then elseif vm_context.calc_lighting then
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true) vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
end end
current_chunks[i] = nil
end end
for i, b in pairs(current_blocks) do for _, b in pairs(current_blocks) do
-- --
-- mcl_mapgen.register_mapgen_block(function(minp, maxp, blockseed), order_number) --
-- --
for _, v in pairs(queue_blocks_nodes) do for _, v in pairs(queue_blocks_nodes) do
v.f(b.minp, b.maxp, b.seed) v.f(b.minp, b.maxp, b.blockseed)
end end
current_blocks[i] = nil
end end
end) end)
@ -393,16 +412,6 @@ mcl_mapgen.minecraft_height_limit = 256
mcl_mapgen.bedrock_is_rough = normal mcl_mapgen.bedrock_is_rough = normal
--[[ Realm stacking (h is for height)
- Overworld (h>=256)
- Void (h>=1000)
- Realm Barrier (h=11), to allow escaping the End
- End (h>=256)
- Void (h>=1000)
- Nether (h=128)
- Void (h>=1000)
]]
-- Overworld -- Overworld
overworld.min = -62 overworld.min = -62
if superflat then if superflat then
@ -434,14 +443,12 @@ else
nether.bedrock_top_min = nether.bedrock_top_max nether.bedrock_top_min = nether.bedrock_top_max
nether.lava_max = nether.min + 2 nether.lava_max = nether.min + 2
end end
if mcl_mapgen.name == "flat" then if superflat then
if superflat then nether.flat_floor = nether.bedrock_bottom_max + 4
nether.flat_floor = nether.bedrock_bottom_max + 4 nether.flat_ceiling = nether.bedrock_bottom_max + 52
nether.flat_ceiling = nether.bedrock_bottom_max + 52 elseif flat then
else nether.flat_floor = nether.lava_max + 4
nether.flat_floor = nether.lava_max + 4 nether.flat_ceiling = nether.lava_max + 52
nether.flat_ceiling = nether.lava_max + 52
end
end end
-- The End (surface at ca. Y = -27000) -- The End (surface at ca. Y = -27000)
@ -498,5 +505,31 @@ function mcl_mapgen.clamp_to_chunk(x, size)
end end
function mcl_mapgen.get_chunk_beginning(x) function mcl_mapgen.get_chunk_beginning(x)
return x - ((x + central_chunk_min_pos) % CS_NODES) if tonumber(x) then
return x - ((x + central_chunk_min_pos) % CS_NODES)
end
if x.x then
return {
x = mcl_mapgen.get_chunk_beginning(x.x),
y = mcl_mapgen.get_chunk_beginning(x.y),
z = mcl_mapgen.get_chunk_beginning(x.z)
}
end
end end
function mcl_mapgen.get_chunk_ending(x)
if tonumber(x) then
return mcl_mapgen.get_chunk_beginning(x) + LAST_NODE_IN_CHUNK
end
if x.x then
return {
x = mcl_mapgen.get_chunk_beginning(x.x) + LAST_NODE_IN_CHUNK,
y = mcl_mapgen.get_chunk_beginning(x.y) + LAST_NODE_IN_CHUNK,
z = mcl_mapgen.get_chunk_beginning(x.z) + LAST_NODE_IN_CHUNK
}
end
end
mcl_mapgen.get_block_seed = get_block_seed
mcl_mapgen.get_block_seed2 = get_block_seed2
mcl_mapgen.get_block_seed3 = get_block_seed3

View File

@ -0,0 +1,52 @@
# mcl_time
## by kay27 for MineClone 5
---------------------------
This mod counts time when all players sleep or some area is inactive.
It depends very much on `time_speed` configuration variable, which could be changed 'on the fly' by a chat command.
If `time_speed` set to 0, this mod logs warnings and returns zeroes.
### mcl_time.get_seconds_irl()
------------------------------
Returns: Integer value of realtime (not in-game) seconds since world creation.
Usually this value grow smoothly. But when you skip the night being in the bed, or leave some area for some time, you may experience value jumps. That's basically the idea of this mod.
### mcl_time.get_number_of_times(last_time, interval, chance)
-------------------------------------------------------------
Handy to process AMBs.
You pass `last_time` - last known value of `seconds_irl`, also ABM `interval` and ABM `chance`.
Returns:
* Integer number of how many times ABM function should be called if the area was active all the time and you didn't skip the night.
* Integer value of realtime (not in-game) seconds since world creation.
### mcl_time.touch(pos)
-----------------------
This function 'toches' node at position `pos` by writing `_t` meta variable of `seconds_irl`.
### mcl_time.get_number_of_times_at_pos(pos, interval, chance)
--------------------------------------------------------------
Much more handy to call from LBM on area load, than `mcl_time.get_number_of_times()`!
It reads meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, which then pass as first argument into `mcl_time.get_number_of_times()`.
After calling this, it also 'touches' the node at `pos` by writing `seconds_irl` into meta variable `_t`.
Returns:
* Integer number of how many times ABM function should be called if the area was active all the time and you didn't skip the night.
* Integer value of realtime (not in-game) seconds since world creation.
*Warning!* This function can return 0. So it's better not to use it for regular ABMs - use `mcl_time.get_number_of_times_at_pos_or_1()` instead.
### mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
-------------------------------------------------------------------
Much more handy to process ABMs than `mcl_time.get_number_of_times()` and `mcl_time.get_number_of_times_at_pos()`!
It just calls `mcl_time.get_number_of_times_at_pos()` but doesn't return 0, the minimum number it can return is 1,
which is the most suitable for regular ABM processing function.
Returns:
* Integer number of how many times ABM function should be called if the area was active all the time and you didn't skip the night.
* Integer value of realtime (not in-game) seconds since world creation.

140
mods/CORE/mcl_time/init.lua Normal file
View File

@ -0,0 +1,140 @@
mcl_time = {}
local time_update_interval = 2
local retry_on_fail_interval = 500
local default_time_speed = 72
local save_to_storage_interval = 600
local meta_name = "_t"
local current_time_update_interval = time_update_interval
local storage = minetest.get_mod_storage()
local seconds_irl_public = tonumber(storage:get_string("seconds_irl")) or -2
local last_save_seconds_irl = seconds_irl_public
local next_save_seconds_irl = last_save_seconds_irl + save_to_storage_interval
local previous_seconds_irl = -2
local function get_seconds_irl()
local time_speed = tonumber(minetest.settings:get("time_speed") or default_time_speed)
if time_speed < 1 then
minetest.log("warning", "[mcl_time] time_speed < 1 - please increase to make mcl_time api work (default: " .. default_time_speed .. ")")
return 0
end
local irl_multiplier = 86400 / time_speed
local day_count = minetest.get_day_count()
local timeofday = minetest.get_timeofday()
local seconds_irl
if not day_count or not timeofday then
seconds_irl = seconds_irl_public
else
local days_ig = 0.0 + day_count + timeofday
seconds_irl = days_ig * irl_multiplier
end
if previous_seconds_irl == seconds_irl then
current_time_update_interval = math.min(current_time_update_interval * 2, retry_on_fail_interval)
minetest.log("warning", "[mcl_time] Time doesn't change! seconds_irl=" .. tostring(seconds_irl)
.. ", day_count = " .. tostring(day_count) .. ", timeofday=" .. tostring(timeofday)
.. " - increasing update interval to " .. tostring(current_time_update_interval))
else
previous_seconds_irl = seconds_irl
if current_time_update_interval ~= time_update_interval then
current_time_update_interval = time_update_interval
minetest.log("action", "[mcl_time] Time is changing again: seconds_irl=" .. tostring(seconds_irl)
.. ", day_count = " .. tostring(day_count) .. ", timeofday=" .. tostring(timeofday)
.. ", update_interval=" .. tostring(current_time_update_interval))
end
end
if last_save_seconds_irl >= next_save_seconds_irl then
storage:set_string("seconds_irl", tostring(seconds_irl))
next_save_seconds_irl = seconds_irl + save_to_storage_interval
end
return seconds_irl
end
local seconds_irl_public = get_seconds_irl()
function mcl_time.get_seconds_irl()
return seconds_irl_public
end
local function time_runner()
seconds_irl_public = get_seconds_irl()
minetest.after(current_time_update_interval, time_runner)
end
function mcl_time.get_number_of_times(last_time, interval, chance)
if not last_time then return 0 end
if seconds_irl_public < 2 then return 0 end
if not interval then return 0 end
if not chance then return 0 end
if interval < 1 then return 0 end
if chance < 1 then return 0 end
local number_of_intervals = (seconds_irl_public - last_time) / interval
if number_of_intervals < 1 then return 0 end
local average_chance = (1 + chance) / 2
local number_of_times = math.floor(number_of_intervals / average_chance)
return number_of_times, seconds_irl_public
end
local get_number_of_times = mcl_time.get_number_of_times
function mcl_time.touch(pos)
local meta = minetest.get_meta(pos)
meta:set_int(meta_name, seconds_irl_public)
end
local touch = mcl_time.touch
function mcl_time.get_number_of_times_at_pos(pos, interval, chance)
if not pos then return 0 end
local meta = minetest.get_meta(pos)
local last_time = meta:get_int(meta_name)
local number_of_times = (last_time == 0) and 0 or get_number_of_times(last_time, interval, chance)
touch(pos)
return number_of_times, seconds_irl_public
end
local get_number_of_times_at_pos = mcl_time.get_number_of_times_at_pos
function mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
return math.max(get_number_of_times_at_pos(pos, interval, chance), 1), seconds_irl_public
end
function mcl_time.get_irl_seconds_passed_at_pos(pos)
if not pos then return 0 end
local meta = minetest.get_meta(pos)
local last_time = meta:get_int(meta_name)
local irl_seconds_passed = (last_time == 0) and 0 or (seconds_irl_public - last_time)
return irl_seconds_passed
end
function mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
if not pos then return 1 end
local meta = minetest.get_meta(pos)
local last_time = meta:get_int(meta_name)
local irl_seconds_passed = (last_time == 0) and 1 or (seconds_irl_public - last_time)
return irl_seconds_passed
end
function mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
if not pos then return end
local meta = minetest.get_meta(pos)
local last_time = meta:get_int(meta_name)
if last_time == 0 then return end
local delta_time = seconds_irl_public - last_time
if delta_time <= 0 then return end
return delta_time
end
time_runner()
local day_count = minetest.get_day_count()
local timeofday = minetest.get_timeofday()
minetest.log("action", "[mcl_time] time runner started, current in-real-life seconds: " .. seconds_irl_public
.. ", time_speed: " .. tostring(minetest.settings:get("time_speed"))
.. ", day_count: " .. tostring(day_count)
.. ", timeofday: " .. tostring(timeofday)
.. ", update_interval=" .. tostring(current_time_update_interval)
)

View File

@ -0,0 +1,3 @@
name = mcl_time
author = kay27
description = This mod counts time when all players sleep or some area is inactive

View File

@ -2,6 +2,10 @@ local S = minetest.get_translator(minetest.get_current_modname())
local table = table local table = table
local interval = 35
local chance = 11
local max_interval = interval * chance
minetest.register_node("mcl_nether:nether_wart_0", { minetest.register_node("mcl_nether:nether_wart_0", {
description = S("Premature Nether Wart (Stage 1)"), description = S("Premature Nether Wart (Stage 1)"),
_doc_items_longdesc = S("A premature nether wart has just recently been planted on soul sand. Nether wart slowly grows on soul sand in 4 stages (the second and third stages look identical). Although nether wart is home to the Nether, it grows in any dimension."), _doc_items_longdesc = S("A premature nether wart has just recently been planted on soul sand. Nether wart slowly grows on soul sand in 4 stages (the second and third stages look identical). Although nether wart is home to the Nether, it grows in any dimension."),
@ -148,40 +152,65 @@ minetest.register_craftitem("mcl_nether:nether_wart_item", {
local names = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"} local names = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"}
local function grow(pos, node)
local step = nil
for i, name in ipairs(names) do
if name == node.name then
step = i
break
end
end
if not step then return end
local new_node = {name = names[step + 1]}
if not new_node.name then
new_node.name = "mcl_nether:nether_wart"
end
new_node.param = node.param
new_node.param2 = node.param2
minetest.set_node(pos, new_node)
local meta = minetest.get_meta(pos)
meta:set_string("gametime", tostring(mcl_time:get_seconds_irl()))
end
minetest.register_abm({ minetest.register_abm({
label = "Nether wart growth", label = "Nether wart growth",
nodenames = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"}, nodenames = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"},
neighbors = {"group:soil_nether_wart"}, neighbors = {"group:soil_nether_wart"},
interval = 35, interval = interval,
chance = 11, chance = chance,
action = function(pos, node) action = function(pos, node)
pos.y = pos.y-1 pos.y = pos.y-1
if minetest.get_item_group(minetest.get_node(pos).name, "soil_nether_wart") == 0 then if minetest.get_item_group(minetest.get_node(pos).name, "soil_nether_wart") == 0 then
return return
end end
pos.y = pos.y+1 pos.y = pos.y+1
local step = nil
for i,name in ipairs(names) do for i = 1, mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance) do
if name == node.name then grow(pos, node)
step = i
break
end
end end
if step == nil then end
})
minetest.register_lbm({
label = "Nether wart growth update",
name = "mcl_nether:growth_warts",
nodenames = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"},
run_at_every_load = true,
action = function(pos, node)
pos.y = pos.y-1
if minetest.get_item_group(minetest.get_node(pos).name, "soil_nether_wart") == 0 then
return return
end end
local new_node = {name=names[step+1]} pos.y = pos.y+1
if new_node.name == nil then for i = 1, mcl_time.get_number_of_times_at_pos(pos, interval, chance) do
new_node.name = "mcl_nether:nether_wart" grow(pos, node)
end end
new_node.param = node.param
new_node.param2 = node.param2
minetest.set_node(pos, new_node)
end end
}) })
if minetest.get_modpath("doc") then if minetest.get_modpath("doc") then
for i=1,2 do for i=1, 2 do
doc.add_entry_alias("nodes", "mcl_nether:nether_wart_0", "nodes", "mcl_nether:nether_wart_"..i) doc.add_entry_alias("nodes", "mcl_nether:nether_wart_0", "nodes", "mcl_nether:nether_wart_"..i)
end end
end end

View File

@ -0,0 +1,50 @@
local c_dirt_with_grass_snow = minetest.get_content_id("mcl_core:dirt_with_grass_snow")
local c_top_snow = minetest.get_content_id("mcl_core:snow")
local c_snow_block = minetest.get_content_id("mcl_core:snowblock")
mcl_mapgen.register_on_generated(function(vm_context)
local minp, maxp = vm_context.minp, vm_context.maxp
local min_y, max_y = minp.y, maxp.y
if min_y > mcl_mapgen.overworld.max or max_y < mcl_mapgen.overworld.min then return end
vm_context.param2_data = vm_context.param2_data or vm:get_param2_data(vm_context.lvm_param2_buffer)
vm_context.biomemap = vm_context.biomemap or minetest.get_mapgen_object("biomemap")
local param2_data = vm_context.param2_data
local biomemap = vm_context.biomemap
local vm, data, area = vm_context.vm, vm_context.data, vm_context.area
local min_x, min_z = minp.x, minp.z
local chunksize = max_y - min_y + 1
----- Interactive block fixing section -----
----- The section to perform basic block overrides of the core mapgen generated world. -----
-- Snow and sand fixes. This code implements snow consistency
-- and fixes floating sand and cut plants.
-- A snowy grass block must be below a top snow or snow block at all times.
-- Set param2 (=color) of grass blocks.
-- Clear snowy grass blocks without snow above to ensure consistency.
local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:dirt_with_grass", "mcl_core:dirt_with_grass_snow"})
for n=1, #nodes do
local pos = nodes[n]
local x, y, z = pos.x, pos.y, pos.z
local p_pos = area:index(x, y, z)
local p_pos_above = area:index(x, y + 1, z)
local biomemap_offset = (z - min_z) * chunksize + x - min_x + 1
local biome_id = biomemap[biomemap_offset]
local biome_name = minetest.get_biome_name(biome_id)
if biome_name then
local biome = minetest.registered_biomes[biome_name]
if biome and biome._mcl_biome_type then
param2_data[p_pos] = biome._mcl_palette_index
vm_context.write_param2 = true
end
end
if data[p_pos] == c_dirt_with_grass_snow and p_pos_above and data[p_pos_above] ~= c_top_snow and data[p_pos_above] ~= c_snow_block then
data[p_pos] = c_dirt_with_grass
vm_context.write = true
end
end
end, 999999999)

View File

@ -56,19 +56,14 @@ local mg_name = mcl_mapgen.name
local superflat = mcl_mapgen.superflat local superflat = mcl_mapgen.superflat
local v6 = mcl_mapgen.v6 local v6 = mcl_mapgen.v6
local singlenode = mcl_mapgen.singlenode local singlenode = mcl_mapgen.singlenode
local flat = mcl_mapgen.flat
-- Content IDs -- Content IDs
local c_bedrock = minetest.get_content_id("mcl_core:bedrock") local c_bedrock = minetest.get_content_id("mcl_core:bedrock")
local c_obsidian = minetest.get_content_id("mcl_core:obsidian")
local c_stone = minetest.get_content_id("mcl_core:stone")
local c_dirt = minetest.get_content_id("mcl_core:dirt") local c_dirt = minetest.get_content_id("mcl_core:dirt")
local c_dirt_with_grass = minetest.get_content_id("mcl_core:dirt_with_grass") local c_dirt_with_grass = minetest.get_content_id("mcl_core:dirt_with_grass")
local c_dirt_with_grass_snow = minetest.get_content_id("mcl_core:dirt_with_grass_snow")
local c_sand = minetest.get_content_id("mcl_core:sand")
--local c_sandstone = minetest.get_content_id("mcl_core:sandstone")
local c_void = minetest.get_content_id("mcl_core:void") local c_void = minetest.get_content_id("mcl_core:void")
local c_lava = minetest.get_content_id("mcl_core:lava_source") local c_lava = minetest.get_content_id("mcl_core:lava_source")
local c_water = minetest.get_content_id("mcl_core:water_source")
local c_nether = nil local c_nether = nil
if minetest.get_modpath("mcl_nether") then if minetest.get_modpath("mcl_nether") then
@ -79,12 +74,7 @@ if minetest.get_modpath("mcl_nether") then
} }
end end
--local c_end_stone = minetest.get_content_id("mcl_end:end_stone")
local c_realm_barrier = minetest.get_content_id("mcl_core:realm_barrier") local c_realm_barrier = minetest.get_content_id("mcl_core:realm_barrier")
local c_top_snow = minetest.get_content_id("mcl_core:snow")
local c_snow_block = minetest.get_content_id("mcl_core:snowblock")
local c_clay = minetest.get_content_id("mcl_core:clay")
--local c_jungletree = minetest.get_content_id("mcl_core:jungletree")
local c_air = minetest.CONTENT_AIR local c_air = minetest.CONTENT_AIR
-- --
@ -1143,166 +1133,6 @@ if string.len(mg_flags_str) > 0 then
end end
minetest.set_mapgen_setting("mg_flags", mg_flags_str, true) minetest.set_mapgen_setting("mg_flags", mg_flags_str, true)
-- Takes an index of a biomemap table (from minetest.get_mapgen_object),
-- minp and maxp (from an on_generated callback) and returns the real world coordinates
-- as X, Z.
-- Inverse function of xz_to_biomemap
--[[local function biomemap_to_xz(index, minp, maxp)
local xwidth = maxp.x - minp.x + 1
local zwidth = maxp.z - minp.z + 1
local x = ((index-1) % xwidth) + minp.x
local z = ((index-1) / zwidth) + minp.z
return x, z
end]]
local dragon_spawn_pos = false
local dragon_spawned, portal_generated = false, false
local function spawn_ender_dragon()
local obj = minetest.add_entity(dragon_spawn_pos, "mobs_mc:enderdragon")
if not obj then return false end
local dragon_entity = obj:get_luaentity()
dragon_entity._initial = true
dragon_entity._portal_pos = pos
return obj
end
local function try_to_spawn_ender_dragon()
if spawn_ender_dragon() then
dragon_spawned = true
return
end
minetest.after(2, try_to_spawn_ender_dragon)
minetest.log("warning", "[mcl_mapgen_core] WARNING! Ender dragon doesn't want to spawn at "..minetest.pos_to_string(dragon_spawn_pos))
end
if portal_generated and not dragon_spawned then
minetest.after(10, try_to_spawn_ender_dragon)
end
function mcl_mapgen_core.generate_end_exit_portal(pos)
if dragon_spawn_pos then return false end
dragon_spawn_pos = vector.add(pos, vector.new(3, 11, 3))
mcl_structures.call_struct(pos, "end_exit_portal", nil, nil, function()
minetest.after(2, function()
minetest.emerge_area(vector.subtract(dragon_spawn_pos, {x = 64, y = 12, z = 5}), vector.add(dragon_spawn_pos, {x = 3, y = 3, z = 5}), function(blockpos, action, calls_remaining, param)
if calls_remaining > 0 then return end
minetest.after(2, try_to_spawn_ender_dragon)
end)
end)
end)
portal_generated = true
end
-- Generate mushrooms in caves manually.
-- Minetest's API does not support decorations in caves yet. :-(
local function generate_underground_mushrooms(minp, maxp, seed)
if not mcl_mushrooms then return end
local pr_shroom = PseudoRandom(seed-24359)
-- Generate rare underground mushrooms
-- TODO: Make them appear in groups, use Perlin noise
local min, max = mcl_mapgen.overworld.lava_max + 4, 0
if minp.y > max or maxp.y < min then
return
end
local bpos
local stone = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_core:stone", "mcl_core:dirt", "mcl_core:mycelium", "mcl_core:podzol", "mcl_core:andesite", "mcl_core:diorite", "mcl_core:granite", "mcl_core:stone_with_coal", "mcl_core:stone_with_iron", "mcl_core:stone_with_gold"})
for n = 1, #stone do
bpos = {x = stone[n].x, y = stone[n].y + 1, z = stone[n].z }
local l = minetest.get_node_light(bpos, 0.5)
if bpos.y >= min and bpos.y <= max and l and l <= 12 and pr_shroom:next(1,1000) < 4 then
if pr_shroom:next(1,2) == 1 then
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end
end
end
end
local nether_wart_chance
if v6 then
nether_wart_chance = 85
else
nether_wart_chance = 170
end
-- Generate Nether decorations manually: Eternal fire, mushrooms, nether wart
-- Minetest's API does not support decorations in caves yet. :-(
local function generate_nether_decorations(minp, maxp, seed)
if c_nether == nil then
return
end
local pr_nether = PseudoRandom(seed+667)
if minp.y > mcl_mapgen.nether.max or maxp.y < mcl_mapgen.nether.min then
return
end
minetest.log("action", "[mcl_mapgen_core] Nether decorations " .. minetest.pos_to_string(minp) .. " ... " .. minetest.pos_to_string(maxp))
-- TODO: Generate everything based on Perlin noise instead of PseudoRandom
local bpos
local rack = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_nether:netherrack"})
local magma = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_nether:magma"})
local ssand = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_nether:soul_sand"})
-- Helper function to spawn “fake” decoration
local function special_deco(nodes, spawn_func)
for n = 1, #nodes do
bpos = {x = nodes[n].x, y = nodes[n].y + 1, z = nodes[n].z }
spawn_func(bpos)
end
end
-- Eternal fire on netherrack
special_deco(rack, function(bpos)
-- Eternal fire on netherrack
if pr_nether:next(1,100) <= 3 then
minetest.set_node(bpos, {name = "mcl_fire:eternal_fire"})
end
end)
-- Eternal fire on magma cubes
special_deco(magma, function(bpos)
if pr_nether:next(1,150) == 1 then
minetest.set_node(bpos, {name = "mcl_fire:eternal_fire"})
end
end)
-- Mushrooms on netherrack
-- Note: Spawned *after* the fire because of light level checks
if mcl_mushrooms then
special_deco(rack, function(bpos)
local l = minetest.get_node_light(bpos, 0.5)
if bpos.y > mcl_mapgen.nether.lava_max + 6 and l and l <= 12 and pr_nether:next(1,1000) <= 4 then
-- TODO: Make mushrooms appear in groups, use Perlin noise
if pr_nether:next(1,2) == 1 then
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end
end
end)
end
-- Nether wart on soul sand
-- TODO: Spawn in Nether fortresses
special_deco(ssand, function(bpos)
if pr_nether:next(1, nether_wart_chance) == 1 then
minetest.set_node(bpos, {name = "mcl_nether:nether_wart"})
end
end)
end
-- Generate basic layer-based nodes: void, bedrock, realm barrier, lava seas, etc. -- Generate basic layer-based nodes: void, bedrock, realm barrier, lava seas, etc.
-- Also perform some basic node replacements. -- Also perform some basic node replacements.
@ -1353,267 +1183,96 @@ end
-- lvm_used: Set to true if any node in this on_generated has been set before. -- lvm_used: Set to true if any node in this on_generated has been set before.
-- --
-- returns true if any node was set and lvm_used otherwise -- returns true if any node was set and lvm_used otherwise
local function set_layers(data, area, content_id, check, min, max, minp, maxp, lvm_used, pr) local function set_layers(vm_context, pr, min, max, content_id, check)
local minp, maxp, data, area = vm_context.minp, vm_context.maxp, vm_context.data, vm_context.area
if (maxp.y >= min and minp.y <= max) then if (maxp.y >= min and minp.y <= max) then
for y = math.max(min, minp.y), math.min(max, maxp.y) do for y = math.max(min, minp.y), math.min(max, maxp.y) do
for x = minp.x, maxp.x do for x = minp.x, maxp.x do
for z = minp.z, maxp.z do for z = minp.z, maxp.z do
local p_pos = area:index(x, y, z) local p_pos = vm_context.area:index(x, y, z)
if check then if check then
if type(check) == "function" and check({x=x,y=y,z=z}, data[p_pos], pr) then if type(check) == "function" and check({x=x,y=y,z=z}, data[p_pos], pr) then
data[p_pos] = content_id data[p_pos] = content_id
lvm_used = true vm_context.write = true
elseif check == data[p_pos] then elseif check == data[p_pos] then
data[p_pos] = content_id data[p_pos] = content_id
lvm_used = true vm_context.write = true
end end
else else
data[p_pos] = content_id vm_context.data[p_pos] = content_id
lvm_used = true vm_context.write = true
end end
end end
end end
end end
end end
return lvm_used
end end
-- Below the bedrock, generate air/void ---- Generate layers of air, void, etc
local function basic_safe(vm_context) local air_layers = {
local vm, data, emin, emax, area, minp, maxp, chunkseed, blockseed = vm_context.vm, vm_context.data, vm_context.emin, vm_context.emax, vm_context.area, vm_context.minp, vm_context.maxp, vm_context.chunkseed, vm_context.blockseed {mcl_mapgen.nether.max + 1, mcl_mapgen.nether.max + 128} -- on Nether Roof
}
if flat then
air_layers[#air_layers + 1] = {mcl_mapgen.nether.flat_floor, mcl_mapgen.nether.flat_ceiling} -- Flat Nether
end
-- Realm barrier between the Overworld void and the End
local barrier_min = mcl_mapgen.realm_barrier_overworld_end_min
local barrier_max = mcl_mapgen.realm_barrier_overworld_end_max
local void_layers = {
{mcl_mapgen.EDGE_MIN , mcl_mapgen.nether.min - 1 }, -- below Nether
{mcl_mapgen.nether.max + 129, mcl_mapgen.end_.min - 1 }, -- below End (above Nether)
{mcl_mapgen.end_.max + 1 , barrier_min - 1 }, -- below Realm Barrier, above End
{barrier_max + 1 , mcl_mapgen.overworld.min - 1}, -- below Overworld, above Realm Barrier
}
local bedrock_layers = {}
if not singlenode then
bedrock_layers = {
{mcl_mapgen.overworld.bedrock_min , mcl_mapgen.overworld.bedrock_max },
{mcl_mapgen.nether.bedrock_bottom_min, mcl_mapgen.nether.bedrock_bottom_max},
{mcl_mapgen.nether.bedrock_top_min , mcl_mapgen.nether.bedrock_top_max },
}
end
mcl_mapgen.register_mapgen_block_lvm(function(vm_context)
local vm, data, area, minp, maxp, chunkseed, blockseed = vm_context.vm, vm_context.data, vm_context.area, vm_context.minp, vm_context.maxp, vm_context.chunkseed, vm_context.blockseed
vm_context.param2_data = vm_context.param2_data or vm:get_param2_data(vm_context.lvm_param2_buffer) vm_context.param2_data = vm_context.param2_data or vm:get_param2_data(vm_context.lvm_param2_buffer)
local param2_data = vm_context.param2_data local param2_data = vm_context.param2_data
local lvm_used = false
local pr = PseudoRandom(blockseed) local pr = PseudoRandom(blockseed)
for _, layer in pairs(void_layers) do
-- The Void below the Nether: set_layers(vm_context, pr, layer[1], layer[2], c_void)
lvm_used = set_layers(data, area, c_void , nil, mcl_mapgen.EDGE_MIN , mcl_mapgen.nether.min -1, minp, maxp, lvm_used, pr) end
for _, layer in pairs(air_layers) do
-- [[ THE NETHER: mcl_mapgen.nether.min mcl_mapgen.nether.max ]] set_layers(vm_context, pr, layer[1], layer[2], c_air)
end
-- The Air on the Nether roof, https://git.minetest.land/MineClone2/MineClone2/issues/1186 set_layers(vm_context, pr, barrier_min, barrier_max, c_realm_barrier)
lvm_used = set_layers(data, area, c_air , nil, mcl_mapgen.nether.max +1, mcl_mapgen.nether.max + 128 , minp, maxp, lvm_used, pr) for _, layer in pairs(bedrock_layers) do
-- The Void above the Nether below the End: set_layers(vm_context, pr, layer[1], layer[2], c_bedrock, bedrock_check)
lvm_used = set_layers(data, area, c_void , nil, mcl_mapgen.nether.max + 128 +1, mcl_mapgen.end_.min -1, minp, maxp, lvm_used, pr) end
-- [[ THE END: mcl_mapgen.end_.min mcl_mapgen.end_.max ]]
-- The Void above the End below the Realm barrier:
lvm_used = set_layers(data, area, c_void , nil, mcl_mapgen.end_.max +1, mcl_mapgen.realm_barrier_overworld_end_min-1, minp, maxp, lvm_used, pr)
-- Realm barrier between the Overworld void and the End
lvm_used = set_layers(data, area, c_realm_barrier, nil, mcl_mapgen.realm_barrier_overworld_end_min , mcl_mapgen.realm_barrier_overworld_end_max , minp, maxp, lvm_used, pr)
-- The Void above Realm barrier below the Overworld:
lvm_used = set_layers(data, area, c_void , nil, mcl_mapgen.realm_barrier_overworld_end_max+1, mcl_mapgen.overworld.min -1, minp, maxp, lvm_used, pr)
if not singlenode then if not singlenode then
-- Bedrock
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_mapgen.overworld.bedrock_min, mcl_mapgen.overworld.bedrock_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_mapgen.nether.bedrock_bottom_min, mcl_mapgen.nether.bedrock_bottom_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_mapgen.nether.bedrock_top_min, mcl_mapgen.nether.bedrock_top_max, minp, maxp, lvm_used, pr)
-- Flat Nether
if mg_name == "flat" then
lvm_used = set_layers(data, area, c_air, nil, mcl_mapgen.nether.flat_floor, mcl_mapgen.nether.flat_ceiling, minp, maxp, lvm_used, pr)
end
-- Big lava seas by replacing air below a certain height -- Big lava seas by replacing air below a certain height
if mcl_mapgen.lava then if mcl_mapgen.lava then
lvm_used = set_layers(data, area, c_lava, c_air, mcl_mapgen.overworld.min, mcl_mapgen.overworld.lava_max, minp, maxp, lvm_used, pr) set_layers(vm_context, pr, mcl_mapgen.overworld.min, mcl_mapgen.overworld.lava_max, c_lava, c_air)
if c_nether then if c_nether then
lvm_used = set_layers(data, area, c_nether.lava, c_air, mcl_mapgen.nether.min, mcl_mapgen.nether.lava_max, minp, maxp, lvm_used, pr) set_layers(vm_context, pr, mcl_mapgen.nether.min, mcl_mapgen.nether.lava_max, c_nether.lava, c_air)
end
end
vm_context.biomemap = vm_context.biomemap or minetest.get_mapgen_object("biomemap")
local biomemap = vm_context.biomemap
----- Interactive block fixing section -----
----- The section to perform basic block overrides of the core mapgen generated world. -----
-- Snow and sand fixes. This code implements snow consistency
-- and fixes floating sand and cut plants.
-- A snowy grass block must be below a top snow or snow block at all times.
if minp.y <= mcl_mapgen.overworld.max and maxp.y >= mcl_mapgen.overworld.min then
-- v6 mapgen:
if v6 then
--[[ Remove broken double plants caused by v6 weirdness.
v6 might break the bottom part of double plants because of how it works.
There are 3 possibilities:
1) Jungle: Top part is placed on top of a jungle tree or fern (=v6 jungle grass).
This is because the schematic might be placed even if some nodes of it
could not be placed because the destination was already occupied.
TODO: A better fix for this would be if schematics could abort placement
altogether if ANY of their nodes could not be placed.
2) Cavegen: Removes the bottom part, the upper part floats
3) Mudflow: Same as 2) ]]
local plants = minetest.find_nodes_in_area(minp, maxp, "group:double_plant")
for n = 1, #plants do
local node = vm:get_node_at(plants[n])
local is_top = minetest.get_item_group(node.name, "double_plant") == 2
if is_top then
local p_pos = area:index(plants[n].x, plants[n].y-1, plants[n].z)
if p_pos then
node = vm:get_node_at({x=plants[n].x, y=plants[n].y-1, z=plants[n].z})
local is_bottom = minetest.get_item_group(node.name, "double_plant") == 1
if not is_bottom then
p_pos = area:index(plants[n].x, plants[n].y, plants[n].z)
data[p_pos] = c_air
lvm_used = true
end
end
end
end
-- Non-v6 mapgens:
else
-- Set param2 (=color) of grass blocks.
-- Clear snowy grass blocks without snow above to ensure consistency.
local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:dirt_with_grass", "mcl_core:dirt_with_grass_snow"})
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}})
for n=1, #nodes do
local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z)
local p_pos_above = area:index(n.x, n.y+1, n.z)
--local p_pos_below = area:index(n.x, n.y-1, n.z)
local b_pos = aream:index(n.x, 0, n.z)
local bn = minetest.get_biome_name(biomemap[b_pos])
if bn then
local biome = minetest.registered_biomes[bn]
if biome and biome._mcl_biome_type then
param2_data[p_pos] = biome._mcl_palette_index
vm_context.write_param2 = true
end
end
if data[p_pos] == c_dirt_with_grass_snow and p_pos_above and data[p_pos_above] ~= c_top_snow and data[p_pos_above] ~= c_snow_block then
data[p_pos] = c_dirt_with_grass
lvm_used = true
end
end
end
-- Nether block fixes:
-- * Replace water with Nether lava.
-- * Replace stone, sand dirt in v6 so the Nether works in v6.
elseif minp.y <= mcl_mapgen.nether.max and maxp.y >= mcl_mapgen.nether.min then
-- elseif emin.y <= mcl_mapgen.nether.max and emax.y >= mcl_mapgen.nether.min then
if c_nether then
if v6 then
-- local nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
for n=1, #nodes do
local p_pos = area:index(nodes[n].x, nodes[n].y, nodes[n].z)
if data[p_pos] == c_water then
data[p_pos] = c_nether.lava
lvm_used = true
elseif data[p_pos] == c_stone then
data[p_pos] = c_netherrack
lvm_used = true
elseif data[p_pos] == c_sand or data[p_pos] == c_dirt then
data[p_pos] = c_soul_sand
lvm_used = true
end
end
else
-- local nodes = minetest.find_nodes_in_area(emin, emax, {"group:water"})
local nodes = minetest.find_nodes_in_area(minp, maxp, {"group:water"})
for _, n in pairs(nodes) do
data[area:index(n.x, n.y, n.z)] = c_nether.lava
end
end
end
-- End block fixes:
-- * Replace water with end stone or air (depending on height).
-- * Remove stone, sand, dirt in v6 so our End map generator works in v6.
-- * Generate spawn platform (End portal destination)
elseif minp.y <= mcl_mapgen.end_.max and maxp.y >= mcl_mapgen.end_.min then
local nodes
if v6 then
nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
-- nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
else
nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source"})
-- nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source"})
end
if #nodes > 0 then
lvm_used = true
for _,n in pairs(nodes) do
data[area:index(n.x, n.y, n.z)] = c_air
end
end
-- Obsidian spawn platform
if minp.y <= mcl_mapgen.end_.platform_pos.y and maxp.y >= mcl_mapgen.end_.platform_pos.y and
minp.x <= mcl_mapgen.end_.platform_pos.x and maxp.x >= mcl_mapgen.end_.platform_pos.z and
minp.z <= mcl_mapgen.end_.platform_pos.z and maxp.z >= mcl_mapgen.end_.platform_pos.z then
--local pos1 = {x = math.max(minp.x, mcl_mapgen.end_.platform_pos.x-2), y = math.max(minp.y, mcl_mapgen.end_.platform_pos.y), z = math.max(minp.z, mcl_mapgen.end_.platform_pos.z-2)}
--local pos2 = {x = math.min(maxp.x, mcl_mapgen.end_.platform_pos.x+2), y = math.min(maxp.y, mcl_mapgen.end_.platform_pos.y+2), z = math.min(maxp.z, mcl_mapgen.end_.platform_pos.z+2)}
for x=math.max(minp.x, mcl_mapgen.end_.platform_pos.x-2), math.min(maxp.x, mcl_mapgen.end_.platform_pos.x+2) do
for z=math.max(minp.z, mcl_mapgen.end_.platform_pos.z-2), math.min(maxp.z, mcl_mapgen.end_.platform_pos.z+2) do
for y=math.max(minp.y, mcl_mapgen.end_.platform_pos.y), math.min(maxp.y, mcl_mapgen.end_.platform_pos.y+2) do
local p_pos = area:index(x, y, z)
if y == mcl_mapgen.end_.platform_pos.y then
data[p_pos] = c_obsidian
else
data[p_pos] = c_air
end
end
end
end
lvm_used = true
end end
end end
end end
end, 1)
if not singlenode then
-- Generate special decorations
generate_underground_mushrooms(minp, maxp, blockseed)
generate_nether_decorations(minp, maxp, blockseed)
end
vm_context.write = vm_context.write or lvm_used
end
mcl_mapgen.register_mapgen_block_lvm(basic_safe, 1)
local modpath = minetest.get_modpath(minetest.get_current_modname()) local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath .. "/clay.lua") dofile(modpath .. "/clay.lua")
dofile(modpath .. "/tree_decoration.lua") dofile(modpath .. "/tree_decoration.lua")
dofile(modpath .. "/nether_wart.lua")
-- Nether Roof Light: dofile(modpath .. "/light.lua")
mcl_mapgen.register_mapgen_block_lvm(function(vm_context) if v6 then
local minp = vm_context.minp dofile(modpath .. "/v6.lua")
local miny = minp.y elseif not singlenode then
if miny > mcl_mapgen.nether.max+127 then return end dofile(modpath .. "/biomes.lua")
local maxp = vm_context.maxp end
local maxy = maxp.y if not singlenode and c_nether then
if maxy <= mcl_mapgen.nether.max then return end dofile(modpath .. "/nether.lua")
local p1 = {x = minp.x, y = math.max(miny, mcl_mapgen.nether.max + 1), z = minp.z} end
local p2 = {x = maxp.x, y = math.min(maxy, mcl_mapgen.nether.max + 127), z = maxp.z}
vm_context.vm:set_lighting({day=15, night=15}, p1, p2)
vm_context.write = true
end, 999999999)
-- End Light:
mcl_mapgen.register_mapgen_block_lvm(function(vm_context)
local minp = vm_context.minp
local miny = minp.y
if miny > mcl_mapgen.end_.max then return end
local maxp = vm_context.maxp
local maxy = maxp.y
if maxy <= mcl_mapgen.end_.min then return end
local p1 = {x = minp.x, y = math.max(miny, mcl_mapgen.end_.min), z = minp.z}
local p2 = {x = maxp.x, y = math.min(maxy, mcl_mapgen.end_.max), z = maxp.z}
vm_context.vm:set_lighting({day=15, night=15}, p1, p2)
vm_context.write = true
end, 9999999999)

View File

@ -0,0 +1,41 @@
-- Nether Light:
mcl_mapgen.register_mapgen_block_lvm(function(vm_context)
local minp = vm_context.minp
local miny = minp.y
if miny > mcl_mapgen.nether.max then return end
local maxp = vm_context.maxp
local maxy = maxp.y
if maxy < mcl_mapgen.nether.min then return end
local p1 = {x = minp.x, y = math.max(miny, mcl_mapgen.nether.min), z = minp.z}
local p2 = {x = maxp.x, y = math.min(maxy, mcl_mapgen.nether.max), z = maxp.z}
vm_context.vm:set_lighting({day = 3, night = 4}, p1, p2)
vm_context.write = true
end, 999999999)
-- Nether Roof Light:
mcl_mapgen.register_mapgen_block_lvm(function(vm_context)
local minp = vm_context.minp
local miny = minp.y
if miny > mcl_mapgen.nether.max+127 then return end
local maxp = vm_context.maxp
local maxy = maxp.y
if maxy <= mcl_mapgen.nether.max then return end
local p1 = {x = minp.x, y = math.max(miny, mcl_mapgen.nether.max + 1), z = minp.z}
local p2 = {x = maxp.x, y = math.min(maxy, mcl_mapgen.nether.max + 127), z = maxp.z}
vm_context.vm:set_lighting({day = 15, night = 15}, p1, p2)
vm_context.write = true
end, 999999999)
-- End Light:
mcl_mapgen.register_mapgen_block_lvm(function(vm_context)
local minp = vm_context.minp
local miny = minp.y
if miny > mcl_mapgen.end_.max then return end
local maxp = vm_context.maxp
local maxy = maxp.y
if maxy <= mcl_mapgen.end_.min then return end
local p1 = {x = minp.x, y = math.max(miny, mcl_mapgen.end_.min), z = minp.z}
local p2 = {x = maxp.x, y = math.min(maxy, mcl_mapgen.end_.max), z = maxp.z}
vm_context.vm:set_lighting({day=15, night=15}, p1, p2)
vm_context.write = true
end, 9999999999)

View File

@ -0,0 +1,131 @@
local v6 = mcl_mapgen.v6
local mcl_mushrooms = minetest.get_modpath("mcl_mushrooms")
local c_water = minetest.get_content_id("mcl_core:water_source")
local c_stone = minetest.get_content_id("mcl_core:stone")
local c_sand = minetest.get_content_id("mcl_core:sand")
local c_soul_sand = minetest.get_content_id("mcl_nether:soul_sand")
local c_netherrack = minetest.get_content_id("mcl_nether:netherrack")
local c_nether_lava = minetest.get_content_id("mcl_nether:nether_lava_source")
-- Generate mushrooms in caves manually.
-- Minetest's API does not support decorations in caves yet. :-(
local function generate_underground_mushrooms(minp, maxp, seed)
if not mcl_mushrooms then return end
local pr_shroom = PseudoRandom(seed-24359)
-- Generate rare underground mushrooms
-- TODO: Make them appear in groups, use Perlin noise
local min, max = mcl_mapgen.overworld.lava_max + 4, 0
if minp.y > max or maxp.y < min then
return
end
local bpos
local stone = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_core:stone", "mcl_core:dirt", "mcl_core:mycelium", "mcl_core:podzol", "mcl_core:andesite", "mcl_core:diorite", "mcl_core:granite", "mcl_core:stone_with_coal", "mcl_core:stone_with_iron", "mcl_core:stone_with_gold"})
for n = 1, #stone do
bpos = {x = stone[n].x, y = stone[n].y + 1, z = stone[n].z }
local l = minetest.get_node_light(bpos, 0.5)
if bpos.y >= min and bpos.y <= max and l and l <= 12 and pr_shroom:next(1,1000) < 4 then
if pr_shroom:next(1,2) == 1 then
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end
end
end
end
-- Generate Nether decorations manually: Eternal fire, mushrooms
-- Minetest's API does not support decorations in caves yet. :-(
local function generate_nether_decorations(minp, maxp, seed)
local pr_nether = PseudoRandom(seed+667)
if minp.y > mcl_mapgen.nether.max or maxp.y < mcl_mapgen.nether.min then
return
end
minetest.log("action", "[mcl_mapgen_core] Nether decorations " .. minetest.pos_to_string(minp) .. " ... " .. minetest.pos_to_string(maxp))
-- TODO: Generate everything based on Perlin noise instead of PseudoRandom
local bpos
local rack = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_nether:netherrack"})
local magma = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_nether:magma"})
local ssand = minetest.find_nodes_in_area_under_air(minp, maxp, {"mcl_nether:soul_sand"})
-- Helper function to spawn “fake” decoration
local function special_deco(nodes, spawn_func)
for n = 1, #nodes do
bpos = {x = nodes[n].x, y = nodes[n].y + 1, z = nodes[n].z }
spawn_func(bpos)
end
end
-- Eternal fire on netherrack
special_deco(rack, function(bpos)
-- Eternal fire on netherrack
if pr_nether:next(1,100) <= 3 then
minetest.set_node(bpos, {name = "mcl_fire:eternal_fire"})
end
end)
-- Eternal fire on magma cubes
special_deco(magma, function(bpos)
if pr_nether:next(1,150) == 1 then
minetest.set_node(bpos, {name = "mcl_fire:eternal_fire"})
end
end)
-- Mushrooms on netherrack
-- Note: Spawned *after* the fire because of light level checks
if mcl_mushrooms then
special_deco(rack, function(bpos)
local l = minetest.get_node_light(bpos, 0.5)
if bpos.y > mcl_mapgen.nether.lava_max + 6 and l and l <= 12 and pr_nether:next(1,1000) <= 4 then
-- TODO: Make mushrooms appear in groups, use Perlin noise
if pr_nether:next(1,2) == 1 then
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end
end
end)
end
end
mcl_mapgen.register_mapgen(function(minp, maxp, seed, vm_context)
local min_y, max_y = minp.y, maxp.y
-- Nether block fixes:
-- * Replace water with Nether lava.
-- * Replace stone, sand dirt in v6 so the Nether works in v6.
if min_y > mcl_mapgen.nether.max or max_y < mcl_mapgen.nether.min then return end
if v6 then
local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
if #nodes < 1 then return end
vm_context.write = true
local data = vm_context.data
local area = vm_context.area
for n = 1, #nodes do
local p_pos = area:index(nodes[n].x, nodes[n].y, nodes[n].z)
if data[p_pos] == c_water then
data[p_pos] = c_nether_lava
elseif data[p_pos] == c_stone then
data[p_pos] = c_netherrack
elseif data[p_pos] == c_sand or data[p_pos] == c_dirt then
data[p_pos] = c_soul_sand
end
end
else
end
generate_underground_mushrooms(minp, maxp, seed)
generate_nether_decorations(minp, maxp, seed)
end, 1)

View File

@ -0,0 +1,57 @@
local nether_wart_chance
if mcl_mapgen.v6 then
nether_wart_chance = 85
else
nether_wart_chance = 170
end
local y_min = mcl_mapgen.nether.min
local y_max = mcl_mapgen.nether.max
local place_on = {"group:soil_nether_wart"}
local block_size = mcl_mapgen.BS
local decrease_search_area = math.min(2, math.floor(block_size/2))
local search_area_size = math.max(block_size - 2 * decrease_search_area, math.max(1, math.ceil(nether_wart_chance^(1/3))))
nether_wart_chance = math.floor(nether_wart_chance * (search_area_size^3) / (block_size^3))
local nether_wart_chance_threshold = nether_wart_chance
local minetest_swap_node = minetest.swap_node
local wart_perlin
local noise_params = {
offset = 0.4,
scale = 0.4,
spread = {x = block_size, y = block_size, z = block_size},
seed = 238742,
octaves = 1,
persist = 0.5,
}
minetest.log("action", "Nether Wart block_size=" .. block_size .. ", search_area_size=" .. search_area_size .. ", per-area nether_wart_chance=" .. nether_wart_chance)
local minetest_find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
local minetest_get_perlin = minetest.get_perlin
mcl_mapgen.register_mapgen_block(function(minp, maxp, seed)
local minp = minp
local y1 = minp.y
if y1 > y_max then return end
local maxp = maxp
local y2 = maxp.y
if y2 < y_min then return end
local p1 = {x = minp.x + decrease_search_area, y = y1 + decrease_search_area, z = minp.z + decrease_search_area}
local p2 = {x = maxp.x - decrease_search_area, y = y2 - decrease_search_area, z = maxp.z - decrease_search_area}
pos_list = minetest_find_nodes_in_area_under_air(p1, p2, place_on)
local pr = PseudoRandom(seed)
wart_perlin = wart_perlin or minetest_get_perlin(noise_params)
for i = 1, #pos_list do
local pos = pos_list[i]
if pr:next(1, nether_wart_chance) + wart_perlin:get_3d(pos) >= nether_wart_chance_threshold then
pos.y = pos.y + 1
minetest.swap_node(pos, {name = "mcl_nether:nether_wart"})
end
end
end, 999999999)

View File

@ -0,0 +1,49 @@
local c_air = minetest.CONTENT_AIR
mcl_mapgen.register_on_generated(function(vm_context)
local minp, maxp = vm_context.minp, vm_context.maxp
if minp.y <= mcl_mapgen.end_.max and maxp.y >= mcl_mapgen.end_.min then
local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
if #nodes > 0 then
for _, n in pairs(nodes) do
data[area:index(n.x, n.y, n.z)] = c_air
end
end
vm_context.write = true
return
end
if minp.y > mcl_mapgen.overworld.max or maxp.y < mcl_mapgen.overworld.min then return end
local vm, data, area = vm_context.vm, vm_context.data, vm_context.area
--[[ Remove broken double plants caused by v6 weirdness.
v6 might break the bottom part of double plants because of how it works.
There are 3 possibilities:
1) Jungle: Top part is placed on top of a jungle tree or fern (=v6 jungle grass).
This is because the schematic might be placed even if some nodes of it
could not be placed because the destination was already occupied.
TODO: A better fix for this would be if schematics could abort placement
altogether if ANY of their nodes could not be placed.
2) Cavegen: Removes the bottom part, the upper part floats
3) Mudflow: Same as 2) ]]
local plants = minetest.find_nodes_in_area(minp, maxp, "group:double_plant")
for n = 1, #plants do
local node = vm:get_node_at(plants[n])
local is_top = minetest.get_item_group(node.name, "double_plant") == 2
if is_top then
local p_pos = area:index(plants[n].x, plants[n].y-1, plants[n].z)
if p_pos then
node = vm:get_node_at({x=plants[n].x, y=plants[n].y-1, z=plants[n].z})
local is_bottom = minetest.get_item_group(node.name, "double_plant") == 1
if not is_bottom then
p_pos = area:index(plants[n].x, plants[n].y, plants[n].z)
data[p_pos] = c_air
vm_context.write = true
end
end
end
end
end, 999999999)

View File

@ -1,7 +1,10 @@
-- Check: v7 apple 2320,4,-12558
-- Check it: local chance_per_chunk = 5
-- seed 1, v7 mapgen local noise_multiplier = 1
-- /teleport 14958,8,11370 local random_offset = 12342
local struct_threshold = chance_per_chunk
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local mcl_mapgen_get_far_node = mcl_mapgen.get_far_node local mcl_mapgen_get_far_node = mcl_mapgen.get_far_node
local minetest_log = minetest.log local minetest_log = minetest.log
@ -44,8 +47,12 @@ mcl_mapgen.register_mapgen(function(minp, maxp, seed)
local y = minp.y local y = minp.y
if y ~= y_wanted then return end if y ~= y_wanted then return end
local pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_chunk)
local noise = mcl_structures_get_perlin_noise_level(minp) * noise_multiplier
if not noise or (random_number + noise) < struct_threshold then return end
local x, z = minp.x, minp.z local x, z = minp.x, minp.z
local pr = PseudoRandom(seed)
-- scan the ocean - it should be the ocean: -- scan the ocean - it should be the ocean:
for i = 1, pr:next(10, 100) do for i = 1, pr:next(10, 100) do

View File

@ -1,11 +1,13 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
-- Check: v7 apple -21539,27,2404
local chance_per_chunk = 11 local chance_per_chunk = 11
local noise_multiplier = 1 local noise_multiplier = 1
local random_offset = 999 local random_offset = 999
local scanning_ratio = 0.00003 local scanning_ratio = 0.0003
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
@ -130,7 +132,7 @@ mcl_structures.register_structure({
place_on = node_list, place_on = node_list,
flags = "all_floors", flags = "all_floors",
fill_ratio = scanning_ratio, fill_ratio = scanning_ratio,
y_min = 3, y_min = 1,
y_max = mcl_mapgen.overworld.max, y_max = mcl_mapgen.overworld.max,
height = 1, height = 1,
biomes = not mcl_mapgen.v6 and { biomes = not mcl_mapgen.v6 and {

View File

@ -1,11 +1,11 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 60 local chance_per_chunk = 20
local noise_multiplier = 1 local noise_multiplier = 1
local random_offset = 999 local random_offset = 999
local scanning_ratio = 0.00001 local scanning_ratio = 0.0002
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level

View File

@ -12,6 +12,31 @@ local p0 = {
local schematic = modpath .. "/schematics/mcl_structures_end_exit_portal.mts" local schematic = modpath .. "/schematics/mcl_structures_end_exit_portal.mts"
local dragon_spawn_pos = false
local dragon_spawned, portal_generated = false, false
local function spawn_ender_dragon()
local obj = minetest.add_entity(dragon_spawn_pos, "mobs_mc:enderdragon")
if not obj then return false end
local dragon_entity = obj:get_luaentity()
dragon_entity._initial = true
dragon_entity._portal_pos = p0
return obj
end
local function try_to_spawn_ender_dragon()
if spawn_ender_dragon() then
dragon_spawned = true
return
end
minetest.after(2, try_to_spawn_ender_dragon)
minetest.log("warning", "Ender dragon doesn't want to spawn at "..minetest.pos_to_string(dragon_spawn_pos))
end
if portal_generated and not dragon_spawned then
minetest.after(10, try_to_spawn_ender_dragon)
end
local function place(pos, rotation, pr) local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, schematic = schematic, rotation = rotation, pr = pr}) mcl_structures.place_schematic({pos = pos, schematic = schematic, rotation = rotation, pr = pr})
end end
@ -28,6 +53,10 @@ mcl_mapgen.register_mapgen(function(minp, maxp, seed, vm_context)
if minp.z > END_EXIT_PORTAL_POS_Z then return end if minp.z > END_EXIT_PORTAL_POS_Z then return end
if maxp.z < END_EXIT_PORTAL_POS_Z then return end if maxp.z < END_EXIT_PORTAL_POS_Z then return end
dragon_spawn_pos = vector.add(p0, vector.new(3, 11, 3))
portal_generated = true
try_to_spawn_ender_dragon()
local p = table.copy(p0) local p = table.copy(p0)
for y = y2, y1, -1 do for y = y2, y1, -1 do

View File

@ -4,7 +4,7 @@ local modpath = minetest.get_modpath(modname)
local chance_per_block = mcl_structures.from_16x16_to_block_inverted_chance(64) local chance_per_block = mcl_structures.from_16x16_to_block_inverted_chance(64)
local noise_multiplier = 2 local noise_multiplier = 2
local random_offset = 5 local random_offset = 5
local struct_threshold = chance_per_block - 1 local struct_threshold = chance_per_block
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local minetest_find_nodes_in_area = minetest.find_nodes_in_area local minetest_find_nodes_in_area = minetest.find_nodes_in_area
local min_y = mcl_worlds.layer_to_y(40) local min_y = mcl_worlds.layer_to_y(40)

View File

@ -3,7 +3,7 @@ local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 3 local chance_per_chunk = 3
local random_offset = 1264 local random_offset = 1264
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local noise_params = { local noise_params = {
offset = 0, offset = 0,
scale = 1, scale = 1,

View File

@ -1,11 +1,12 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
-- local chance_per_chunk = mcl_structures.from_16x16_to_chunk_inverted_chance(4400) -- Check: v7 apple -27787,31,3115
local chance_per_chunk = 100
local chance_per_chunk = 39
local noise_multiplier = 1.4 local noise_multiplier = 1.4
local random_offset = 555 local random_offset = 555
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local scanning_ratio = 0.0003 local scanning_ratio = 0.0003
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level

View File

@ -337,9 +337,7 @@ function mcl_structures.call_struct(pos, struct_style, rotation, pr, callback)
if not rotation then if not rotation then
rotation = "random" rotation = "random"
end end
if struct_style == "witch_hut" then if struct_style == "boulder" then
return mcl_structures.generate_witch_hut(pos, rotation)
elseif struct_style == "boulder" then
return mcl_structures.generate_boulder(pos, rotation, pr) return mcl_structures.generate_boulder(pos, rotation, pr)
elseif struct_style == "end_exit_portal" then elseif struct_style == "end_exit_portal" then
return mcl_structures.generate_end_exit_portal(pos, rotation, pr, callback) return mcl_structures.generate_end_exit_portal(pos, rotation, pr, callback)
@ -385,27 +383,6 @@ function mcl_structures.generate_boulder(pos, rotation, pr)
return minetest.place_schematic(newpos, path, rotation) -- don't serialize schematics for registered biome decorations, for MT 5.4.0, https://github.com/minetest/minetest/issues/10995 return minetest.place_schematic(newpos, path, rotation) -- don't serialize schematics for registered biome decorations, for MT 5.4.0, https://github.com/minetest/minetest/issues/10995
end end
local function hut_placement_callback(p1, p2, size, orientation, pr)
if not p1 or not p2 then return end
local legs = minetest.find_nodes_in_area(p1, p2, "mcl_core:tree")
for i = 1, #legs do
while minetest.get_item_group(mcl_mapgen.get_far_node({x=legs[i].x, y=legs[i].y-1, z=legs[i].z}, true, 333333).name, "water") ~= 0 do
legs[i].y = legs[i].y - 1
minetest.swap_node(legs[i], {name = "mcl_core:tree", param2 = 2})
end
end
end
function mcl_structures.generate_witch_hut(pos, rotation, pr)
local path = modpath.."/schematics/mcl_structures_witch_hut.mts"
mcl_structures.place_schematic(pos, path, rotation, nil, true, nil, hut_placement_callback, pr)
end
function mcl_structures.generate_ice_spike_large(pos, rotation)
local path = modpath.."/schematics/mcl_structures_ice_spike_large.mts"
return minetest.place_schematic(pos, path, rotation or "random", nil, false) -- don't serialize schematics for registered biome decorations, for MT 5.4.0
end
function mcl_structures.generate_end_exit_portal(pos, rot, pr, callback) function mcl_structures.generate_end_exit_portal(pos, rot, pr, callback)
local path = modpath.."/schematics/mcl_structures_end_exit_portal.mts" local path = modpath.."/schematics/mcl_structures_end_exit_portal.mts"
return mcl_structures.place_schematic(pos, path, rot or "0", {["mcl_portals:portal_end"] = "air"}, true, nil, callback) return mcl_structures.place_schematic(pos, path, rot or "0", {["mcl_portals:portal_end"] = "air"}, true, nil, callback)

View File

@ -1,10 +1,13 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 9 -- Check: v7 apple 28530,6,28070
-- Check: v7 apple -16343,24,5330
local chance_per_chunk = 30
local noise_multiplier = 1.3 local noise_multiplier = 1.3
local random_offset = 132 local random_offset = 132
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local scanning_ratio = 0.0003 local scanning_ratio = 0.0003
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level

View File

@ -1,10 +1,13 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 15 -- Check: v7 apple 27576,14,28368
-- Check: v7 apple 29570,10,29266
local chance_per_chunk = 40
local noise_multiplier = 1 local noise_multiplier = 1
local random_offset = 133 local random_offset = 133
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local scanning_ratio = 0.00021 local scanning_ratio = 0.00021
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level

View File

@ -57,4 +57,4 @@ mcl_mapgen.register_mapgen(function(minp, maxp, seed, vm_context)
end end
end end
end end
end, -1) end, 99999999999999)

View File

@ -1,11 +1,15 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 3 -- Check: v7 apple 30584,5,30356
-- Check: v7 apple 2637,6,-12031
-- Check: v7 apple 2644,6,-17968
local chance_per_chunk = 17
local noise_multiplier = -0.9 local noise_multiplier = -0.9
local random_offset = 8 local random_offset = 8
local scanning_ratio = 0.01 local scanning_ratio = 0.01
local struct_threshold = chance_per_chunk - 1 local struct_threshold = chance_per_chunk
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level

View File

@ -0,0 +1,22 @@
# MCL_Villages version 1.0
--------------------------
Originally a fork of Rochambeau's "Settlements", fully rewritten for MineClone 5.
## Using the mod
----------------
This mod adds villages on world generation.
## Credits
----------
* This mod is originally based on "ruins" by BlockMen
* Completely new schematics for MineClone2:
* MysticTempest - CC-BY-SA 4.0
* Basic conversion of Settlements mod for compatibility with MineClone2: MysticTempest
* Reimplemention: kay27
## License
----------
* License of source code: WTFPL

View File

@ -1,45 +0,0 @@
MCL_Villages:
============================
A fork of Rochambeau's "Settlements" mod converted for use in MineClone 2.
--------------
Using the mod:
--------------
This mod adds settlements on world generation.
And, in Creative Mode; also comes with a debug tool for spawning in villages.
-------------
MCL2 Credits:
-------------
Code forked from: https://github.com/MysticTempest/settlements/tree/mcl_villages
Commit: e24b4be
================================================================================
Basic conversion of Settlements mod for compatibility with MineClone2, plus new schematics: MysticTempest
Seed-based Village Generation, multi-threading, bugfixes: kay27
=========================
version: 0.1 alpha
License of source code: WTFPL
-----------------------------
(c) Copyright Rochambeau (2018)
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
Credits:
--------------
This mod is based on "ruins" by BlockMen
Completely new schematics for MineClone2:
MysticTempest - CC-BY-SA 4.0

View File

@ -1,224 +0,0 @@
-------------------------------------------------------------------------------
-- initialize settlement_info
-------------------------------------------------------------------------------
function settlements.initialize_settlement_info(pr)
local count_buildings = {}
-- count_buildings table reset
for k,v in pairs(settlements.schematic_table) do
count_buildings[v["name"]] = 0
end
-- randomize number of buildings
local number_of_buildings = pr:next(10, 25)
local number_built = 1
settlements.debug("Village ".. number_of_buildings)
return count_buildings, number_of_buildings, number_built
end
-------------------------------------------------------------------------------
-- fill settlement_info
--------------------------------------------------------------------------------
function settlements.create_site_plan(maxp, minp, pr)
local settlement_info = {}
local building_all_info
local possible_rotations = {"0", "90", "180", "270"}
-- find center of chunk
local center = {
x=math.floor((minp.x+maxp.x)/2),
y=maxp.y,
z=math.floor((minp.z+maxp.z)/2)
}
-- find center_surface of chunk
local center_surface , surface_material = settlements.find_surface(center, true)
local chunks = {}
chunks[mcl_mapgen.get_chunk_number(center)] = true
-- go build settlement around center
if not center_surface then return false end
-- add settlement to list
table.insert(settlements_in_world, center_surface)
-- save list to file
settlements.save()
-- initialize all settlement_info table
local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr)
-- first building is townhall in the center
building_all_info = settlements.schematic_table[1]
local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
-- add to settlement info table
local index = 1
settlement_info[index] = {
pos = center_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
--increase index for following buildings
index = index + 1
-- now some buildings around in a circle, radius = size of town center
local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"]
-- draw j circles around center and increase radius by math.random(2,5)
for j = 1,20 do
-- set position on imaginary circle
for j = 0, 360, 15 do
local angle = j * math.pi / 180
local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle )
ptx = settlements.round(ptx, 0)
ptz = settlements.round(ptz, 0)
local pos1 = { x=ptx, y=center_surface.y+50, z=ptz}
local chunk_number = mcl_mapgen.get_chunk_number(pos1)
local pos_surface, surface_material
if chunks[chunk_number] then
pos_surface, surface_material = settlements.find_surface(pos1)
else
chunks[chunk_number] = true
pos_surface, surface_material = settlements.find_surface(pos1, true)
end
if not pos_surface then break end
local randomized_schematic_table = shuffle(settlements.schematic_table, pr)
-- pick schematic
local size = #randomized_schematic_table
for i = size, 1, -1 do
-- already enough buildings of that type?
if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then
building_all_info = randomized_schematic_table[i]
-- check distance to other buildings
local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"])
if distance_to_other_buildings_ok then
-- count built houses
count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1
rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
number_built = number_built + 1
settlement_info[index] = {
pos = pos_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
index = index + 1
break
end
end
end
if number_of_buildings == number_built then
break
end
end
if number_built >= number_of_buildings then
break
end
r = r + pr:next(2,5)
end
settlements.debug("really ".. number_built)
return settlement_info
end
-------------------------------------------------------------------------------
-- evaluate settlement_info and place schematics
-------------------------------------------------------------------------------
-- Initialize node
local function construct_node(p1, p2, name)
local r = minetest.registered_nodes[name]
if r then
if r.on_construct then
local nodes = minetest.find_nodes_in_area(p1, p2, name)
for p=1, #nodes do
local pos = nodes[p]
r.on_construct(pos)
end
return nodes
end
minetest.log("warning", "[mcl_villages] No on_construct defined for node name " .. name)
return
end
minetest.log("warning", "[mcl_villages] Attempt to 'construct' inexistant nodes: " .. name)
end
local function init_nodes(p1, p2, size, rotation, pr)
construct_node(p1, p2, "mcl_itemframes:item_frame")
construct_node(p1, p2, "mcl_furnaces:furnace")
construct_node(p1, p2, "mcl_anvils:anvil")
local nodes = construct_node(p1, p2, "mcl_chests:chest")
if nodes and #nodes > 0 then
for p=1, #nodes do
local pos = nodes[p]
settlements.fill_chest(pos, pr)
end
end
end
function settlements.place_schematics(settlement_info, pr)
local building_all_info
for i, built_house in ipairs(settlement_info) do
for j, schem in ipairs(settlements.schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
building_all_info = schem
break
end
end
local pos = settlement_info[i]["pos"]
local rotation = settlement_info[i]["rotat"]
-- get building node material for better integration to surrounding
local platform_material = settlement_info[i]["surface_mat"]
--platform_material_name = minetest.get_name_from_content_id(platform_material)
-- pick random material
--local material = wallmaterial[pr:next(1,#wallmaterial)]
--
local building = building_all_info["mts"]
local replace_wall = building_all_info["rplc"]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return schematic"
schem_lua = schem_lua:gsub("mcl_core:stonebrickcarved", "mcl_villages:stonebrickcarved")
-- replace material
if replace_wall then
--Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs.
-- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled.
if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood")
--schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:spruce_fence")
--schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_sprucewood_top")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_sprucewood")
--schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_sprucewood_off")
elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved")
schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth")
--schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:birch_fence")
--schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_birchwood_top")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_birchwood")
--schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_birchwood_off")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_stonebrick", "mcl_stairs:stair_redsandstone")
--schem_lua = schem_lua:gsub("mcl_core:stonebrick", "mcl_core:redsandstonesmooth")
schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone")
end
end
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material)
--[[ Disable special junglewood for now.
-- special material for spawning npcs
schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood")
--]]
schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood")
schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air")
-- format schematic string
local schematic = loadstring(schem_lua)()
-- build foundation for the building an make room above
-- place schematic
mcl_structures.place_schematic({
pos = pos,
schematic = schematic,
rotation = rotation,
force_placement = true,
on_place = init_nodes,
pr = pr,
})
end
end

View File

@ -1,81 +0,0 @@
-- switch for debugging
function settlements.debug(message)
-- minetest.chat_send_all(message)
-- minetest.log("warning", "[mcl_villages] "..message)
minetest.log("verbose", "[mcl_villages] "..message)
end
--[[ Manually set in 'buildings.lua'
-- material to replace cobblestone with
local wallmaterial = {
"mcl_core:junglewood",
"mcl_core:sprucewood",
"mcl_core:wood",
"mcl_core:birchwood",
"mcl_core:acaciawood",
"mcl_core:stonebrick",
"mcl_core:cobble",
"mcl_core:sandstonecarved",
"mcl_core:sandstone",
"mcl_core:sandstonesmooth2"
}
--]]
settlements.surface_mat = {}
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------
function settlements.grundstellungen()
settlements.surface_mat = settlements.Set {
"mcl_core:dirt_with_grass",
--"mcl_core:dry_dirt_with_grass",
"mcl_core:dirt_with_grass_snow",
--"mcl_core:dirt_with_dry_grass",
"mcl_core:podzol",
"mcl_core:sand",
"mcl_core:redsand",
--"mcl_core:silver_sand",
"mcl_core:snow"
}
end
--
-- possible surfaces where buildings can be built
--
--
-- path to schematics
--
schem_path = settlements.modpath.."/schematics/"
--
-- list of schematics
--
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true)
settlements.schematic_table = {
{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 11, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages },
{name = "butcher", mts = schem_path.."butcher.mts", hwidth = 11, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages },
{name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages },
{name = "farm", mts = schem_path.."farm.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages },
{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 3, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false },
{name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages },
{name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 8, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 13, max_num = 0.7 , rplc = basic_pseudobiome_villages },
{name = "tavern", mts = schem_path.."tavern.mts", hwidth = 11, hdepth = 10, hheight = 10, hsize = 13, max_num = 0.050, rplc = basic_pseudobiome_villages },
{name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 6, hsize = 10, max_num = 0.045, rplc = basic_pseudobiome_villages },
}
--
-- list of settlements, load on server start up
--
settlements_in_world = {}
--
--
-- maximum allowed difference in height for building a sttlement
--
max_height_difference = 56
--
--
--
half_map_chunk_size = 40
--quarter_map_chunk_size = 20

View File

@ -1,59 +0,0 @@
-------------------------------------------------------------------------------
-- function to fill empty space below baseplate when building on a hill
-------------------------------------------------------------------------------
function settlements.ground(pos, pr) -- role model: Wendelsteinkircherl, Brannenburg
local p2 = vector.new(pos)
local cnt = 0
local mat = "mcl_core:dirt"
p2.y = p2.y-1
while true do
cnt = cnt+1
if cnt > 20 then break end
if cnt>pr:next(2,4) then
mat = "mcl_core:stone"
end
minetest.swap_node(p2, {name=mat})
p2.y = p2.y-1
end
end
-------------------------------------------------------------------------------
-- function clear space above baseplate
-------------------------------------------------------------------------------
function settlements.terraform(settlement_info, pr)
local fheight, fwidth, fdepth, schematic_data
for i, built_house in ipairs(settlement_info) do
-- pick right schematic_info to current built_house
for j, schem in ipairs(settlements.schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
schematic_data = schem
break
end
end
local pos = settlement_info[i]["pos"]
if settlement_info[i]["rotat"] == "0" or settlement_info[i]["rotat"] == "180" then
fwidth = schematic_data["hwidth"]
fdepth = schematic_data["hdepth"]
else
fwidth = schematic_data["hdepth"]
fdepth = schematic_data["hwidth"]
end
--fheight = schematic_data["hheight"] * 3 -- remove trees and leaves above
fheight = schematic_data["hheight"] -- remove trees and leaves above
--
-- now that every info is available -> create platform and clear space above
--
for xi = 0,fwidth-1 do
for zi = 0,fdepth-1 do
for yi = 0,fheight *3 do
if yi == 0 then
local p = {x=pos.x+xi, y=pos.y, z=pos.z+zi}
settlements.ground(p, pr)
else
minetest.swap_node({x=pos.x+xi, y=pos.y+yi, z=pos.z+zi},{name="air"})
end
end
end
end
end
end

View File

@ -1,26 +1,379 @@
settlements = {} mcl_villages = {}
settlements.modpath = minetest.get_modpath(minetest.get_current_modname()) local chance_per_chunk = 90
local chunk_offset_top = 15
local chunk_offset_bottom = 3
local max_height_difference = 12
local minp_min = -64
local noise_multiplier = 1
local random_offset = 1
local random_multiply = 19
local struct_threshold = chance_per_chunk
local noise_params = {
offset = 0,
scale = 2,
spread = {
x = mcl_mapgen.CS_NODES * chance_per_chunk,
y = mcl_mapgen.CS_NODES * chance_per_chunk,
z = mcl_mapgen.CS_NODES * chance_per_chunk,
},
seed = 842458,
octaves = 2,
persistence = 0.5,
}
local perlin_noise
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local S = minetest.get_translator(modname)
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true)
local schem_path = modpath .. "/schematics/"
local schematic_table = {
{name = "large_house", mts = schem_path.."large_house.mts", max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "blacksmith", mts = schem_path.."blacksmith.mts", max_num = 0.055, rplc = basic_pseudobiome_villages },
{name = "butcher", mts = schem_path.."butcher.mts", max_num = 0.03 , rplc = basic_pseudobiome_villages },
{name = "church", mts = schem_path.."church.mts", max_num = 0.04 , rplc = basic_pseudobiome_villages },
{name = "farm", mts = schem_path.."farm.mts", max_num = 0.1 , rplc = basic_pseudobiome_villages },
{name = "lamp", mts = schem_path.."lamp.mts", max_num = 0.1 , rplc = false },
{name = "library", mts = schem_path.."library.mts", max_num = 0.04 , rplc = basic_pseudobiome_villages },
{name = "medium_house", mts = schem_path.."medium_house.mts", max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "small_house", mts = schem_path.."small_house.mts", max_num = 0.7 , rplc = basic_pseudobiome_villages },
{name = "tavern", mts = schem_path.."tavern.mts", max_num = 0.050, rplc = basic_pseudobiome_villages },
{name = "well", mts = schem_path.."well.mts", max_num = 0.045, rplc = basic_pseudobiome_villages },
}
local surface_mat = {
["mcl_core:dirt_with_dry_grass"] = { top = "mcl_core:dirt", bottom = "mcl_core:stone" },
["mcl_core:dirt_with_grass"] = { top = "mcl_core:dirt", bottom = "mcl_core:stone" },
["mcl_core:dirt_with_grass_snow"] = { top = "mcl_core:dirt", bottom = "mcl_core:stone" },
["mcl_core:podzol"] = { top = "mcl_core:podzol", bottom = "mcl_core:stone" },
["mcl_core:redsand"] = { top = "mcl_core:redsand", bottom = "mcl_core:redsandstone" },
["mcl_core:sand"] = { top = "mcl_core:sand", bottom = "mcl_core:sandstone" },
["mcl_core:snow"] = { top = "mcl_core:dirt", bottom = "mcl_core:stone" },
}
local storage = minetest.get_mod_storage()
local villages = minetest.deserialize(storage:get_string("villages") or "return {}") or {}
local minetest_get_spawn_level = minetest.get_spawn_level
local minetest_get_node = minetest.get_node
local minetest_find_nodes_in_area = minetest.find_nodes_in_area
local minetest_get_perlin = minetest.get_perlin
local math_pi = math.pi
local math_cos = math.cos
local math_sin = math.sin
local math_min = math.min
local math_max = math.max
local math_floor = math.floor
local math_ceil = math.ceil
local string_find = string.find
local minetest_swap_node = minetest.swap_node
local minetest_registered_nodes = minetest.registered_nodes
local minetest_bulk_set_node = minetest.bulk_set_node
local air_offset = chunk_offset_top - 1
local ground_offset = chunk_offset_bottom + 1
local surface_search_list = {}
for k, _ in pairs(surface_mat) do
table.insert(surface_search_list, k)
end
local minetest_get_spawn_level = minetest.get_spawn_level local function math_round(x)
return (x < 0) and math_ceil(x - 0.5) or math_floor(x + 0.5)
end
dofile(settlements.modpath.."/const.lua") local function find_surface(pos, minp, maxp)
dofile(settlements.modpath.."/utils.lua") local x, z = pos.x, pos.z
dofile(settlements.modpath.."/foundation.lua") local y_top = maxp.y
dofile(settlements.modpath.."/buildings.lua") local y_max = y_top - air_offset
dofile(settlements.modpath.."/paths.lua") if #minetest_find_nodes_in_area({x=x, y=y_max, z=z}, {x=x, y=y_top, z=z}, "air") < chunk_offset_top then return end
--dofile(settlements.modpath.."/convert_lua_mts.lua") y_max = y_max - 1
-- local y_bottom = minp.y
-- load settlements on server local y_min = y_bottom + chunk_offset_bottom
-- local nodes = minetest_find_nodes_in_area({x=x, y=y_min, z=z}, {x=x, y=y_max, z=z}, surface_search_list)
settlements_in_world = settlements.load() for _, surface_pos in pairs(nodes) do
settlements.grundstellungen() local node_name_from_above = minetest_get_node({x=surface_pos.x, y=surface_pos.y+1, z=surface_pos.z}).name
if string_find(node_name_from_above, "air" )
or string_find(node_name_from_above, "snow" )
or string_find(node_name_from_above, "fern" )
or string_find(node_name_from_above, "flower")
or string_find(node_name_from_above, "bush" )
or string_find(node_name_from_above, "tree" )
or string_find(node_name_from_above, "grass" )
then
return surface_pos, minetest_get_node(surface_pos).name
end
end
end
local function get_treasures(pr)
local loottable = {{
stacks_min = 3,
stacks_max = 8,
items = {
{ itemstring = "mcl_core:diamond" , weight = 3, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:iron_ingot" , weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:gold_ingot" , weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:bread" , weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple" , weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tools:pick_iron" , weight = 5, },
{ itemstring = "mcl_tools:sword_iron" , weight = 5, },
{ itemstring = "mcl_armor:chestplate_iron" , weight = 5, },
{ itemstring = "mcl_armor:helmet_iron" , weight = 5, },
{ itemstring = "mcl_armor:leggings_iron" , weight = 5, },
{ itemstring = "mcl_armor:boots_iron" , weight = 5, },
{ itemstring = "mcl_core:obsidian" , weight = 5, amount_min = 3, amount_max = 7 },
{ itemstring = "mcl_core:sapling" , weight = 5, amount_min = 3, amount_max = 7 },
{ itemstring = "mcl_mobitems:saddle" , weight = 3, },
{ itemstring = "mobs_mc:iron_horse_armor" , weight = 1, },
{ itemstring = "mobs_mc:gold_horse_armor" , weight = 1, },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 1, },
}
}}
local items = mcl_loot.get_multi_loot(loottable, pr)
return items
end
local function fill_chest(pos, pr)
local meta = minetest.get_meta(pos)
minetest.registered_nodes["mcl_chests:chest_small"].on_construct(pos)
local inv = minetest.get_inventory( {type="node", pos=pos} )
local items = get_treasures(pr)
mcl_loot.fill_inventory(inv, "main", items, pr)
end
local possible_rotations = {"0", "90", "180", "270"}
local function get_random_rotation(pr)
return possible_rotations[pr:next(1, #possible_rotations)]
end
local function create_site_plan(minp, maxp, pr)
local plan = {}
local building_all_info
local center = vector.add(minp, mcl_mapgen.HALF_CS_NODES)
local center_surface, surface_material = find_surface(center, minp, maxp)
if not center_surface then return end
local number_of_buildings = pr:next(10, 25)
local shuffle = {}
local count_buildings = {}
for i = 1, #schematic_table do
shuffle[i] = i
count_buildings[i] = 0
end
for i = #shuffle, 2, -1 do
local j = pr:next(1, i)
shuffle[i], shuffle[j] = shuffle[j], shuffle[i]
end
local number_built = 1
local shuffle_index = pr:next(1, #schematic_table)
-- first building is townhall in the center
plan[#plan + 1] = {
pos = center_surface,
building = schematic_table[shuffle_index],
rotation = get_random_rotation(pr),
surface_mat = surface_material,
}
count_buildings[1] = count_buildings[1] + 1
-- now some buildings around in a circle, radius = size of town center
local x, z, r = center_surface.x, center_surface.z, schematic_table[1].hsize
-- draw j circles around center and increase radius by random(2, 5)
for k = 1, 20 do
-- set position on imaginary circle
for j = 0, 360, 15 do
local angle = j * math_pi / 180
local pos_surface, surface_material = find_surface(
{
x = math_round(x + r * math_cos(angle)),
z = math_round(z + r * math_sin(angle))
},
minp,
maxp
)
if pos_surface then
shuffle_index = (shuffle_index % (#schematic_table)) + 1
local schematic_index = shuffle[shuffle_index]
local schematic = schematic_table[schematic_index]
if count_buildings[schematic_index] < schematic.max_num * number_of_buildings then
local hsize2 = schematic.hsize^2
local is_distance_ok = true
for _, built_house in pairs(plan) do
local pos = built_house.pos
local building = built_house.building
local distance2 = (pos_surface.x - pos.x)^2 + (pos_surface.z - pos.z)^2
if distance2 < building.hsize^2 or distance2 < hsize2 then
is_distance_ok = false
break
end
end
if is_distance_ok then
plan[#plan + 1] = {
pos = pos_surface,
building = schematic,
rotation = get_random_rotation(pr),
surface_mat = surface_material,
}
count_buildings[schematic_index] = count_buildings[schematic_index] + 1
number_built = number_built + 1
break
end
end
end
if number_built >= number_of_buildings then
break
end
end
if number_built >= number_of_buildings then
break
end
r = r + pr:next(2, 5)
end
return plan
end
local function ground(pos1, pos2, minp, maxp, pr, mat)
local pos1, pos2 = pos1, pos2
local x1, x2, z1, z2, y = pos1.x, pos2.x, pos1.z, pos2.z, pos1.y - 1
local pos_list_dirt = {}
local pos_list_stone = {}
for x0 = x1, x2 do
for z0 = z1, z2 do
local finish = false
local y1 = y - pr:next(2, 4)
for y0 = y, y1, -1 do
local p0 = {x = x0, y = y0, z = z0}
local node = minetest_get_node(p0)
local node_name = node.name
if node_name ~= "air" and not string_find(node_name, "water") and not string_find(node_name, "flower") then
finish = true
break
end
pos_list_dirt[#pos_list_dirt + 1] = p0
end
if not finish then
for y0 = y1 - 1, math_max(minp.y, y - pr:next(17, 27)), -1 do
local p0 = {x = x0, y = y0, z = z0}
local node = minetest_get_node(p0)
local node_name = node.name
if node_name ~= "air" and not string_find(node_name, "water") and not string_find(node_name, "flower") then
break
end
pos_list_stone[#pos_list_stone + 1] = p0
end
end
end
end
minetest_bulk_set_node(pos_list_dirt, {name = surface_mat[mat].top})
minetest_bulk_set_node(pos_list_stone, {name = surface_mat[mat].bottom})
end
local function terraform(plan, minp, maxp, pr)
local fheight, fwidth, fdepth, schematic_data, pos, rotation, swap_wd, build_material
for _, built_house in pairs(plan) do
schematic_data = built_house.building
pos = built_house.pos
rotation = built_house.rotation
build_material = built_house.surface_mat
swap_wd = rotation == "90" or rotation == "270"
fwidth = swap_wd and schematic_data.hdepth or schematic_data.hwidth
fdepth = swap_wd and schematic_data.hwidth or schematic_data.hdepth
fheight = schematic_data.hheight
local pos2 = {
x = pos.x + fwidth - 1,
y = math_min(pos.y + fheight + 4, maxp.y),
z = pos.z + fdepth - 1
}
ground(pos, {x = pos2.x, y = pos.y + 1, z = pos2.z}, minp, maxp, pr, build_material)
local node_list = {}
for xi = pos.x, pos2.x do
for zi = pos.z, pos2.z do
for yi = pos.y + 1, pos2.y do
node_list[#node_list + 1] = {x = xi, y = yi, z = zi}
end
end
end
minetest_bulk_set_node(node_list, {name = "air"})
end
end
local function paths(plan, minp, maxp)
local starting_point = find_surface({x = plan[1].pos.x + 2, z = plan[1].pos.z + 2}, minp, maxp)
if not starting_point then return end
starting_point.y = starting_point.y + 1
for i = 2, #plan do
local p = plan[i]
local end_point = p.pos
end_point.y = end_point.y + 1
local path = minetest.find_path(starting_point, end_point, mcl_mapgen.CS_NODES, 2, 2, "A*_noprefetch")
if path then
for _, pos in pairs(path) do
pos.y = pos.y - 1
local surface_mat = minetest.get_node(pos).name
if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then
minetest.swap_node(pos, {name = "mcl_core:sandstonesmooth2"})
else
minetest.swap_node(pos, {name = "mcl_core:grass_path"})
end
end
end
end
end
local function init_nodes(p1, rotation, pr, size)
local p2 = vector.subtract(vector.add(p1, size), 1)
local nodes = minetest.find_nodes_in_area(p1, p2, {"mcl_itemframes:item_frame", "mcl_furnaces:furnace", "mcl_anvils:anvil", "mcl_chests:chest", "mcl_villages:stonebrickcarved"})
for _, pos in pairs(nodes) do
local name = minetest_get_node(pos).name
local def = minetest_registered_nodes[minetest_get_node(pos).name]
def.on_construct(pos)
if name == "mcl_chests:chest" then
minetest_swap_node(pos, {name = "mcl_chests:chest_small"})
fill_chest(pos, pr)
end
end
end
local function place_schematics(plan, pr)
for _, built_house in pairs(plan) do
local pos = built_house.pos
local rotation = built_house.rotation
local platform_material = built_house.surface_mat
local replace_wall = built_house.building.rplc
local schem_lua = built_house.building.preloaded_schematic
if replace_wall then
--Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs.
-- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled.
if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood")
elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved")
schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth")
schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone")
end
end
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material)
schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood")
schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air")
local schematic = loadstring(schem_lua)()
-- build foundation for the building an make room above
-- place schematic
mcl_structures.place_schematic({
pos = pos,
schematic = schematic,
rotation = rotation,
on_placed = init_nodes,
pr = pr,
})
end
end
-- --
-- register block for npc spawn -- register block for npc spawn
-- --
local function spawn_villager(pos)
minetest.add_entity({x = pos.x, y = pos.y + 1, z = pos.z}, "mobs_mc:villager")
end
minetest.register_node("mcl_villages:stonebrickcarved", { minetest.register_node("mcl_villages:stonebrickcarved", {
description = ("Chiseled Stone Village Bricks"), description = S("Chiseled Stone Village Bricks"),
_doc_items_longdesc = doc.sub.items.temp.build, _doc_items_longdesc = doc.sub.items.temp.build,
tiles = {"mcl_core_stonebrick_carved.png"}, tiles = {"mcl_core_stonebrick_carved.png"},
stack_max = 64, stack_max = 64,
@ -30,93 +383,142 @@ minetest.register_node("mcl_villages:stonebrickcarved", {
is_ground_content = false, is_ground_content = false,
_mcl_blast_resistance = 6, _mcl_blast_resistance = 6,
_mcl_hardness = 1.5, _mcl_hardness = 1.5,
on_construct = spawn_villager,
})
minetest.register_abm({
label = "Spawn villagers",
nodenames = {"mcl_villages:stonebrickcarved"},
interval = 60,
chance = 3,
action = function(pos, node)
-- check the space above
local p = table.copy(pos)
p.y = p.y + 1
if minetest_get_node(p).name ~= "air" then return end
p.y = p.y + 1
if minetest_get_node(p).name ~= "air" then return end
p.y = p.y - 1
local villagers_counter = 0
for _, obj in pairs(minetest.get_objects_inside_radius(p, 40)) do
local lua_entity = obj:get_luaentity()
if luaentity and luaentity.name == "mobs_mc:villager" then
villagers_counter = villagers_counter + 1
if villagers_counter > 7 then return end
end
end
spawn_villager(pos)
end
}) })
--[[ Enable for testing, but use MineClone2's own spawn code if/when merging.
--
-- register inhabitants
--
if minetest.get_modpath("mobs_mc") then
mobs:register_spawn("mobs_mc:villager", --name
{"mcl_core:stonebrickcarved"}, --nodes
15, --max_light
0, --min_light
20, --chance
7, --active_object_count
31000, --max_height
nil) --day_toggle
end
--]]
-- --
-- on map generation, try to build a settlement -- on map generation, try to build a settlement
-- --
local function build_a_settlement(minp, maxp, blockseed) local function build_a_village(minp, maxp, pr, placer)
minetest.log("action","[mcl_villages] Building village at mapchunk " .. minetest.pos_to_string(minp) .. "..." .. minetest.pos_to_string(maxp) .. ", blockseed = " .. tostring(blockseed)) minetest.log("action","[mcl_villages] Building village at mapchunk " .. minetest.pos_to_string(minp) .. "..." .. minetest.pos_to_string(maxp))
local pr = PseudoRandom(blockseed) local pr = pr or PseudoRandom(mcl_mapgen.get_block_seed3(minp))
local plan = create_site_plan(minp, maxp, pr)
-- fill settlement_info with buildings and their data if not plan then
local settlement_info = settlements.create_site_plan(maxp, minp, pr) if placer then
if not settlement_info then return end if placer:is_player() then
minetest.chat_send_player(placer:get_player_name(), S("Map chunk @1 to @2 is not suitable for placing villages.", minetest.pos_to_string(minp), minetest.pos_to_string(maxp)))
-- evaluate settlement_info and prepair terrain end
settlements.terraform(settlement_info, pr) end
return
-- evaluate settlement_info and build paths between buildings end
settlements.paths(settlement_info) paths(plan, minp, maxp)
terraform(plan, minp, maxp, pr)
-- evaluate settlement_info and place schematics place_schematics(plan, pr)
settlements.place_schematics(settlement_info, pr) villages[#villages + 1] = minp
storage:set_string("villages", minetest.serialize(villages))
end end
-- Disable natural generation in singlenode. -- Disable natural generation in singlenode.
local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name ~= "singlenode" then if mg_name ~= "singlenode" then
mcl_mapgen.register_mapgen(function(minp, maxp, blockseed) local mg_name = minetest.get_mapgen_setting("mg_name")
-- local str1 = (maxp.y >= 0 and blockseed % 77 == 17) and "YES" or "no" local scan_last_node = mcl_mapgen.LAST_BLOCK * mcl_mapgen.BS - 1
-- minetest.log("action","[mcl_villages] " .. str1 .. ": minp=" .. minetest.pos_to_string(minp) .. ", maxp=" .. minetest.pos_to_string(maxp) .. ", blockseed=" .. tostring(blockseed)) local scan_offset = mcl_mapgen.BS
-- don't build settlement underground mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed)
if maxp.y < 0 then return end if minp.y < minp_min then return end
-- randomly try to build settlements local pr = PseudoRandom(chunkseed * random_multiply + random_offset)
if blockseed % 77 ~= 17 then return end local random_number = pr:next(1, chance_per_chunk)
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
-- don't build settlements on (too) uneven terrain local noise = perlin_noise:get_3d(minp) * noise_multiplier
if (random_number + noise) < struct_threshold then return end
-- lame and quick replacement of `heightmap` by kay27 - we maybe need to restore `heightmap` analysis if there will be a way for the engine to avoid cavegen conflicts: local min, max = 9999999, -9999999
-------------------------------------------------------------------------- for i = 1, pr:next(5,10) do
local height_difference, min, max local surface_point = find_surface(
local pr1=PseudoRandom(blockseed) vector.add(
for i=1,pr1:next(5,10) do vector.new(
local x = pr1:next(0, 40) + minp.x + 19 pr:next(scan_offset, scan_last_node),
local z = pr1:next(0, 40) + minp.z + 19 0,
local y = minetest_get_spawn_level(x, z) pr:next(scan_offset, scan_last_node)
if not y then return end ),
if y < (min or y+1) then min = y end minp
if y > (max or y-1) then max = y end ),
minp,
maxp
)
if not surface_point then return end
local y = surface_point.y
min = math_min(y, min)
max = math_max(y, max)
end end
height_difference = max - min + 1 local height_difference = max - min
--------------------------------------------------------------------------
if height_difference > max_height_difference then return end if height_difference > max_height_difference then return end
build_a_village(minp, maxp, chunkkseed)
build_a_settlement(minp, maxp, blockseed)
end, mcl_mapgen.order.VILLAGES) end, mcl_mapgen.order.VILLAGES)
end end
-- manually place villages
if minetest.is_creative_enabled("") then for k, v in pairs(schematic_table) do
minetest.register_craftitem("mcl_villages:tool", { local schem_lua = minetest.serialize_schematic(
description = "mcl_villages build tool", v.mts,
inventory_image = "default_tool_woodshovel.png", "lua",
-- build ssettlement {
on_place = function(itemstack, placer, pointed_thing) lua_use_comments = false,
if not pointed_thing.under then return end lua_num_indent_spaces = 0,
local minp = vector.subtract( pointed_thing.under, half_map_chunk_size) }
local maxp = vector.add( pointed_thing.under, half_map_chunk_size) ):gsub("mcl_core:stonebrickcarved", "mcl_villages:stonebrickcarved") .. " return schematic"
build_a_settlement(minp, maxp, math.random(0,32767)) v.preloaded_schematic = schem_lua
local loaded_schematic = loadstring(schem_lua)()
local size = loaded_schematic.size
v.hwidth = size.x
v.hheight = size.y
v.hdepth = size.z
v.hsize = math.ceil(math.sqrt((size.x/2)^2 + (size.y/2)^2) * 2 + 1)
mcl_structures.register_structure({
name = v.name,
place_function = function(pos, rotation, pr, placer)
local minp = mcl_mapgen.get_chunk_beginning(pos)
local maxp = mcl_mapgen.get_chunk_ending(pos)
local surface_pos, surface_material = find_surface(pos, minp, maxp)
local plan = {
[1] = {
pos = pos,
building = schematic_table[k],
rotation = rotation,
surface_mat = surface_material or "mcl_core:snow",
}
}
if surface_material then
terraform(plan, minp, maxp, pr)
end
place_schematics(plan, pr)
end end
}) })
mcl_wip.register_experimental_item("mcl_villages:tool") end
mcl_structures.register_structure({
name = "village",
place_function = function(pos, rotation, pr, placer)
local minp = mcl_mapgen.get_chunk_beginning(pos)
local maxp = mcl_mapgen.get_chunk_ending(pos)
build_a_village(minp, maxp, pr, placer)
end
})
function mcl_villages.get_villages()
return villages
end end

View File

@ -0,0 +1,3 @@
# textdomain: mcl_villages
Chiseled Stone Village Bricks=Точёный каменный блок из деревни
Map chunk @1 to @2 is not suitable for placing villages.=Чанк с @1 по @2 непригоден для размещения деревень.

View File

@ -0,0 +1,3 @@
# textdomain: mcl_villages
Chiseled Stone Village Bricks=
Map chunk @1 to @2 is not suitable for placing villages.=

View File

@ -1,5 +1,5 @@
name = mcl_villages name = mcl_villages
author = Rochambeau author = Rochambeau, MysticTempest, kay27
description = This mod adds settlements on world generation. description = This mod adds villages on world generation.
depends = mcl_util, mcl_mapgen_core, mcl_structures, mcl_core, mcl_loot depends = mcl_util, mcl_structures, mcl_core, mcl_loot, mcl_mapgen
optional_depends = mcl_farming, mobs_mc optional_depends = mcl_farming, mobs_mc

View File

@ -1,91 +0,0 @@
-------------------------------------------------------------------------------
-- generate paths between buildings
-------------------------------------------------------------------------------
function settlements.paths(settlement_info)
local starting_point
local end_point
local distance
--for k,v in pairs(settlement_info) do
starting_point = settlement_info[1]["pos"]
for o,p in pairs(settlement_info) do
end_point = settlement_info[o]["pos"]
if starting_point ~= end_point
then
-- loop until end_point is reched (distance == 0)
while true do
-- define surrounding pos to starting_point
local north_p = {x=starting_point.x+1, y=starting_point.y, z=starting_point.z}
local south_p = {x=starting_point.x-1, y=starting_point.y, z=starting_point.z}
local west_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z+1}
local east_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z-1}
-- measure distance to end_point
local dist_north_p_to_end = math.sqrt(
((north_p.x - end_point.x)*(north_p.x - end_point.x))+
((north_p.z - end_point.z)*(north_p.z - end_point.z))
)
local dist_south_p_to_end = math.sqrt(
((south_p.x - end_point.x)*(south_p.x - end_point.x))+
((south_p.z - end_point.z)*(south_p.z - end_point.z))
)
local dist_west_p_to_end = math.sqrt(
((west_p.x - end_point.x)*(west_p.x - end_point.x))+
((west_p.z - end_point.z)*(west_p.z - end_point.z))
)
local dist_east_p_to_end = math.sqrt(
((east_p.x - end_point.x)*(east_p.x - end_point.x))+
((east_p.z - end_point.z)*(east_p.z - end_point.z))
)
-- evaluate which pos is closer to the end_point
if dist_north_p_to_end <= dist_south_p_to_end and
dist_north_p_to_end <= dist_west_p_to_end and
dist_north_p_to_end <= dist_east_p_to_end
then
starting_point = north_p
distance = dist_north_p_to_end
elseif dist_south_p_to_end <= dist_north_p_to_end and
dist_south_p_to_end <= dist_west_p_to_end and
dist_south_p_to_end <= dist_east_p_to_end
then
starting_point = south_p
distance = dist_south_p_to_end
elseif dist_west_p_to_end <= dist_north_p_to_end and
dist_west_p_to_end <= dist_south_p_to_end and
dist_west_p_to_end <= dist_east_p_to_end
then
starting_point = west_p
distance = dist_west_p_to_end
elseif dist_east_p_to_end <= dist_north_p_to_end and
dist_east_p_to_end <= dist_south_p_to_end and
dist_east_p_to_end <= dist_west_p_to_end
then
starting_point = east_p
distance = dist_east_p_to_end
end
-- find surface of new starting point
local surface_point, surface_mat = settlements.find_surface(starting_point)
-- replace surface node with mcl_core:grass_path
if surface_point
then
if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then
minetest.swap_node(surface_point,{name="mcl_core:sandstonesmooth2"})
else
minetest.swap_node(surface_point,{name="mcl_core:grass_path"})
end
-- don't set y coordinate, surface might be too low or high
starting_point.x = surface_point.x
starting_point.z = surface_point.z
end
if distance <= 1 or
starting_point == end_point
then
break
end
end
end
end
end

View File

@ -1,217 +0,0 @@
local get_node = mcl_mapgen.get_far_node
-------------------------------------------------------------------------------
-- function to copy tables
-------------------------------------------------------------------------------
function settlements.shallowCopy(original)
local copy = {}
for key, value in pairs(original) do
copy[key] = value
end
return copy
end
--
--
--
function settlements.round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
-------------------------------------------------------------------------------
-- function to find surface block y coordinate
-- returns surface postion
-------------------------------------------------------------------------------
function settlements.find_surface(pos, wait)
local p6 = vector.new(pos)
local cnt = 0
local itter = 1 -- count up or down
local cnt_max = 200
-- check, in which direction to look for surface
local surface_node
if wait then
surface_node = get_node(p6, true, 10000000)
else
surface_node = get_node(p6)
end
if surface_node.name=="air" or surface_node.name=="ignore" then
itter = -1
end
-- go through nodes an find surface
while cnt < cnt_max do
-- Check Surface_node and Node above
--
if settlements.surface_mat[surface_node.name] then
local surface_node_plus_1 = get_node({ x=p6.x, y=p6.y+1, z=p6.z})
if surface_node_plus_1 and surface_node and
(string.find(surface_node_plus_1.name,"air") or
string.find(surface_node_plus_1.name,"snow") or
string.find(surface_node_plus_1.name,"fern") or
string.find(surface_node_plus_1.name,"flower") or
string.find(surface_node_plus_1.name,"bush") or
string.find(surface_node_plus_1.name,"tree") or
string.find(surface_node_plus_1.name,"grass"))
then
settlements.debug("find_surface7: " ..surface_node.name.. " " .. surface_node_plus_1.name)
return p6, surface_node.name
else
settlements.debug("find_surface2: wrong surface+1")
end
else
settlements.debug("find_surface3: wrong surface "..surface_node.name.." at pos "..minetest.pos_to_string(p6))
end
p6.y = p6.y + itter
if p6.y < 0 then
settlements.debug("find_surface4: y<0")
return nil
end
cnt = cnt+1
surface_node = get_node(p6)
end
settlements.debug("find_surface5: cnt_max overflow")
return nil
end
-------------------------------------------------------------------------------
-- check distance for new building
-------------------------------------------------------------------------------
function settlements.check_distance(settlement_info, building_pos, building_size)
local distance
for i, built_house in ipairs(settlement_info) do
distance = math.sqrt(
((building_pos.x - built_house["pos"].x)*(building_pos.x - built_house["pos"].x))+
((building_pos.z - built_house["pos"].z)*(building_pos.z - built_house["pos"].z)))
if distance < building_size or distance < built_house["hsize"] then
return false
end
end
return true
end
-------------------------------------------------------------------------------
-- save list of generated settlements
-------------------------------------------------------------------------------
function settlements.save()
local file = io.open(minetest.get_worldpath().."/settlements.txt", "w")
if file then
file:write(minetest.serialize(settlements_in_world))
file:close()
end
end
-------------------------------------------------------------------------------
-- load list of generated settlements
-------------------------------------------------------------------------------
function settlements.load()
local file = io.open(minetest.get_worldpath().."/settlements.txt", "r")
if file then
local table = minetest.deserialize(file:read("*all"))
if type(table) == "table" then
return table
end
end
return {}
end
-------------------------------------------------------------------------------
-- fill chests
-------------------------------------------------------------------------------
function settlements.fill_chest(pos, pr)
-- initialize chest (mts chests don't have meta)
local meta = minetest.get_meta(pos)
if meta:get_string("infotext") ~= "Chest" then
-- For MineClone2 0.70 or before
-- minetest.registered_nodes["mcl_chests:chest"].on_construct(pos)
--
-- For MineClone2 after commit 09ab1482b5 (the new entity chests)
minetest.registered_nodes["mcl_chests:chest_small"].on_construct(pos)
end
-- fill chest
local inv = minetest.get_inventory( {type="node", pos=pos} )
local function get_treasures(prand)
local loottable = {{
stacks_min = 3,
stacks_max = 8,
items = {
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:gold_ingot", weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:bread", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tools:pick_iron", weight = 5 },
{ itemstring = "mcl_tools:sword_iron", weight = 5 },
{ itemstring = "mcl_armor:chestplate_iron", weight = 5 },
{ itemstring = "mcl_armor:helmet_iron", weight = 5 },
{ itemstring = "mcl_armor:leggings_iron", weight = 5 },
{ itemstring = "mcl_armor:boots_iron", weight = 5 },
{ itemstring = "mcl_core:obsidian", weight = 5, amount_min = 3, amount_max = 7 },
{ itemstring = "mcl_core:sapling", weight = 5, amount_min = 3, amount_max = 7 },
{ itemstring = "mcl_mobitems:saddle", weight = 3 },
{ itemstring = "mobs_mc:iron_horse_armor", weight = 1 },
{ itemstring = "mobs_mc:gold_horse_armor", weight = 1 },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 1 },
}
}}
local items = mcl_loot.get_multi_loot(loottable, prand)
return items
end
local items = get_treasures(pr)
mcl_loot.fill_inventory(inv, "main", items, pr)
end
-------------------------------------------------------------------------------
-- initialize furnace
-------------------------------------------------------------------------------
function settlements.initialize_furnace(pos)
-- find chests within radius
local furnacepos = minetest.find_node_near(pos,
7, --radius
{"mcl_furnaces:furnace"})
-- initialize furnacepos (mts furnacepos don't have meta)
if furnacepos
then
local meta = minetest.get_meta(furnacepos)
if meta:get_string("infotext") ~= "furnace"
then
minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos)
end
end
end
-------------------------------------------------------------------------------
-- initialize anvil
-------------------------------------------------------------------------------
function settlements.initialize_anvil(pos)
-- find chests within radius
local anvilpos = minetest.find_node_near(pos,
7, --radius
{"mcl_anvils:anvil"})
-- initialize anvilpos (mts anvilpos don't have meta)
if anvilpos
then
local meta = minetest.get_meta(anvilpos)
if meta:get_string("infotext") ~= "anvil"
then
minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos)
end
end
end
-------------------------------------------------------------------------------
-- randomize table
-------------------------------------------------------------------------------
function shuffle(tbl, pr)
local table = settlements.shallowCopy(tbl)
local size = #table
for i = size, 1, -1 do
local rand = pr:next(1, size)
table[i], table[rand] = table[rand], table[i]
end
return table
end
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------
function settlements.Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end