Add basic part of new mapgen api

This commit is contained in:
kay27 2022-01-19 19:09:37 +04:00
parent 9b881df465
commit b3e8f24876
50 changed files with 3740 additions and 2302 deletions

View File

@ -24,240 +24,9 @@ mcl_vars.inventory_header = ""
-- Tool wield size
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
-- Mapgen variables
local mg_name = minetest.get_mapgen_setting("mg_name")
local minecraft_height_limit = 256
local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
local singlenode = mg_name == "singlenode"
-- Calculate mapgen_edge_min/mapgen_edge_max
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
local function coordinate_to_block(x)
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
end
local function coordinate_to_chunk(x)
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
end
function mcl_vars.pos_to_block(pos)
return {
x = coordinate_to_block(pos.x),
y = coordinate_to_block(pos.y),
z = coordinate_to_block(pos.z)
}
end
function mcl_vars.pos_to_chunk(pos)
return {
x = coordinate_to_chunk(pos.x),
y = coordinate_to_chunk(pos.y),
z = coordinate_to_chunk(pos.z)
}
end
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
local k_positive_z = k_positive * 2
local k_positive_y = k_positive_z * k_positive_z
function mcl_vars.get_chunk_number(pos) -- unsigned int
local c = mcl_vars.pos_to_chunk(pos)
return
(c.y + k_positive) * k_positive_y +
(c.z + k_positive) * k_positive_z +
c.x + k_positive
end
if not superflat and not singlenode then
-- Normal mode
--[[ 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
mcl_vars.mg_overworld_min = -62
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min + 4
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min + 10
mcl_vars.mg_lava = true
mcl_vars.mg_bedrock_is_rough = true
elseif singlenode then
mcl_vars.mg_overworld_min = -66
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min
mcl_vars.mg_lava = false
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_is_rough = false
else
-- Classic superflat
local ground = minetest.get_mapgen_setting("mgflat_ground_level")
ground = tonumber(ground)
if not ground then
ground = 8
end
mcl_vars.mg_overworld_min = ground - 3
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min
mcl_vars.mg_lava = false
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_is_rough = false
end
mcl_vars.mg_overworld_max = mcl_vars.mapgen_edge_max
-- The Nether (around Y = -29000)
mcl_vars.mg_nether_min = -29067 -- Carefully chosen to be at a mapchunk border
mcl_vars.mg_nether_max = mcl_vars.mg_nether_min + 128
mcl_vars.mg_bedrock_nether_bottom_min = mcl_vars.mg_nether_min
mcl_vars.mg_bedrock_nether_top_max = mcl_vars.mg_nether_max
if not superflat then
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min + 4
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max - 4
mcl_vars.mg_lava_nether_max = mcl_vars.mg_nether_min + 31
else
-- Thin bedrock in classic superflat mapgen
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max
mcl_vars.mg_lava_nether_max = mcl_vars.mg_nether_min + 2
end
if mg_name == "flat" then
if superflat then
mcl_vars.mg_flat_nether_floor = mcl_vars.mg_bedrock_nether_bottom_max + 4
mcl_vars.mg_flat_nether_ceiling = mcl_vars.mg_bedrock_nether_bottom_max + 52
else
mcl_vars.mg_flat_nether_floor = mcl_vars.mg_lava_nether_max + 4
mcl_vars.mg_flat_nether_ceiling = mcl_vars.mg_lava_nether_max + 52
end
end
-- The End (surface at ca. Y = -27000)
mcl_vars.mg_end_min = -27073 -- Carefully chosen to be at a mapchunk border
mcl_vars.mg_end_max_official = mcl_vars.mg_end_min + minecraft_height_limit
mcl_vars.mg_end_max = mcl_vars.mg_overworld_min - 2000
mcl_vars.mg_end_platform_pos = { x = 100, y = mcl_vars.mg_end_min + 74, z = 0 }
-- Realm barrier used to safely separate the End from the void below the Overworld
mcl_vars.mg_realm_barrier_overworld_end_max = mcl_vars.mg_end_max
mcl_vars.mg_realm_barrier_overworld_end_min = mcl_vars.mg_end_max - 11
-- Use MineClone 2-style dungeons
mcl_vars.mg_dungeons = true
-- Set default stack sizes
minetest.nodedef_default.stack_max = 64
minetest.craftitemdef_default.stack_max = 64
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
math.randomseed(os.time())
local chunks = {} -- intervals of chunks generated
function mcl_vars.add_chunk(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
local prev
for i, d in pairs(chunks) do
if n <= d[2] then -- we've found it
if (n == d[2]) or (n >= d[1]) then return end -- already here
if n == d[1]-1 then -- right before:
if prev and (prev[2] == n-1) then
prev[2] = d[2]
table.remove(chunks, i)
return
end
d[1] = n
return
end
if prev and (prev[2] == n-1) then --join to previous
prev[2] = n
return
end
table.insert(chunks, i, {n, n}) -- insert new interval before i
return
end
prev = d
end
chunks[#chunks+1] = {n, n}
end
function mcl_vars.is_generated(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
for i, d in pairs(chunks) do
if n <= d[2] then
return (n >= d[1])
end
end
return false
end
-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
-- p: Position, if it's wrong, {name="error"} node will return.
-- force: optional (default: false) - Do the maximum to still read the node within us_timeout.
-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job.
--
-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}.
function mcl_vars.get_node(p, force, us_timeout)
-- check initial circumstances
if not p or not p.x or not p.y or not p.z then return {name="error"} end
-- try common way
local node = minetest.get_node(p)
if node.name ~= "ignore" then
return node
end
-- copy table to get sure it won't changed by other threads
local pos = {x=p.x,y=p.y,z=p.z}
-- try LVM
minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node(pos)
if node.name ~= "ignore" or not force then
return node
end
-- all ways failed - need to emerge (or forceload if generated)
local us_timeout = us_timeout or 244
if mcl_vars.is_generated(pos) then
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
minetest.forceload_block(pos)
else
minetest.emerge_area(pos, pos)
end
local t = minetest.get_us_time()
node = minetest.get_node(pos)
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
node = minetest.get_node(pos)
end
return node
-- it still can return "ignore", LOL, even if force = true, but only after time out
end

100
mods/CORE/mcl_mapgen/API.md Normal file
View File

@ -0,0 +1,100 @@
# mcl_mapgen
============
Helps to avoid problems caused by 'chunk-in-shell' feature of mapgen.cpp.
It also queues your generators to run them in proper order:
### mcl_mapgen.register_on_generated(lvm_callback_function, order_number)
=========================================================================
Replacement of engine API function `minetest.register_on_generated(function(vm_context))`
It is still unsafe. Cavegen part can and will overwrite outer 1-block layer of the chunk which is expected to be generated.
Nodes marked as `is_ground_content` could be overwritten. Air and water are usually 'ground content' too.
For Minetest 5.4 it doesn't recommended to place blocks within lvm callback function.
See https://git.minetest.land/MineClone2/MineClone2/issues/1395
`lvm_callback_function`: chunk callback LVM function definition:
`function(vm_context)`:
`vm_context` will pass into next lvm callback function from the queue!
`vm_context`: a table which already contains some LVM data as the fields, and some of them can be added in your lvm callback function:
`vm`: curent voxel manipulator object itself;
`chunkseed`: seed of this mapchunk;
`minp` & `maxp`: minimum and maximum chunk position;
`emin` & `emax`: minimum and maximum chunk position WITH SHELL AROUND IT;
`area`: voxel area, can be helpful to access data;
`data`: LVM buffer data array, data loads into it before the callbacks;
`write`: set it to true in your lvm callback functionm, if you changed `data` and want to write it;
`param2_data`: LVM buffer data array of `param2`, !NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - you load it yourself:
`vm_context.param2_data = vm_context.param2_data or vm_context.vm:get_param2_data(vm_context.lvm_param2_buffer)`
`write_param2`: set it to true in your lvm callback function, if you used `param2_data` and want to write it;
`light`: LVM buffer data array of light, !NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - you load it yourself:
`vm_context.light = vm_context.light or vm_context.vm.get_light_data(vm_context.lvm_light_buffer)`
`write_light`: set it to true in your lvm callback function, if you used `light` and want to write it;
`lvm_param2_buffer`: static `param2` buffer pointer, used to load `param2_data` array;
`shadow`: set it to false to disable shadow propagation;
`heightmap`: mapgen object contanting y coordinates of ground level,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.heightmap = vm_context.heightmap or minetest.get_mapgen_object('heightmap')`
`biomemap`: mapgen object contanting biome IDs of nodes,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.biomemap = vm_context.biomemap or minetest.get_mapgen_object('biomemap')`
`heatmap`: mapgen object contanting temperature values of nodes,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.heatmap = vm_context.heatmap or minetest.get_mapgen_object('heatmap')`
`humiditymap`: mapgen object contanting humidity values of nodes,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.humiditymap = vm_context.humiditymap or minetest.get_mapgen_object('humiditymap')`
`gennotify`: mapgen object contanting mapping table of structures, see Minetest Lua API for explanation,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.gennotify = vm_context.gennotify or minetest.get_mapgen_object('gennotify')`
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen_block_lvm(lvm_callback_function, order_number)
=============================================================================
Registers lvm callback function to be called when current block (usually 16x16x16 nodes) generation is REALLY 100% finished.
`vm_context` passes into lvm callback function.
`lvm_callback_function`: the block callback LVM function definition - same as for chunks - see definition example above;
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen_block(node_callback_function, order_number)
==========================================================================
Registers node_callback function to be called when current block (usually 16x16x16 nodes) generation is REALLY 100% finished.
`node_callback_function`: node callback function definition:
`function(minp, maxp, seed)`:
`minp` & `maxp`: minimum and maximum block position;
`seed`: seed of this mapblock;
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen(callback_function, order_number)
===============================================================
Registers callback function to be called when current chunk generation is REALLY 100% finished.
For LVM it's the most frustrating function from this mod.
It can't provide you access to mapgen objects. They are probably gone long ago.
Don't use it for accessing mapgen objects please.
To use VM you have to run `vm_context.vm = mcl_mapgen.get_voxel_manip(vm_context.emin, vm_context.emax)`.
`callback_function`: callback function definition:
`function(minp, maxp, seed, vm_context)`:
`minp` & `maxp`: minimum and maximum block position;
`seed`: seed of this mapblock;
`vm_context`: a table - see description above.
`order_number` (optional): the less, the earlier.
### mcl_mapgen.register_mapgen_lvm(lvm_callback_function, order_number)
=======================================================================
Registers lvm callback function to be called when current chunk generation is REALLY 100% finished.
It's the most frustrating function from this mod. It can't provide you access to mapgen objects. They are probably gone long ago.
Don't use it for accessing mapgen objects please.
`vm_context` passes into lvm callback function.
`lvm_callback_function`: the block callback LVM function definition - same as above;
`order_number` (optional): the less, the earlier.
### mcl_mapgen.get_far_node(pos)
================================
Returns node if it is generated, otherwise returns `{name = "ignore"}`.
## Constants:
* `mcl_mapgen.EDGE_MIN`, `mcl_mapgen.EDGE_MAX` - world edges, min & max.
* `mcl_mapgen.seed`, `mcl_mapgen.name` - mapgen seed & name.
* `mcl_mapgen.v6`, `mcl_mapgen.superflat`, `mcl_mapgen.singlenode` - is mapgen v6, superflat, singlenode.
* `mcl_mapgen.normal` is mapgen normal (not superflat or singlenode).

View File

@ -0,0 +1,498 @@
mcl_mapgen = {}
local order = { -- mcl_mapgen.order...
DEFAULT = 5000,
CHORUS = 100000,
BUILDINGS = 200000,
VILLAGES = 900000,
DUNGEONS = 950000,
STRONGHOLDS = 999999,
OCEAN_MONUMENT = 1000000,
LARGE_BUILDINGS = 2000000,
}
local math_floor = math.floor
local math_max = math.max
local minetest_get_node = minetest.get_node
local minetest_get_voxel_manip = minetest.get_voxel_manip
local minetest_log = minetest.log
local minetest_pos_to_string = minetest.pos_to_string
-- Calculate mapgen_edge_min/mapgen_edge_max
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.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.OFFSET = - math_floor(mcl_mapgen.CS / 2)
mcl_mapgen.OFFSET_NODES = mcl_mapgen.OFFSET * mcl_mapgen.BS
mcl_mapgen.CS_NODES = mcl_mapgen.CS * 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 ccfmin = central_chunk_min_pos - mcl_mapgen.BS -- Fullminp/fullmaxp of central chunk, in nodes
local ccfmax = central_chunk_max_pos + mcl_mapgen.BS
local mapgen_limit_b = math_floor(math.min(mcl_mapgen.LIMIT, mcl_mapgen.MAX_LIMIT) / mcl_mapgen.BS)
local mapgen_limit_min = - mapgen_limit_b * mcl_mapgen.BS
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_mapgen.BS - 1
local numcmin = math_max(math_floor((ccfmin - mapgen_limit_min) / mcl_mapgen.CS_NODES), 0) -- Number of complete chunks from central chunk
local numcmax = math_max(math_floor((mapgen_limit_max - ccfmax) / mcl_mapgen.CS_NODES), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_mapgen.EDGE_MIN = central_chunk_min_pos - numcmin * mcl_mapgen.CS_NODES
mcl_mapgen.EDGE_MAX = central_chunk_max_pos + numcmax * mcl_mapgen.CS_NODES
minetest_log("action", "[mcl_mapgen] World edges: mcl_mapgen.EDGE_MIN = " .. tostring(mcl_mapgen.EDGE_MIN) .. ", mcl_mapgen.EDGE_MAX = " .. tostring(mcl_mapgen.EDGE_MAX))
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Mapgen variables
local overworld, end_, nether = {}, {}, {}
local seed = minetest.get_mapgen_setting("seed")
mcl_mapgen.seed = seed
mcl_mapgen.name = minetest.get_mapgen_setting("mg_name")
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.singlenode = mcl_mapgen.name == "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
minetest_log("action", "[mcl_mapgen] Mapgen mode: " .. (normal and "normal" or (superflat and "superflat" or "singlenode")))
----------------------------------------------------------------------------------------------------------------------------
-- Generator queues
local queue_unsafe_engine = {}
local queue_chunks_nodes = {}
local queue_chunks_lvm = {}
local queue_blocks_nodes = {}
local queue_blocks_lvm = {}
-- Requirements. 0 means 'none', greater than 0 means 'required'
local block = 0
local queue_blocks_lvm_counter = 0
local lvm_chunk = 0
local param2 = 0
local nodes_block = 0
local nodes_chunk = 0
local safe_functions = 0
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 CS_NODES = mcl_mapgen.CS_NODES -- 80
local CS_3D = CS * CS * CS
local DEFAULT_ORDER = order.DEFAULT
function mcl_mapgen.register_on_generated(callback_function, order)
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)
end
function mcl_mapgen.register_mapgen(callback_function, order)
nodes_chunk = nodes_chunk + 1
safe_functions = safe_functions + 1
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)
end
function mcl_mapgen.register_mapgen_lvm(callback_function, order)
lvm_chunk = lvm_chunk + 1
safe_functions = safe_functions + 1
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)
end
function mcl_mapgen.register_mapgen_block(callback_function, order)
block = block + 1
nodes_block = nodes_block + 1
safe_functions = safe_functions + 1
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)
end
function mcl_mapgen.register_mapgen_block_lvm(callback_function, order)
block = block + 1
queue_blocks_lvm_counter = queue_blocks_lvm_counter + 1
safe_functions = safe_functions + 1
queue_blocks_lvm[queue_blocks_lvm_counter] = {order = order or DEFAULT_ORDER, callback_function = callback_function}
table.sort(queue_blocks_lvm, function(a, b) return (a.order <= b.order) 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 data, param2_data, light, area
local current_blocks = {}
local current_chunks = {}
local lvm_buffer, lvm_param2_buffer, lvm_light_buffer = {}, {}, {} -- Static buffer pointers
minetest.register_on_generated(function(minp, maxp, chunkseed)
local minp, maxp, chunkseed = minp, maxp, chunkseed
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)
area = VoxelArea:new({MinEdge=emin, MaxEdge=emax})
vm_context = {
data = data,
param2_data = param2_data,
light = light,
area = area,
lvm_buffer = lvm_buffer,
lvm_param2_buffer = lvm_param2_buffer,
lvm_light_buffer = lvm_light_buffer,
vm = vm,
emin = emin,
emax = emax,
minp = minp,
maxp = maxp,
chunkseed = chunkseed,
}
if safe_functions > 0 then
local x0, y0, z0 = minp.x, minp.y, minp.z
local bx0, by0, bz0 = math_floor(x0/BS), math_floor(y0/BS), math_floor(z0/BS)
local bx1, by1, bz1 = bx0 + LAST_BLOCK, by0 + LAST_BLOCK, bz0 + LAST_BLOCK -- only for entire chunk check
-- Keep `chunkseed` in `chunks[cx][cy][cz].seed` for further safe usage:
local cx0, cy0, cz0 = math_floor((bx0-offset)/CS), math_floor((by0-offset)/CS), math_floor((bz0-offset)/CS)
if not chunks[cx0] then chunks[cx0] = {} end
if not chunks[cx0][cy0] then chunks[cx0][cy0] = {} end
if not chunks[cx0][cy0][cz0] then
chunks[cx0][cy0][cz0] = {seed = chunkseed, counter = 0}
else
chunks[cx0][cy0][cz0].seed = chunkseed
end
local x1, y1, z1, x2, y2, z2 = emin.x, emin.y, emin.z, emax.x, emax.y, emax.z
local x, y, z = x1, y1, z1 -- iterate 7x7x7 mapchunk, {x,y,z} - first node pos. of mapblock
local bx, by, bz -- block coords (in blocs)
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
chunks[cx][cy][cz].counter = chunks[cx][cy][cz].counter + 1
if chunks[cx][cy][cz].counter >= CS_3D then
current_chunks[#current_chunks+1] = { x = cx, y = cy, z = cz, s = chunks[cx][cy][cz].seed }
-- this chunk shouldn't be overwritten anymore, no need to keep it in memory
chunks[cx][cy][cz] = nil
if next(chunks[cx][cy]) == nil then chunks[cx][cy] = nil end
if next(chunks[cx]) == nil then chunks[cx] = nil 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
z = z + BS
end
if next(blocks[bx][by]) == nil then blocks[bx][by] = nil end
z = z1
y = y + BS
end
if next(blocks[bx]) == nil then blocks[bx] = nil end
y = y1
x = x + BS
end
end
if #queue_unsafe_engine > 0 then
for _, v in pairs(queue_unsafe_engine) do
v.f(vm_context)
end
if vm_context.write then
vm:set_data(data)
end
if vm_context.write_param2 then
vm:set_param2_data(vm_context.param2_data)
end
if vm_context.write_light then
vm:set_light_data(light)
end
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:write_to_map()
vm:update_liquids()
elseif vm_context.calc_lighting then
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
end
end
for i, b in pairs(current_chunks) do
local cx, cy, cz, seed = b.x, b.y, b.z, b.s
local bx, by, bz = cx * CS + offset, cy * CS + offset, cz * CS + offset
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})
vm_context = {
data = data,
param2_data = param2_data,
light = light,
area = area,
lvm_buffer = lvm_buffer,
lvm_param2_buffer = lvm_param2_buffer,
lvm_light_buffer = lvm_light_buffer,
emin = minp,
emax = maxp,
minp = minp,
maxp = maxp,
chunkseed = seed,
}
for _, v in pairs(queue_chunks_lvm) do
vm_context = v.f(vm_context)
end
for _, v in pairs(queue_chunks_nodes) do
v.f(minp, maxp, seed, vm_context)
end
if vm_context.write or vm_context.write_param2 or vm_context.write_light then
if vm_context.write then
vm:set_data(data)
end
if vm_context.write_param2 then
vm:set_param2_data(param2_data)
end
if vm_context.write_light then
vm:set_light_data(light)
end
-- caused error from torches (?)
-- vm:calc_lighting(minp, maxp, vm_context.shadow or true)
vm:write_to_map()
vm:update_liquids()
elseif vm_context.calc_lighting then
vm:calc_lighting(minp, maxp, (vm_context.shadow ~= nil) or true)
end
current_chunks[i] = nil
end
for i, b in pairs(current_blocks) do
for _, v in pairs(queue_blocks_nodes) do
v.f(b.minp, b.maxp, b.seed)
end
current_blocks[i] = nil
end
end)
minetest.register_on_generated = mcl_mapgen.register_chunk_generator
function mcl_mapgen.get_far_node(p)
local p = p
local node = minetest_get_node(p)
if node.name ~= "ignore" then return node end
minetest_get_voxel_manip():read_from_map(p, p)
return minetest_get_node(p)
end
local function coordinate_to_block(x)
return math_floor(x / BS)
end
local function coordinate_to_chunk(x)
return math_floor((coordinate_to_block(x) - offset) / CS)
end
function mcl_mapgen.pos_to_block(pos)
return {
x = coordinate_to_block(pos.x),
y = coordinate_to_block(pos.y),
z = coordinate_to_block(pos.z)
}
end
function mcl_mapgen.pos_to_chunk(pos)
return {
x = coordinate_to_chunk(pos.x),
y = coordinate_to_chunk(pos.y),
z = coordinate_to_chunk(pos.z)
}
end
local k_positive = math.ceil(mcl_mapgen.MAX_LIMIT / mcl_mapgen.CS_NODES)
local k_positive_z = k_positive * 2
local k_positive_y = k_positive_z * k_positive_z
function mcl_mapgen.get_chunk_number(pos) -- unsigned int
local c = mcl_mapgen.pos_to_chunk(pos)
return
(c.y + k_positive) * k_positive_y +
(c.z + k_positive) * k_positive_z +
c.x + k_positive
end
mcl_mapgen.minecraft_height_limit = 256
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.min = -62
if superflat then
mcl_mapgen.ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
overworld.min = ground - 3
end
-- if singlenode then mcl_mapgen.overworld.min = -66 end -- DONT KNOW WHY
overworld.max = mcl_mapgen.EDGE_MAX
overworld.bedrock_min = overworld.min
overworld.bedrock_max = overworld.bedrock_min + (mcl_mapgen.bedrock_is_rough and 4 or 0)
mcl_mapgen.lava = normal
overworld.lava_max = overworld.min + (normal and 10 or 0)
-- The Nether (around Y = -29000)
nether.min = -29067 -- Carefully chosen to be at a mapchunk border
nether.max = nether.min + 128
nether.bedrock_bottom_min = nether.min
nether.bedrock_top_max = nether.max
if not superflat then
nether.bedrock_bottom_max = nether.bedrock_bottom_min + 4
nether.bedrock_top_min = nether.bedrock_top_max - 4
nether.lava_max = nether.min + 31
else
-- Thin bedrock in classic superflat mapgen
nether.bedrock_bottom_max = nether.bedrock_bottom_min
nether.bedrock_top_min = nether.bedrock_top_max
nether.lava_max = nether.min + 2
end
if mcl_mapgen.name == "flat" then
if superflat then
nether.flat_floor = nether.bedrock_bottom_max + 4
nether.flat_ceiling = nether.bedrock_bottom_max + 52
else
nether.flat_floor = nether.lava_max + 4
nether.flat_ceiling = nether.lava_max + 52
end
end
-- The End (surface at ca. Y = -27000)
end_.min = -27073 -- Carefully chosen to be at a mapchunk border
end_.max = overworld.min - 2000
end_.platform_pos = { x = 100, y = end_.min + 74, z = 0 }
-- Realm barrier used to safely separate the End from the void below the Overworld
mcl_mapgen.realm_barrier_overworld_end_max = end_.max
mcl_mapgen.realm_barrier_overworld_end_min = end_.max - 11
-- Use MineClone 2-style dungeons for normal mapgen
mcl_mapgen.dungeons = normal
mcl_mapgen.overworld = overworld
mcl_mapgen.end_ = end_
mcl_mapgen["end"] = mcl_mapgen.end_
mcl_mapgen.nether = nether
mcl_mapgen.order = order
function mcl_mapgen.get_voxel_manip(vm_context)
if vm_context.vm then
return vm
end
vm_context.vm = minetest.get_voxel_manip(vm_context.emin, vm_context.emax)
vm_context.emin, vm_context.emax = vm_context.vm:read_from_map(vm_context.emin, vm_context.emax)
vm_context.area = VoxelArea:new({MinEdge=vm_context.emin, MaxEdge=vm_context.emax})
return vm_context.vm
end
function mcl_mapgen.clamp_to_chunk(x, size)
if not size then
minetest.log("warning", "[mcl_mapgen] Couldn't clamp " .. tostring(x) .. " - missing size")
return x
end
if size > CS_NODES then
minetest.log("warning", "[mcl_mapgen] Couldn't clamp " .. tostring(x) .. " - given size " .. tostring(size) .. " greater than chunk size " .. tostring(mcl_mapgen.CS_NODES))
return x
end
local offset_in_chunk = (x + central_chunk_min_pos) % CS_NODES
local x2_in_chunk = offset_in_chunk + size
if x2_in_chunk <= CS_NODES then
return x
end
local overflow = x2_in_chunk - CS_NODES
if overflow > size / 2 then
local next_x = x + (size - overflow)
if next_x < mcl_mapgen.EDGE_MAX then
return next_x
end
end
return x - overflow
end
function mcl_mapgen.get_chunk_beginning(x)
return x - ((x + central_chunk_min_pos) % CS_NODES)
end

View File

@ -0,0 +1,4 @@
name = mcl_mapgen
author = kay27
description = MineClone 2/5 MapGen Basic Stuff
depends = mcl_init

View File

@ -175,16 +175,16 @@ end
-- check if within physical map limits (-30911 to 30927)
-- within_limits, wmin, wmax = nil, -30913, 30928
mobs.within_limits = function(pos, radius)
local wmin, wmax
if mcl_vars then
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
local wmin, wmax
if mcl_mapgen then
if mcl_mapgen.EDGE_MIN and mcl_mapgen.EDGE_MAX then
wmin, wmax = mcl_mapgen.EDGE_MIN, mcl_mapgen.EDGE_MAX
return pos
and (pos.x - radius) > wmin and (pos.x + radius) < wmax
and (pos.y - radius) > wmin and (pos.y + radius) < wmax
and (pos.z - radius) > wmin and (pos.z + radius) < wmax
end
end
return pos
and (pos.x - radius) > wmin and (pos.x + radius) < wmax
and (pos.y - radius) > wmin and (pos.y + radius) < wmax
and (pos.z - radius) > wmin and (pos.z + radius) < wmax
end
-- get node but use fallback for nil or unknown
@ -214,6 +214,7 @@ end
--a function used for despawning mobs
mobs.check_for_player_within_area = function(self, radius)
local pos1 = self.object:get_pos()
if not pos1 then return end
--get players in radius
for _,player in pairs(minetest_get_connected_players()) do
if player and player:get_hp() > 0 then

View File

@ -1,5 +1,5 @@
name = mcl_mobs
author = PilzAdam
description = Adds a mob API for mods to add animals or monsters, etc.
depends = mcl_particles
depends = mcl_mapgen, mcl_particles
optional_depends = mcl_weather, mcl_explosions, mcl_hunger, mcl_worlds, cmi, doc_identifier, mcl_armor, mcl_portals, mcl_experience

View File

@ -233,15 +233,15 @@ mobs_mc.override.spawn_height = {
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
-- Overworld boundaries (inclusive)
overworld_min = mcl_vars.mg_overworld_min,
overworld_max = mcl_vars.mg_overworld_max,
overworld_min = mcl_mapgen.overworld.min,
overworld_max = mcl_mapgen.overworld.max,
-- Nether boundaries (inclusive)
nether_min = mcl_vars.mg_nether_min,
nether_max = mcl_vars.mg_nether_max,
nether_min = mcl_mapgen.nether.min,
nether_max = mcl_mapgen.nether.max,
-- End boundaries (inclusive)
end_min = mcl_vars.mg_end_min,
end_max = mcl_vars.mg_end_max,
end_min = mcl_mapgen.end_.min,
end_max = mcl_mapgen.end_.max,
}

View File

@ -4,7 +4,8 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
local mg_name = minetest.get_mapgen_setting("mg_name")
local mg_name = mcl_mapgen.name
local v6 = mcl_mapgen.v6
local math = math
local vector = vector
@ -204,8 +205,23 @@ minetest.register_abm({
action = function(pos, node, active_object_count, active_object_count_wider)
for _, object in pairs(minetest.get_objects_inside_radius(pos, 0.9)) do
local entity = object:get_luaentity()
if entity and entity.name == "__builtin:item" then
object:remove()
if entity then
local entity_name = entity.name
if entity_name == "__builtin:item" then
object:remove()
elseif entity_name == "mcl_minecarts:minecart" then
local pos = object:get_pos()
local driver = entity._driver
if driver then
mcl_player.player_attached[driver] = nil
local player = minetest.get_player_by_name(driver)
player:set_detach()
player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
mcl_player.player_set_animation(player, "stand" , 30)
end
minetest.add_item(pos, "mcl_minecarts:minecart")
object:remove()
end
end
end
local posses = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }
@ -366,7 +382,7 @@ function mcl_core.generate_tree(pos, tree_type, options)
local balloon = options and options.balloon
if tree_type == nil or tree_type == OAK_TREE_ID then
if mg_name == "v6" then
if v6 then
mcl_core.generate_v6_oak_tree(pos)
else
if balloon then
@ -381,7 +397,7 @@ function mcl_core.generate_tree(pos, tree_type, options)
if two_by_two then
mcl_core.generate_huge_spruce_tree(pos)
else
if mg_name == "v6" then
if v6 then
mcl_core.generate_v6_spruce_tree(pos)
else
mcl_core.generate_spruce_tree(pos)
@ -393,7 +409,7 @@ function mcl_core.generate_tree(pos, tree_type, options)
if two_by_two then
mcl_core.generate_huge_jungle_tree(pos)
else
if mg_name == "v6" then
if v6 then
mcl_core.generate_v6_jungle_tree(pos)
else
mcl_core.generate_jungle_tree(pos)
@ -771,7 +787,7 @@ function mcl_core.generate_huge_jungle_tree(pos)
end
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
local grass_spread_randomizer = PseudoRandom(mcl_mapgen.seed)
function mcl_core.get_grass_palette_index(pos)
local biome_data = minetest.get_biome_data(pos)

View File

@ -1,4 +1,4 @@
name = mcl_core
description = Core items of MineClone 2: Basic biome blocks (dirt, sand, stones, etc.), derived items, glass, sugar cane, cactus, barrier, mining tools, hand, craftitems, and misc. items which don't really fit anywhere else.
depends = mcl_autogroup, mcl_init, mcl_sounds, mcl_particles, mcl_util, mcl_worlds, doc_items, mcl_enchanting, mcl_colors
depends = mcl_autogroup, mcl_init, mcl_sounds, mcl_particles, mcl_util, mcl_worlds, doc_items, mcl_enchanting, mcl_colors, mcl_mapgen
optional_depends = doc

View File

@ -1,4 +1,4 @@
name = mcl_portals
description = Adds buildable portals to the Nether and End dimensions.
depends = mcl_nether, mcl_end, mcl_particles, mcl_spawn, mcl_credits
depends = mcl_mapgen, mcl_nether, mcl_end, mcl_particles, mcl_spawn, mcl_credits, mcl_structures
optional_depends = awards, doc

View File

@ -6,12 +6,6 @@ local math = math
local has_doc = minetest.get_modpath("doc")
-- Parameters
--local SPAWN_MIN = mcl_vars.mg_end_min+70
--local SPAWN_MAX = mcl_vars.mg_end_min+98
--local mg_name = minetest.get_mapgen_setting("mg_name")
local function destroy_portal(pos)
local neighbors = {
{ x=1, y=0, z=0 },
@ -184,7 +178,7 @@ function mcl_portals.end_teleport(obj, pos)
-- Teleport to the End at a fixed position and generate a
-- 5×5 obsidian platform below.
local platform_pos = mcl_vars.mg_end_platform_pos
local platform_pos = mcl_mapgen.end_.platform_pos
-- force emerge of target1 area
minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos)
if not minetest.get_node_or_nil(platform_pos) then

View File

@ -19,7 +19,7 @@ local W_MIN, W_MAX = 4, 23
local H_MIN, H_MAX = 5, 23
local N_MIN, N_MAX = 6, (W_MAX-2) * (H_MAX-2)
local TRAVEL_X, TRAVEL_Y, TRAVEL_Z = 8, 1, 8
local LIM_MIN, LIM_MAX = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
local LIM_MIN, LIM_MAX = mcl_mapgen.EDGE_MIN, mcl_mapgen.EDGE_MAX
local PLAYER_COOLOFF, MOB_COOLOFF = 3, 14 -- for this many seconds they won't teleported again
local TOUCH_CHATTER_TIME = 1 -- prevent multiple teleportation attempts caused by multiple portal touches, for this number of seconds
local CHATTER_US = TOUCH_CHATTER_TIME * 1000000
@ -27,8 +27,8 @@ local DELAY = 3 -- seconds before teleporting in Nether portal in Survival mo
local DISTANCE_MAX = 128
local PORTAL = "mcl_portals:portal"
local OBSIDIAN = "mcl_core:obsidian"
local O_Y_MIN, O_Y_MAX = max(mcl_vars.mg_overworld_min, -31), min(mcl_vars.mg_overworld_max, 2048)
local N_Y_MIN, N_Y_MAX = mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_top_min - H_MIN
local O_Y_MIN, O_Y_MAX = max(mcl_mapgen.overworld.min, -31), min(mcl_mapgen.overworld.max, 2048)
local N_Y_MIN, N_Y_MAX = mcl_mapgen.nether.bedrock_bottom_min, mcl_mapgen.nether.bedrock_top_min - H_MIN
-- Alpha and particles
local node_particles_allowed = minetest.settings:get("mcl_node_particles") or "none"
@ -66,7 +66,7 @@ minetest.register_on_shutdown(function()
storage:set_string("nether_exits_keys", minetest.serialize(keys))
end)
local get_node = mcl_vars.get_node
local get_node = mcl_mapgen.get_far_node
local set_node = minetest.set_node
local registered_nodes = minetest.registered_nodes
local is_protected = minetest.is_protected
@ -137,19 +137,8 @@ local function find_exit(p, dx, dy, dz)
if not p or not p.y or not p.z or not p.x then return end
local dx, dy, dz = dx or DISTANCE_MAX, dy or DISTANCE_MAX, dz or DISTANCE_MAX
if dx < 1 or dy < 1 or dz < 1 then return false end
--y values aren't used
local x = floor(p.x)
--local y = floor(p.y)
local z = floor(p.z)
local x1 = x-dx+1
--local y1 = y-dy+1
local z1 = z-dz+1
local x2 = x+dx-1
--local y2 = y+dy-1
local z2 = z+dz-1
local x, y, z = floor(p.x), floor(p.y), floor(p.z)
local x1, y1, z1, x2, y2, z2 = x-dx+1, y-dy+1, z-dz+1, x+dx-1, y+dy-1, z+dz-1
local k1x, k2x = floor(x1/256), floor(x2/256)
local k1z, k2z = floor(z1/256), floor(z2/256)
@ -170,11 +159,27 @@ local function find_exit(p, dx, dy, dz)
end
end end
if t and abs(t.x-p.x) <= dx and abs(t.y-p.y) <= dy and abs(t.z-p.z) <= dz then
if t and abs(t.x-x) <= dx and abs(t.y-y) <= dy and abs(t.z-z) <= dz then
return t
end
end
-- This functon searches Nether portal nodes whitin distance specified and checks the node
local function find_exit_with_check(p, dx, dy, dz)
while true do
local pos = find_exit(p, dx, dy, dz)
if not pos then
-- not found:
return
end
if (get_node(pos).name == PORTAL) then
return pos
end
-- I don't know the reason why it can happen, but if we're here, let's log it, remove this record and try again:
log("warning", "[mcl_portals] Found faulty exit from Nether portal at " .. pos_to_string(pos) .. " - removed")
remove_exit(pos)
end
end
-- Ping-Pong the coordinate for Fast Travelling, https://git.minetest.land/Wuzzy/MineClone2/issues/795#issuecomment-11058
local function ping_pong(x, m, l1, l2)
@ -350,7 +355,7 @@ function build_nether_portal(pos, width, height, orientation, name, clear_before
return pos
end
function mcl_portals.spawn_nether_portal(pos, rot, pr, name)
function mcl_portals.spawn_nether_portal(pos, rot, pr, placer)
if not pos then return end
local o = 0
if rot then
@ -360,6 +365,10 @@ function mcl_portals.spawn_nether_portal(pos, rot, pr, name)
o = random(0,1)
end
end
local name
if placer and placer:is_player() then
name = placer:get_player_name()
end
build_nether_portal(pos, nil, nil, o, name, true)
end
@ -432,7 +441,7 @@ local function create_portal_2(pos1, name, obj)
end
local exit = build_nether_portal(pos1, W_MIN-2, H_MIN-2, orientation, name)
finalize_teleport(obj, exit)
local cn = mcl_vars.get_chunk_number(pos1)
local cn = mcl_mapgen.get_chunk_number(pos1)
chunks[cn] = nil
if queue[cn] then
for next_obj, _ in pairs(queue[cn]) do
@ -446,9 +455,9 @@ end
local function get_lava_level(pos, pos1, pos2)
if pos.y > -1000 then
return max(min(mcl_vars.mg_lava_overworld_max, pos2.y-1), pos1.y+1)
return max(min(mcl_mapgen.overworld.lava_max, pos2.y-1), pos1.y+1)
end
return max(min(mcl_vars.mg_lava_nether_max, pos2.y-1), pos1.y+1)
return max(min(mcl_mapgen.nether.lava_max, pos2.y-1), pos1.y+1)
end
local function ecb_scan_area_2(blockpos, action, calls_remaining, param)
@ -464,7 +473,7 @@ local function ecb_scan_area_2(blockpos, action, calls_remaining, param)
for _, p in pairs(portals) do
add_exit(p)
end
local exit = find_exit(pos)
local exit = find_exit_with_check(pos)
if exit then
finalize_teleport(obj, exit)
end
@ -527,7 +536,7 @@ local function ecb_scan_area_2(blockpos, action, calls_remaining, param)
end
local function create_portal(pos, limit1, limit2, name, obj)
local cn = mcl_vars.get_chunk_number(pos)
local cn = mcl_mapgen.get_chunk_number(pos)
if chunks[cn] then
local q = queue[cn] or {}
q[obj] = true
@ -540,8 +549,8 @@ local function create_portal(pos, limit1, limit2, name, obj)
-- so we'll emerge single chunk only: 5x5x5 blocks, 80x80x80 nodes maximum
-- and maybe one more chunk from below if (SCAN_2_MAP_CHUNKS = true)
local pos1 = add(mul(mcl_vars.pos_to_chunk(pos), mcl_vars.chunk_size_in_nodes), mcl_vars.central_chunk_offset_in_nodes)
local pos2 = add(pos1, mcl_vars.chunk_size_in_nodes - 1)
local pos1 = add(mul(mcl_mapgen.pos_to_chunk(pos), mcl_mapgen.CS_NODES), mcl_mapgen.OFFSET_NODES)
local pos2 = add(pos1, mcl_mapgen.CS_NODES - 1)
if not SCAN_2_MAP_CHUNKS then
if limit1 and limit1.x and limit1.y and limit1.z then
@ -555,8 +564,8 @@ local function create_portal(pos, limit1, limit2, name, obj)
end
-- Basically the copy of code above, with minor additions to continue the search in single additional chunk below:
local next_chunk_1 = {x = pos1.x, y = pos1.y - mcl_vars.chunk_size_in_nodes, z = pos1.z}
local next_chunk_2 = add(next_chunk_1, mcl_vars.chunk_size_in_nodes - 1)
local next_chunk_1 = {x = pos1.x, y = pos1.y - mcl_mapgen.CS_NODES, z = pos1.z}
local next_chunk_2 = add(next_chunk_1, mcl_mapgen.CS_NODES - 1)
local next_pos = {x = pos.x, y=max(next_chunk_2.y, limit1.y), z = pos.z}
if limit1 and limit1.x and limit1.y and limit1.z then
pos1 = {x = max(min(limit1.x, pos.x), pos1.x), y = max(min(limit1.y, pos.y), pos1.y), z = max(min(limit1.z, pos.z), pos1.z)}
@ -682,11 +691,12 @@ local function teleport_no_delay(obj, pos)
name = obj:get_player_name()
end
local exit = find_exit(target)
local exit = find_exit_with_check(target)
if exit then
finalize_teleport(obj, exit)
else
dim = dimension_to_teleport[dim]
if not dim then return end
-- need to create arrival portal
create_portal(target, limits[dim].pmin, limits[dim].pmax, name, obj)
end
@ -748,6 +758,8 @@ local function teleport(obj, portal_pos)
minetest.after(DELAY, teleport_no_delay, obj, portal_pos)
end
mcl_structures.register_structure({name = "nether_portal", place_function = mcl_portals.spawn_nether_portal})
minetest.register_abm({
label = "Nether portal teleportation and particles",
nodenames = {PORTAL},

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
name = mcl_biomes
author = maikerumine
description = Adds the various biomes and biome-related things for non-v6 map generators.
depends = mcl_init, mcl_mapgen_core, mcl_core, mcl_worlds, mcl_farming, mcl_flowers, mcl_end, mcl_ocean
depends = mcl_mapgen, mcl_mapgen_core, mcl_core, mcl_worlds, mcl_farming, mcl_flowers, mcl_end, mcl_ocean

View File

@ -2,21 +2,19 @@
mcl_dungeons = {}
local mg_name = minetest.get_mapgen_setting("mg_name")
-- Are dungeons disabled?
if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then
if mcl_mapgen.dungeons == false or mcl_mapgen.singlenode == true then
return
end
--lua locals
--minetest
local minetest_find_nodes_in_area = minetest.find_nodes_in_area
local registered_nodes = minetest.registered_nodes
local swap_node = minetest.swap_node
local set_node = minetest.set_node
local dir_to_facedir = minetest.dir_to_facedir
local get_meta = minetest.get_meta
local emerge_area = minetest.emerge_area
--vector
local vector_add = vector.add
@ -32,15 +30,15 @@ local math_max = math.max
local math_ceil = math.ceil
--custom mcl_vars
local get_node = mcl_vars.get_node
local get_node = mcl_mapgen.get_far_node
local min_y = math_max(mcl_vars.mg_overworld_min, mcl_vars.mg_bedrock_overworld_max) + 1
local max_y = mcl_vars.mg_overworld_max - 1
local min_y = math_max(mcl_mapgen.overworld.min, mcl_mapgen.overworld.bedrock_max) + 1
local max_y = mcl_mapgen.overworld.max - 1
-- Calculate the number of dungeon spawn attempts
-- In Minecraft, there 8 dungeon spawn attempts Minecraft chunk (16*256*16 = 65536 blocks).
-- Minetest chunks don't have this size, so scale the number accordingly.
local attempts = math_ceil(((mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE) ^ 3) / 8192) -- 63 = 80*80*80/8192
local attempts = math_ceil((mcl_mapgen.CS_NODES ^ 3) / 8192) -- 63 = 80*80*80/8192
local dungeonsizes = {
{ x=5, y=4, z=5},
@ -112,7 +110,7 @@ local loottable =
}
-- Bonus loot for v6 mapgen: Otherwise unobtainable saplings.
if mg_name == "v6" then
if mcl_mapgen.v6 then
table.insert(loottable, {
stacks_min = 1,
stacks_max = 3,
@ -124,20 +122,28 @@ if mg_name == "v6" then
})
end
local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end
--local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
-- if calls_remaining >= 1 then return end
-- local p1, _, dim, pr = param.p1, param.p2, param.dim, param.pr
-- local check = not (param.dontcheck or false)
local m1, m2 = 0, 0
local function spawn_dungeon(p1, p2, dim, pr, dontcheck)
local p1, _, dim, pr = param.p1, param.p2, param.dim, param.pr
local x, y, z = p1.x, p1.y, p1.z
local check = not (param.dontcheck or false)
local check = not (dontcheck or false)
-- Check floor and ceiling: Must be *completely* solid
local y_floor = y
local y_ceiling = y + dim.y + 1
if check then for tx = x+1, x+dim.x do for tz = z+1, z+dim.z do
if not registered_nodes[get_node({x = tx, y = y_floor , z = tz}).name].walkable
or not registered_nodes[get_node({x = tx, y = y_ceiling, z = tz}).name].walkable then return false end
end end end
if check then
local dim_x, dim_z = dim.x, dim.z
local size = dim_z*dim_x
if #minetest_find_nodes_in_area({x=x+1,y=y_floor,z=z+1}, {x=x+dim_z,y=y_floor,z=z+dim_z}, "group:walkabke") < size
or #minetest_find_nodes_in_area({x=x+1,y=y_floor,z=z+1}, {x=x+dim_z,y=y_floor,z=z+dim_z}, "group:walkabke") < size then
return
end
end
-- Check for air openings (2 stacked air at ground level) in wall positions
local openings_counter = 0
@ -404,8 +410,7 @@ local function dungeons_nodes(minp, maxp, blockseed)
local z = pr:next(minp.z, maxp.z-dim.z-1)
local p1 = {x=x,y=y,z=z}
local p2 = {x = x+dim.x+1, y = y+dim.y+1, z = z+dim.z+1}
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr})
spawn_dungeon(p1, p2, dim, pr)
end
end
@ -413,8 +418,7 @@ function mcl_dungeons.spawn_dungeon(p1, _, pr)
if not p1 or not pr or not p1.x or not p1.y or not p1.z then return end
local dim = dungeonsizes[pr:next(1, #dungeonsizes)]
local p2 = {x = p1.x+dim.x+1, y = p1.y+dim.y+1, z = p1.z+dim.z+1}
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr, dontcheck=true})
spawn_dungeon(p1, p2, dim, pr, true)
end
mcl_mapgen_core.register_generator("dungeons", nil, dungeons_nodes, 999999)
mcl_mapgen.register_mapgen(dungeons_nodes, mcl_mapgen.order.DUNGEONS)

View File

@ -1,4 +1,4 @@
name = mcl_dungeons
author = Wuzzy
description = Generates random dungeons in the world
depends = mcl_init, mcl_core, mcl_chests, mcl_mobs, mcl_mobspawners, mcl_mapgen_core, mobs_mc
depends = mcl_mapgen, mcl_core, mcl_chests, mcl_mobs, mcl_mobspawners, mcl_mapgen_core, mobs_mc

View File

@ -10,25 +10,21 @@ local noisemap = PerlinNoiseMap({
local c_end_stone = minetest.get_content_id("mcl_end:end_stone")
local y_offset = -2
minetest.register_on_generated(function(minp, maxp)
mcl_mapgen.register_on_generated(function(vm_context)
local minp, maxp = vm_context.minp, vm_context.maxp
if maxp.y < (-27025 + y_offset) or minp.y > (-27000 + y_offset + 4) or maxp.x < -75 or minp.x > 75 or maxp.z < -75 or minp.z > 75 then
return
end
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local data = vm:get_data()
local area = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
local data = vm_context.data
local area = vm_context.area
local write = false
for idx in area:iter(math.max(minp.x, -75), math.max(minp.y, -27025 + y_offset + 4), math.max(minp.z, -75), math.min(maxp.x, 75), math.min(maxp.y, -27000 + y_offset), math.min(maxp.z, 75)) do
local pos = area:position(idx)
local y = 27025 + pos.y - y_offset
if noisemap[pos.x + 75 + 1][y + 1][pos.z + 75 + 1] > (math.abs(1 - y / 25) ^ 2 + math.abs(pos.x / 75) ^ 2 + math.abs(pos.z / 75) ^ 2) then
data[idx] = c_end_stone
write = true
end
end
vm:set_data(data)
vm:calc_lighting()
vm:update_liquids()
vm:write_to_map()
vm_context.write = vm_context.write or write
end)

View File

@ -0,0 +1,63 @@
local c_water = minetest.get_content_id("mcl_core:water_source")
local c_dirt = minetest.get_content_id("mcl_core:dirt")
local c_clay = minetest.get_content_id("mcl_core:clay")
local perlin_clay
local math_max = math.max
local math_min = math.min
local math_floor = math.floor
local math_abs = math.abs
local offset = math_floor(mcl_mapgen.BS / 2)
local minetest_get_item_group = minetest.get_item_group
local minetest_get_name_from_content_id = minetest.get_name_from_content_id
mcl_mapgen.register_mapgen_block_lvm(function(c)
local minp, maxp, blockseed, voxelmanip_data, voxelmanip_area = c.minp, c.maxp, c.blockseed, c.data, c.area
local max_y = maxp.y
if max_y < -7 then return end
local min_y = minp.y
if min_y > 0 then return end
c.vm = c.vm or mcl_mapgen.get_voxel_manip(c)
local pr = PseudoRandom(blockseed)
perlin_clay = perlin_clay or minetest.get_perlin({
offset = 0.5,
scale = 0.2,
spread = {x = 5, y = 5, z = 5},
seed = -316,
octaves = 1,
persist = 0.0
})
for y = math_max(min_y, -8), math_min(max_y, 0) do
-- Assume X and Z lengths are equal
local x = minp.x + offset + pr:next(-2, 2)
local z = minp.z + offset + pr:next(-2, 2)
if perlin_clay:get_3d({x = x, y = y, z = z}) + pr:next(1, 20) > 19 then
-- Get position and shift it a bit randomly so the clay do not obviously appear in a grid
local water_pos = voxelmanip_area:index(x, y + 1, z)
local water_node = voxelmanip_data[water_pos]
if water_node == c_water or water_node == c_clay then
local surface_pos = voxelmanip_area:index(x, y, z)
local surface_node = voxelmanip_data[surface_pos]
if (surface_node == c_dirt or surface_node == c_clay or minetest_get_item_group(minetest_get_name_from_content_id(surface_node), "sand") == 1) then
local diamondsize = pr:next(1, 3)
for x1 = -diamondsize, diamondsize do
local abs_x1 = math_abs(x1)
for z1 = -(diamondsize - abs_x1), diamondsize - abs_x1 do
local ccpos = voxelmanip_area:index(x + x1, y, z + z1)
local claycandidate = voxelmanip_data[ccpos]
if voxelmanip_data[ccpos] == c_dirt or minetest_get_item_group(minetest_get_name_from_content_id(claycandidate), "sand") == 1 then
voxelmanip_data[ccpos] = c_clay
c.write = true
end
end
end
end
end
end
end
end)

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
name = mcl_mapgen_core
author = Wuzzy
description = The core of the MCL2 mapgen
depends = mcl_init, mcl_core, biomeinfo, mcl_worlds, mcl_cocoas, mcl_sponges, mcl_ocean, mcl_stairs, mcl_monster_eggs, mcl_structures
optional_depends = mclx_core
depends = mcl_mapgen, mcl_init, mcl_core, biomeinfo, mcl_worlds
optional_depends = mclx_core, mcl_cocoas, mcl_sponges, mcl_ocean, mcl_stairs, mcl_monster_eggs, mcl_structures, mcl_flowers, mcl_farming, mcl_mushrooms, mcl_nether

View File

@ -0,0 +1,251 @@
-- Generate tree decorations in the bounding box. This adds:
-- * Cocoa at jungle trees
-- * Jungle tree vines
-- * Oak vines in swamplands
local minetest_find_nodes_in_area = minetest.find_nodes_in_area
local minetest_find_node_near = minetest.find_node_near
local minetest_get_node_light = minetest.get_node_light
local minetest_dir_to_facedir = minetest.dir_to_facedir
local minetest_dir_to_wallmounted = minetest.dir_to_wallmounted
local table_copy = table.copy
local vector_subtract = vector.subtract
local vector_add = vector.add
local math_max = math.max
local math_ceil = math.ceil
local math_abs = math.abs
local c_air = minetest.CONTENT_AIR
local c_cocoas
local c_jungleleaves = minetest.get_content_id("mcl_core:jungleleaves")
local c_leaves = minetest.get_content_id("mcl_core:leaves")
local c_vine = minetest.get_content_id("mcl_core:vine")
if minetest.get_modpath("mcl_cocoas") then
c_cocoas = {
minetest.get_content_id("mcl_cocoas:cocoa_1"),
minetest.get_content_id("mcl_cocoas:cocoa_2"),
minetest.get_content_id("mcl_cocoas:cocoa_3"),
}
end
local swampland
local swampland_shore
local jungle
local jungle_shore
local jungle_m
local jungle_m_shore
local jungle_edge
local jungle_edge_shore
local jungle_edge_m
local jungle_edge_m_shore
local perlin_vines, perlin_vines_fine, perlin_vines_upwards, perlin_vines_length, perlin_vines_density
local dirs = {
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z = -1},
}
local function generate_tree_decorations(vm_context)
local maxp = vm_context.maxp
if maxp.y < 0 then return end
local minp = vm_context.minp
local data = vm_context.data
vm_context.param2_data = vm_context.param2_data or vm_context.vm:get_param2_data(vm_context.lvm_param2_buffer)
local param2_data = vm_context.param2_data
local area = vm_context.area
local biomemap = vm_context.biomemap
local pr = PseudoRandom(vm_context.chunkseed)
local oaktree, oakleaves, jungletree, jungleleaves = {}, {}, {}, {}
-- Modifier for Jungle M biome: More vines and cocoas
local dense_vegetation = false
if biomemap then
swampland = swampland or minetest.get_biome_id("Swampland")
swampland_shore = swampland_shore or minetest.get_biome_id("Swampland_shore")
jungle = jungle or minetest.get_biome_id("Jungle")
jungle_shore = jungle_shore or minetest.get_biome_id("Jungle_shore")
jungle_m = jungle_m or minetest.get_biome_id("JungleM")
jungle_m_shore = jungle_m_shore or minetest.get_biome_id("JungleM_shore")
jungle_edge = jungle_edge or minetest.get_biome_id("JungleEdge")
jungle_edge_shore = jungle_edge_shore or minetest.get_biome_id("JungleEdge_shore")
jungle_edge_m = jungle_edge_m or minetest.get_biome_id("JungleEdgeM")
jungle_edge_m_shore = jungle_edge_m_shore or minetest.get_biome_id("JungleEdgeM_shore")
-- Biome map available: Check if the required biome (jungle or swampland)
-- is in this mapchunk. We are only interested in trees in the correct biome.
-- The nodes are added if the correct biome is *anywhere* in the mapchunk.
-- TODO: Strictly generate vines in the correct biomes only.
local swamp_biome_found, jungle_biome_found = false, false
for b=1, #biomemap do
local id = biomemap[b]
if not swamp_biome_found and (id == swampland or id == swampland_shore) then
oaktree = minetest_find_nodes_in_area(minp, maxp, {"mcl_core:tree"})
oakleaves = minetest_find_nodes_in_area(minp, maxp, {"mcl_core:leaves"})
swamp_biome_found = true
end
if not jungle_biome_found and (id == jungle or id == jungle_shore or id == jungle_m or id == jungle_m_shore or id == jungle_edge or id == jungle_edge_shore or id == jungle_edge_m or id == jungle_edge_m_shore) then
jungletree = minetest_find_nodes_in_area(minp, maxp, {"mcl_core:jungletree"})
jungleleaves = minetest_find_nodes_in_area(minp, maxp, {"mcl_core:jungleleaves"})
jungle_biome_found = true
end
if not dense_vegetation and (id == jungle_m or id == jungle_m_shore) then
dense_vegetation = true
end
if swamp_biome_found and jungle_biome_found and dense_vegetation then
break
end
end
else
-- If there is no biome map, we just count all jungle things we can find.
-- Oak vines will not be generated.
jungletree = minetest_find_nodes_in_area(minp, maxp, {"mcl_core:jungletree"})
jungleleaves = minetest_find_nodes_in_area(minp, maxp, {"mcl_core:jungleleaves"})
end
local pos, treepos, dir
if c_cocoas then
local cocoachance = 40
if dense_vegetation then
cocoachance = 32
end
-- Pass 1: Generate cocoas at jungle trees
for n = 1, #jungletree do
pos = table_copy(jungletree[n])
treepos = table_copy(pos)
if minetest_find_node_near(pos, 1, {"mcl_core:jungleleaves"}) then
dir = pr:next(1, cocoachance)
if dir == 1 then
pos.z = pos.z + 1
elseif dir == 2 then
pos.z = pos.z - 1
elseif dir == 3 then
pos.x = pos.x + 1
elseif dir == 4 then
pos.x = pos.x -1
end
local p_pos = area:index(pos.x, pos.y, pos.z)
local l = minetest_get_node_light(pos)
if dir < 5
and data[p_pos] == c_air
and l and l > 12 then
local c = pr:next(1, 3)
data[p_pos] = c_cocoas[c]
vm_context.write = true
param2_data[p_pos] = minetest_dir_to_facedir(vector_subtract(treepos, pos))
vm_context.write_param2 = true
end
end
end
end
-- Pass 2: Generate vines at jungle wood, jungle leaves in jungle and oak wood, oak leaves in swampland
perlin_vines = perlin_vines or minetest.get_perlin(555, 4, 0.6, 500)
perlin_vines_fine = perlin_vines_fine or minetest.get_perlin(43000, 3, 0.6, 1)
perlin_vines_length = perlin_vines_length or minetest.get_perlin(435, 4, 0.6, 75)
perlin_vines_upwards = perlin_vines_upwards or minetest.get_perlin(436, 3, 0.6, 10)
perlin_vines_density = perlin_vines_density or minetest.get_perlin(436, 3, 0.6, 500)
-- Extra long vines in Jungle M
local maxvinelength = 7
if dense_vegetation then
maxvinelength = 14
end
local treething
for i=1, 4 do
if i==1 then
treething = jungletree
elseif i == 2 then
treething = jungleleaves
elseif i == 3 then
treething = oaktree
elseif i == 4 then
treething = oakleaves
end
for n = 1, #treething do
pos = treething[n]
treepos = table_copy(pos)
for d = 1, #dirs do
local pos = vector_add(pos, dirs[d])
local p_pos = area:index(pos.x, pos.y, pos.z)
local vine_threshold = math_max(0.33333, perlin_vines_density:get_2d(pos))
if dense_vegetation then
vine_threshold = vine_threshold * (2/3)
end
if perlin_vines:get_2d(pos) > -1.0 and perlin_vines_fine:get_3d(pos) > vine_threshold and data[p_pos] == c_air then
local rdir = {}
rdir.x = -dirs[d].x
rdir.y = dirs[d].y
rdir.z = -dirs[d].z
local param2 = minetest_dir_to_wallmounted(rdir)
-- Determine growth direction
local grow_upwards = false
-- Only possible on the wood, not on the leaves
if i == 1 then
grow_upwards = perlin_vines_upwards:get_3d(pos) > 0.8
end
if grow_upwards then
-- Grow vines up 1-4 nodes, even through jungleleaves.
-- This may give climbing access all the way to the top of the tree :-)
-- But this will be fairly rare.
local length = math_ceil(math_abs(perlin_vines_length:get_3d(pos)) * 4)
for l=0, length-1 do
local t_pos = area:index(treepos.x, treepos.y, treepos.z)
if (data[p_pos] == c_air or data[p_pos] == c_jungleleaves or data[p_pos] == c_leaves) and mcl_core.supports_vines(minetest.get_name_from_content_id(data[t_pos])) then
data[p_pos] = c_vine
param2_data[p_pos] = param2
vm_context.write = true
else
break
end
pos.y = pos.y + 1
p_pos = area:index(pos.x, pos.y, pos.z)
treepos.y = treepos.y + 1
end
else
-- Grow vines down, length between 1 and maxvinelength
local length = math_ceil(math_abs(perlin_vines_length:get_3d(pos)) * maxvinelength)
for l=0, length-1 do
if data[p_pos] == c_air then
data[p_pos] = c_vine
param2_data[p_pos] = param2
vm_context.write = true
else
break
end
pos.y = pos.y - 1
p_pos = area:index(pos.x, pos.y, pos.z)
end
end
end
end
end
end
end
mcl_mapgen.register_on_generated(generate_tree_decorations, 0)

View File

@ -0,0 +1,97 @@
-- Check it:
-- seed 1, v7 mapgen
-- /teleport 14958,8,11370
local mcl_mapgen_get_far_node = mcl_mapgen.get_far_node
local minetest_log = minetest.log
local minetest_place_schematic = minetest.place_schematic
local minetest_pos_to_string = minetest.pos_to_string
local minetest_swap_node = minetest.swap_node
local path = minetest.get_modpath("mcl_ocean_monument") .. "/schematics/ocean_monument.mts"
local water = "mcl_core:water_source"
local air = "air"
local ice = "mcl_core:ice"
local leg_materials = {
"mcl_ocean:prismarine_brick",
"mcl_ocean:prismarine",
}
local what_we_can_replace_by_legs = {
water,
air,
"mcl_core:water_flowing",
"mcl_core:stone",
}
local leg_search_quick_index = {}
for _, v in pairs(leg_materials) do
leg_search_quick_index[v] = true
end
local leg_replace_quick_index = {}
for _, v in pairs(what_we_can_replace_by_legs) do
leg_replace_quick_index[v] = true
end
local y_wanted = mcl_mapgen.OFFSET_NODES -- supposed to be -32
local y_bottom = mcl_mapgen.overworld.min -- -62
mcl_mapgen.register_mapgen(function(minp, maxp, seed)
local minp = minp
local y = minp.y
if y ~= y_wanted then return end
local x, z = minp.x, minp.z
local pr = PseudoRandom(seed)
-- scan the ocean - it should be the ocean:
for i = 1, pr:next(10, 100) do
local pos = {x = pr:next(15, 64) + x, y = pr:next(0, 25) - 25, z = pr:next(15, 64) + z}
local node_name = mcl_mapgen_get_far_node(pos).name
if node_name ~= water then return end
end
-- scan nodes above water level - there should be the air:
for i = 1, pr:next(10, 100) do
local pos = {x = pr:next(0, 79) + x, y = 2, z = pr:next(0,79) + z}
local node_name = mcl_mapgen_get_far_node(pos).name
if node_name ~= air then return end
end
-- scan ocean surface - allow only water and ice:
for i = 1, pr:next(10,100) do
local pos = {x=pr:next(0, 79)+x, y=1, z=pr:next(0,79)+z}
local node_name = mcl_mapgen_get_far_node(pos).name
if node_name ~= water and node_name ~= ice then return end
end
-- random rotation:
local rotation = pr:next(0, 3)
local rotation_str = tostring(rotation * 90)
minetest_place_schematic(minp, path, rotation_str, nil, true)
-- search prismarine legs at base level and continue them up to the bottom:
for x = x, maxp.x do
for z = z, maxp.z do
local pos = {x = x, y = y, z = z}
local node_name = mcl_mapgen_get_far_node(pos).name
if leg_search_quick_index[node_name] then
local node_leg = {name = node_name}
for y = y - 1, y_bottom, -1 do
pos.y = y
local next_name = mcl_mapgen_get_far_node(pos).name
if not leg_replace_quick_index[next_name] then
break
end
minetest_swap_node(pos, node_leg)
end
end
end
end
minetest_log("action", "[mcl_ocean_monument] Placed at " .. minetest_pos_to_string(minp) .. ", " .. rotation_str .. " deg.")
end, mcl_mapgen.order.OCEAN_MONUMENT)

View File

@ -0,0 +1,4 @@
name = mcl_ocean_monument
author = TrashPanda
description = Adds Ocean Monument, https://git.minetest.land/MineClone2/MineClone2/issues/958#issuecomment-14102
depends = mcl_mapgen, mcl_structures

View File

@ -1,106 +0,0 @@
-- Generate strongholds.
-- A total of 128 strongholds are generated in rings around the world origin.
-- This is the list of rings, starting with the innermost ring first.
local stronghold_rings = {
-- amount: Number of strongholds in ring.
-- min, max: Minimum and maximum distance from (X=0, Z=0).
{ amount = 3, min = 1408, max = 2688 },
{ amount = 6, min = 4480, max = 5760 },
{ amount = 10, min = 7552, max = 8832 },
{ amount = 15, min = 10624, max = 11904 },
{ amount = 21, min = 13696, max = 14976 },
{ amount = 28, min = 16768, max = 18048 },
{ amount = 36, min = 19840, max = 21120 },
{ amount = 9, min = 22912, max = 24192 },
}
local strongholds = {}
local strongholds_inited = false
local mg_name = minetest.get_mapgen_setting("mg_name")
local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
-- Determine the stronghold positions and store them into the strongholds table.
-- The stronghold positions are based on the world seed.
-- The actual position might be offset by a few blocks because it might be shifted
-- to make sure the end portal room is completely within the boundaries of a mapchunk.
local function init_strongholds()
if strongholds_inited then
return
end
-- Don't generate strongholds in singlenode
if mg_name == "singlenode" then
strongholds_inited = true
return
end
local seed = tonumber(minetest.get_mapgen_setting("seed"))
local pr = PseudoRandom(seed)
for s=1, #stronghold_rings do
local ring = stronghold_rings[s]
-- Get random angle
local angle = pr:next()
-- Scale angle to 0 .. 2*math.pi
angle = (angle / 32767) * (math.pi*2)
for a=1, ring.amount do
local dist = pr:next(ring.min, ring.max)
local y
if superflat then
y = mcl_vars.mg_bedrock_overworld_max + 3
else
y = pr:next(mcl_vars.mg_bedrock_overworld_max+1, mcl_vars.mg_overworld_min+48)
end
local pos = { x = math.cos(angle) * dist, y = y, z = math.sin(angle) * dist }
pos = vector.round(pos)
table.insert(strongholds, { pos = pos, generated = false })
-- Rotate angle by (360 / amount) degrees.
-- This will cause the angles to be evenly distributed in the stronghold ring
angle = math.fmod(angle + ((math.pi*2) / ring.amount), math.pi*2)
end
end
mcl_structures.register_structures("stronghold", table.copy(strongholds))
strongholds_inited = true
end
-- Stronghold generation for register_on_generated.
local function generate_strongholds(minp, maxp, blockseed)
local pr = PseudoRandom(blockseed)
for s=1, #strongholds do
if not strongholds[s].generated then
local pos = strongholds[s].pos
if minp.x <= pos.x and maxp.x >= pos.x and minp.z <= pos.z and maxp.z >= pos.z and minp.y <= pos.y and maxp.y >= pos.y then
-- Make sure the end portal room is completely within the current mapchunk
-- The original pos is changed intentionally.
if pos.x - 6 < minp.x then
pos.x = minp.x + 7
end
if pos.x + 6 > maxp.x then
pos.x = maxp.x - 7
end
if pos.y - 4 < minp.y then
pos.y = minp.y + 5
end
if pos.y + 4 > maxp.y then
pos.y = maxp.y - 5
end
if pos.z - 6 < minp.z then
pos.z = minp.z + 7
end
if pos.z + 6 > maxp.z then
pos.z = maxp.z - 7
end
mcl_structures.call_struct(pos, "end_portal_shrine", nil, pr)
strongholds[s].generated = true
end
end
end
end
init_strongholds()
mcl_mapgen_core.register_generator("strongholds", nil, generate_strongholds, 999999)

View File

@ -1,4 +0,0 @@
name = mcl_strongholds
author = Wuzzy
description = Generates strongholds with end portals in the Overworld
depends = mcl_init, mcl_structures, mcl_mapgen_core

View File

@ -0,0 +1,174 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 11
local noise_multiplier = 1
local random_offset = 999
local scanning_ratio = 0.00003
local struct_threshold = chance_per_chunk - 1
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local node_list = {"mcl_core:sand", "mcl_core:sandstone", "mcl_core:redsand", "mcl_colorblocks:hardened_clay_orange"}
local schematic_file = modpath .. "/schematics/mcl_structures_desert_temple.mts"
local temple_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
local temple_schematic = loadstring(temple_schematic_lua)()
local red_temple_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
red_temple_schematic_lua = red_temple_schematic_lua:gsub("mcl_colorblocks:hardened_clay_orange", "mcl_colorblocks:hardened_clay_red")
red_temple_schematic_lua = red_temple_schematic_lua:gsub("mcl_core:sand_stone", "mcl_colorblocks:hardened_clay_orange")
red_temple_schematic_lua = red_temple_schematic_lua:gsub("mcl_core:sand", "mcl_core:redsand")
red_temple_schematic_lua = red_temple_schematic_lua:gsub("mcl_stairs:stair_sandstone", "mcl_stairs:stair_redsandstone")
red_temple_schematic_lua = red_temple_schematic_lua:gsub("mcl_stairs:slab_sandstone", "mcl_stairs:slab_redsandstone")
red_temple_schematic_lua = red_temple_schematic_lua:gsub("mcl_colorblocks:hardened_clay_yellow", "mcl_colorblocks:hardened_clay_pink")
local red_temple_schematic = loadstring(red_temple_schematic_lua)()
local function on_placed(p1, rotation, pr, size)
local p2 = {x = p1.x + size.x - 1, y = p1.y + size.y - 1, z = p1.z + size.z - 1}
-- Delete cacti leftovers:
local cactus_nodes = minetest.find_nodes_in_area_under_air({x = p1.x, y = p1.y + 11, z = p1.z}, {x = p2.x, y = p2.y - 2, z = p2.z}, "mcl_core:cactus", false)
for _, pos in pairs(cactus_nodes) do
local node_below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
local nn = node_below.name
if nn == "mcl_core:sandstone" then
minetest.swap_node(pos, {name="air"})
end
end
-- Find chests.
local chests = minetest.find_nodes_in_area(p1, {x = p2.x, y = p1.y + 5, z = p2.z}, "mcl_chests:chest")
-- Add desert temple loot into chests
for c=1, #chests do
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 25, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 25, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_mobitems:spider_eye", weight = 25, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_books:book", weight = 20, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr)
end },
{ itemstring = "mcl_mobitems:saddle", weight = 20, },
{ itemstring = "mcl_core:apple_gold", weight = 20, },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:emerald", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "", weight = 15, },
{ itemstring = "mobs_mc:iron_horse_armor", weight = 15, },
{ itemstring = "mobs_mc:gold_horse_armor", weight = 10, },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 5, },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:gunpowder", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_core:sand", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:string", weight = 10, amount_min = 1, amount_max = 8 },
}
}}, pr)
mcl_structures.init_node_construct(chests[c])
local meta = minetest.get_meta(chests[c])
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
-- Initialize pressure plates and randomly remove up to 5 plates
local pplates = minetest.find_nodes_in_area(p1, {x = p2.x, y = p1.y + 5, z = p2.z}, "mesecons_pressureplates:pressure_plate_stone_off")
local pplates_remove = 5
for p=1, #pplates do
if pplates_remove > 0 and pr:next(1, 100) >= 50 then
-- Remove plate
minetest.remove_node(pplates[p])
pplates_remove = pplates_remove - 1
else
-- Initialize plate
minetest.registered_nodes["mesecons_pressureplates:pressure_plate_stone_off"].on_construct(pplates[p])
end
end
end
local function place(pos, rotation, pr)
local pos_below = {x = pos.x, y = pos.y - 1, z = pos.z}
local pos_temple = {x = pos.x - 10, y = pos.y - 12, z = pos.z - 10}
local node_below = minetest.get_node(pos_below)
local nn = node_below.name
if string.find(nn, "red") then
mcl_structures.place_schematic({pos = pos_temple, schematic = red_temple_schematic, pr = pr, on_placed = on_placed})
else
mcl_structures.place_schematic({pos = pos_temple, schematic = temple_schematic, pr = pr, on_placed = on_placed})
end
end
local function get_place_rank(pos)
local x, y, z = pos.x, pos.y - 1, pos.z
local p1 = {x = x - 8, y = y, z = z - 8}
local p2 = {x = x + 8, y = y, z = z + 8}
local best_pos_list_surface = minetest.find_nodes_in_area(p1, p2, node_list, false)
local other_pos_list_surface = minetest.find_nodes_in_area(p1, p2, "group:opaque", false)
p1 = {x = x - 4, y = y - 7, z = z - 4}
p2 = {x = x + 4, y = y - 3, z = z + 4}
local best_pos_list_underground = minetest.find_nodes_in_area(p1, p2, node_list, false)
local other_pos_list_underground = minetest.find_nodes_in_area(p1, p2, "group:opaque", false)
return 10 * (#best_pos_list_surface) + 2 * (#other_pos_list_surface) + 5 * (#best_pos_list_underground) + #other_pos_list_underground
end
mcl_structures.register_structure({
name = "desert_temple",
decoration = {
deco_type = "simple",
place_on = node_list,
flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = 3,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes = not mcl_mapgen.v6 and {
"ColdTaiga_beach",
"ColdTaiga_beach_water",
"Desert",
"Desert_ocean",
"ExtremeHills_beach",
"FlowerForest_beach",
"Forest_beach",
"MesaBryce_sandlevel",
"MesaPlateauF_sandlevel",
"MesaPlateauFM_sandlevel",
"Savanna",
"Savanna_beach",
"StoneBeach",
"StoneBeach_ocean",
"Taiga_beach",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
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 (random_number + noise) < struct_threshold then return end
local pos = pos_list[1]
if #pos_list > 1 then
local count = get_place_rank(pos)
for i = 2, #pos_list do
local pos_i = pos_list[i]
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
end
place(pos, nil, pr)
end,
place_function = place,
})

View File

@ -0,0 +1,93 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 60
local noise_multiplier = 1
local random_offset = 999
local scanning_ratio = 0.00001
local struct_threshold = chance_per_chunk - 1
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local node_list = {"mcl_core:sand", "mcl_core:sandstone", "mcl_core:redsand", "mcl_colorblocks:hardened_clay_orange"}
local schematic_file = modpath .. "/schematics/mcl_structures_desert_well.mts"
local well_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
local well_schematic = loadstring(well_schematic_lua)()
local red_well_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
red_well_schematic_lua = red_well_schematic_lua:gsub("mcl_core:sand", "mcl_core:redsand")
red_well_schematic_lua = red_well_schematic_lua:gsub("mcl_stairs:slab_sandstone", "mcl_stairs:slab_redsandstone")
local red_well_schematic = loadstring(red_well_schematic_lua)()
local function place(pos, rotation, pr)
local pos_below = {x = pos.x, y = pos.y - 1, z = pos.z}
local pos_well = {x = pos.x, y = pos.y - 2, z = pos.z}
local node_below = minetest.get_node(pos_below)
local nn = node_below.name
if string.find(nn, "red") then
mcl_structures.place_schematic({pos = pos_well, rotaton = rotation, schematic = red_well_schematic, pr = pr})
else
mcl_structures.place_schematic({pos = pos_well, rotaton = rotation, schematic = well_schematic, pr = pr})
end
end
local function get_place_rank(pos)
local x, y, z = pos.x, pos.y - 1, pos.z
local p1 = {x = x , y = y, z = z }
local p2 = {x = x + 5, y = y, z = z + 5}
local post_pos_list_surface = #minetest.find_nodes_in_area(p1, p2, node_list, false)
local other_pos_list_surface = #minetest.find_nodes_in_area(p1, p2, "group:opaque", false)
return post_pos_list_surface * 5 + other_pos_list_surface
end
mcl_structures.register_structure({
name = "desert_well",
decoration = {
deco_type = "simple",
place_on = node_list,
flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = -5,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes = not mcl_mapgen.v6 and {
"ColdTaiga_beach",
"ColdTaiga_beach_water",
"Desert",
"Desert_ocean",
"ExtremeHills_beach",
"FlowerForest_beach",
"Forest_beach",
"MesaBryce_sandlevel",
"MesaPlateauF_sandlevel",
"MesaPlateauFM_sandlevel",
"Savanna",
"Savanna_beach",
"StoneBeach",
"StoneBeach_ocean",
"Taiga_beach",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
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 (random_number + noise) < struct_threshold then return end
local pos = pos_list[1]
if #pos_list > 1 then
local count = get_place_rank(pos)
for i = 2, #pos_list do
local pos_i = pos_list[i]
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
end
place(pos, nil, pr)
end,
place_function = place,
})

View File

@ -0,0 +1,52 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local END_EXIT_PORTAL_POS_X = -3
local END_EXIT_PORTAL_POS_Y = -27003
local END_EXIT_PORTAL_POS_Z = -3
local p0 = {
x = END_EXIT_PORTAL_POS_X,
y = END_EXIT_PORTAL_POS_Y,
z = END_EXIT_PORTAL_POS_Z,
}
local schematic = modpath .. "/schematics/mcl_structures_end_exit_portal.mts"
local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, schematic = schematic, rotation = rotation, pr = pr})
end
mcl_mapgen.register_mapgen(function(minp, maxp, seed, vm_context)
local minp = minp
local y1 = minp.y
if y1 > END_EXIT_PORTAL_POS_Y then return end
local maxp = maxp
local y2 = maxp.y
if y2 < END_EXIT_PORTAL_POS_Y then return end
if minp.x > END_EXIT_PORTAL_POS_X then return end
if maxp.x < END_EXIT_PORTAL_POS_X 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
local p = table.copy(p0)
for y = y2, y1, -1 do
p.y = y
if minetest.get_node(p).name == "mcl_end:end_stone" then
place(p, "0", PseudoRandom(vm_context.chunkseed))
return
end
end
for y = y2, y1, -1 do
p.y = y
if minetest.get_node(p).name ~= "air" then
place(p, "0", PseudoRandom(vm_context.chunkseed))
return
end
end
place(p0, "0", PseudoRandom(vm_context.chunkseed))
end)
mcl_structures.register_structure({name = "end_exit_portal", place_function = place})

View File

@ -0,0 +1,53 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_block = mcl_structures.from_16x16_to_block_inverted_chance(64)
local noise_multiplier = 2
local random_offset = 5
local struct_threshold = chance_per_block - 1
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 min_y = mcl_worlds.layer_to_y(40)
local max_y = mcl_worlds.layer_to_y(49)
local fossils = {
"mcl_structures_fossil_skull_1.mts", -- 4×5×5
"mcl_structures_fossil_skull_2.mts", -- 5×5×5
"mcl_structures_fossil_skull_3.mts", -- 5×5×7
"mcl_structures_fossil_skull_4.mts", -- 7×5×5
"mcl_structures_fossil_spine_1.mts", -- 3×3×13
"mcl_structures_fossil_spine_2.mts", -- 5×4×13
"mcl_structures_fossil_spine_3.mts", -- 7×4×13
"mcl_structures_fossil_spine_4.mts", -- 8×5×13
}
local nodes_for_fossil = {"mcl_core:sandstone", "mcl_core:stone", "mcl_core:diorite", "mcl_core:andesite", "mcl_core:granite", "mcl_core:stone_with_coal", "mcl_core:dirt", "mcl_core:gravel"}
function spawn_fossil(pos, rotation, pr, placer)
-- Generates one out of 8 possible fossil pieces
local def = {
pos = {x = pos.x, y = pos.y - 1, z = pos.z},
schematic = modpath .. "/schematics/" .. fossils[pr:next(1, #fossils)],
rotation = rotation,
pr = pr,
}
mcl_structures.place_schematic(def)
end
mcl_mapgen.register_mapgen_block(function(minp, maxp, seed)
local p1 = table.copy(minp)
local y1 = p1.y
if y1 > max_y then return end
local p2 = table.copy(maxp)
local y2 = p2.y
if y2 < min_y then return end
local pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_block)
p1.y = math.max(y1, min_y)
local noise = mcl_structures_get_perlin_noise_level(p1) * noise_multiplier
if (random_number + noise) < struct_threshold then return end
p2.y = math.min(y2, max_y)
local nodes = minetest_find_nodes_in_area(p1, p2, nodes_for_fossil, false)
if #nodes < 100 then return end
spawn_fossil(p1, nil, pr)
end, 1000)
mcl_structures.register_structure({name = 'fossil', place_function = spawn_fossil})

View File

@ -0,0 +1,66 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 5
local random_offset = 24435
local struct_threshold = chance_per_chunk - 1
local noise_params = {
offset = 0,
scale = 1,
spread = {
x = 1000,
y = 1000,
z = 1000,
},
scale = 0.01,
seed = 29313,
octaves = 2,
persistence = 0.7,
}
local node_list = {"mcl_core:snowblock", "mcl_core:dirt_with_grass_snow"}
local schematic = modpath.."/schematics/mcl_structures_ice_spike_large.mts"
minetest_find_nodes_in_area = minetest.find_nodes_in_area
local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, schematic = schematic, rotation = rotation, pr = pr})
end
local function is_place_ok(p)
-- Check surface
local floor = {x=p.x+4, y=p.y-1, z=p.z+4}
local surface = #minetest_find_nodes_in_area({x=p.x+1,y=p.y-1,z=p.z+1}, floor, node_list, false)
if surface < 9 then return end
-- Check for collision with spruce
local spruce_collisions = #minetest_find_nodes_in_area({x=p.x+1,y=p.y+2,z=p.z+1}, {x=p.x+4, y=p.y+6, z=p.z+4}, {"group:tree"}, false)
if spruce_collisions > 0 then return end
return true
end
local def = mcl_mapgen.v6 and {
decoration = {
deco_type = "simple",
place_on = node_list,
noise_params = noise_params,
y_min = mcl_mapgen.overworld.min,
y_max = mcl_mapgen.overworld.max,
height = 1,
},
on_finished_chunk = mcl_mapgen.v6 and function(minp, maxp, seed, vm_context, pos_list)
local pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_chunk)
if random_number < struct_threshold then return end
for i = 1, #pos_list do
local pos = pos_list[i]
if is_place_ok(pos) then
place(pos, nil, pr)
end
end
end,
} or {}
def.name = "ice_spike_large"
def.place_function = place
mcl_structures.register_structure(def)

View File

@ -0,0 +1,65 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 3
local random_offset = 1264
local struct_threshold = chance_per_chunk - 1
local noise_params = {
offset = 0,
scale = 1,
spread = {
x = mcl_mapgen.CS,
y = mcl_mapgen.CS,
z = mcl_mapgen.CS,
},
scale = 0.3,
seed = 32931,
octaves = 2,
persistence = 0.7,
}
local node_list = {"mcl_core:snowblock", "mcl_core:dirt_with_grass_snow"}
local schematic = modpath.."/schematics/mcl_structures_ice_spike_small.mts"
minetest_find_nodes_in_area = minetest.find_nodes_in_area
local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, schematic = schematic, rotation = rotation, pr = pr})
end
local function is_place_ok(p)
local floor = {x=p.x+6, y=p.y-1, z=p.z+6}
local surface = #minetest_find_nodes_in_area({x=p.x+1,y=p.y-1,z=p.z+1}, floor, node_list, false)
if surface < 25 then return end
-- Check for collision with spruce
local spruce_collisions = #minetest_find_nodes_in_area({x=p.x+1,y=p.y+1,z=p.z+1}, {x=p.x+6, y=p.y+6, z=p.z+6}, {"group:tree"}, false)
if spruce_collisions > 0 then return end
return true
end
local def = mcl_mapgen.v6 and {
decoration = {
deco_type = "simple",
place_on = node_list,
noise_params = noise_params,
y_min = mcl_mapgen.overworld.min,
y_max = mcl_mapgen.overworld.max,
height = 1,
},
on_finished_chunk = mcl_mapgen.v6 and function(minp, maxp, seed, vm_context, pos_list)
local pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_chunk)
if random_number < struct_threshold then return end
for i = 1, #pos_list do
local pos = pos_list[i]
if is_place_ok(pos) then
place(pos, nil, pr)
end
end
end,
} or {}
def.name = "ice_spike_small"
def.place_function = place
mcl_structures.register_structure(def)

View File

@ -0,0 +1,195 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
-- local chance_per_chunk = mcl_structures.from_16x16_to_chunk_inverted_chance(4400)
local chance_per_chunk = 100
local noise_multiplier = 1.4
local random_offset = 555
local struct_threshold = chance_per_chunk - 1
local scanning_ratio = 0.0003
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local node_list = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"}
local schematic_top = modpath.."/schematics/mcl_structures_igloo_top.mts"
local schematic_basement = modpath.."/schematics/mcl_structures_igloo_basement.mts"
local brick = {
-- monster egg:
[false] = {
-- cracked:
[false] = "mcl_core:stonebrick",
[true ] = "mcl_core:stonebrickcracked",
},
[true] = {
[false] = "mcl_monster_eggs:monster_egg_stonebrick",
[true ] = "mcl_monster_eggs:monster_egg_stonebrickcracked",
},
}
local dirs = {
["0"] = {x=-1, y=0, z= 0},
["90"] = {x= 0, y=0, z=-1},
["180"] = {x= 1, y=0, z= 0},
["270"] = {x= 0, y=0, z= 1},
}
local tdirs = {
["0"] = {x= 1, y=0, z= 0},
["90"] = {x= 0, y=0, z=-1},
["180"] = {x=-1, y=0, z= 0},
["270"] = {x= 0, y=0, z= 1}
}
local tposes = {
["0"] = {x=7, y=-1, z=3},
["90"] = {x=3, y=-1, z=1},
["180"] = {x=1, y=-1, z=3},
["270"] = {x=3, y=-1, z=7},
}
local chest_offsets = {
["0"] = {x=5, y=1, z=5},
["90"] = {x=5, y=1, z=3},
["180"] = {x=3, y=1, z=1},
["270"] = {x=1, y=1, z=5},
}
local function on_placed(pos, rotation, pr, size)
local chest_offset = chest_offsets[rotation]
if not chest_offset then return end
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 1,
stacks_max = 1,
items = {
{ itemstring = "mcl_core:apple_gold", weight = 1 },
}
},
{
stacks_min = 2,
stacks_max = 8,
items = {
{ itemstring = "mcl_core:coal_lump", weight = 15, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:wheat_item", weight = 10, amount_min = 2, amount_max = 3 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10 },
{ itemstring = "mcl_tools:axe_stone", weight = 2 },
{ itemstring = "mcl_core:emerald", weight = 1 },
}
}}, pr)
local chest_pos = vector.add(pos, chest_offset)
mcl_structures.init_node_construct(chest_pos)
local meta = minetest.get_meta(chest_pos)
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
local function on_placed_top(p1, rotation, pr, size)
local y = p1.y + 1
local pos = {x = p1.x, y = y, z = p1.z}
local dim = mcl_mapgen[mcl_worlds.pos_to_dimension(pos)]
local bottom_of_dimension = (dim and dim.min or mcl_mapgen.EDGE_MIN) + 10
local bottom_of_chunk = mcl_mapgen.get_chunk_beginning(y)
local buffer = y - math.max(bottom_of_chunk, bottom_of_dimension)
if buffer < 20 then return end
local depth = pr:next(19, buffer)
local bpos = {x=pos.x, y=pos.y-depth, z=pos.z}
local dir = dirs[rotation]
if not dir then return end
local tdir = tdirs[rotation]
-- Trapdoor position
local tpos = vector.add(pos, tposes[rotation])
local ladder_param2 = minetest.dir_to_wallmounted(tdir)
-- Check how deep we can actuall dig
local real_depth = 0
for y = 1, depth - 5 do
local node = minetest.get_node({x=tpos.x,y=tpos.y-y,z=tpos.z})
local def = minetest.registered_nodes[node.name]
if (not def) or (not def.walkable) or (def.liquidtype ~= "none") then
bpos.y = tpos.y-y+1
break
end
real_depth = real_depth + 1
end
if real_depth < 6 then return end
-- Generate ladder to basement
for y=1, real_depth-1 do
minetest.set_node({x=tpos.x-1,y=tpos.y-y,z=tpos.z }, {name = brick[pr:next(1, 10) == 1][pr:next(1, 3) == 1]})
minetest.set_node({x=tpos.x+1,y=tpos.y-y,z=tpos.z }, {name = brick[pr:next(1, 10) == 1][pr:next(1, 3) == 1]})
minetest.set_node({x=tpos.x ,y=tpos.y-y,z=tpos.z-1}, {name = brick[pr:next(1, 10) == 1][pr:next(1, 3) == 1]})
minetest.set_node({x=tpos.x ,y=tpos.y-y,z=tpos.z+1}, {name = brick[pr:next(1, 10) == 1][pr:next(1, 3) == 1]})
minetest.set_node({x=tpos.x ,y=tpos.y-y,z=tpos.z }, {name="mcl_core:ladder", param2=ladder_param2})
end
-- Place basement
local def = {
pos = bpos,
schematic = schematic_basement,
rotation = rotation,
pr = pr,
on_placed = on_placed,
}
mcl_structures.place_schematic(def)
minetest.after(5, function(tpos, dir)
minetest.swap_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2
end, tpos, dir)
end
local function place(pos, rotation, pr)
local def = {
pos = {x = pos.x, y = pos.y - 1, z = pos.z},
schematic = schematic_top,
rotation = rotation or tostring(pr:next(0,3)*90),
pr = pr,
on_placed = on_placed_top,
}
-- FIXME: This spawns bookshelf instead of furnace. Fix this!
-- Furnace does not work atm because apparently meta is not set. :-(
mcl_structures.place_schematic(def)
end
local function get_place_rank(pos)
local x, y, z = pos.x, pos.y, pos.z
local p1 = {x = x , y = y, z = z }
local p2 = {x = x + 9, y = y, z = z + 9}
local best_pos_list_surface = #minetest.find_nodes_in_area(p1, p2, node_list, false)
local other_pos_list_surface = #minetest.find_nodes_in_area(p1, p2, "group:opaque", false)
return 10 * (best_pos_list_surface) + other_pos_list_surface - 640
end
mcl_structures.register_structure({
name = "igloo",
decoration = {
deco_type = "simple",
place_on = node_list,
flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = -33,
y_max = mcl_mapgen.overworld.max,
height = 1,
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
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 (random_number + noise) < struct_threshold then return end
local pos
local count = -1
for i = 1, #pos_list do
local pos_i = vector.subtract(pos_list[i], {x = 4, y = 1, z = 4})
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
if count < 0 then return end
place(pos, nil, pr)
end,
place_function = place,
})

View File

@ -2,46 +2,309 @@ local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
mcl_structures = {}
local name_prefix = "mcl_structures:"
mcl_structures = {}
local rotations = {
"0",
"90",
"180",
"270"
}
local registered_structures = {}
local use_process_mapgen_block_lvm = false
local use_process_mapgen_chunk = false
local on_finished_block_callbacks = {}
local on_finished_chunk_callbacks = {}
local function ecb_place(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end
minetest.place_schematic(param.pos, param.schematic, param.rotation, param.replacements, param.force_placement, param.flags)
if param.after_placement_callback and param.p1 and param.p2 then
param.after_placement_callback(param.p1, param.p2, param.size, param.rotation, param.pr, param.callback_param)
local noise_params = {
offset = 0,
scale = 1,
spread = {
x = mcl_mapgen.CS_NODES,
y = mcl_mapgen.CS_NODES,
z = mcl_mapgen.CS_NODES,
},
seed = 329,
octaves = 1,
persistence = 0.6,
}
local perlin_noise
local get_perlin_noise_level = function(minp)
perlin_noise = perlin_noise or minetest.get_perlin(noise_params)
return perlin_noise:get_3d(minp)
end
mcl_structures.get_perlin_noise_level = get_perlin_noise_level
local spawnstruct_hint = S("Use /help spawnstruct to see a list of avaiable types.")
local function dir_to_rotation(dir)
local ax, az = math.abs(dir.x), math.abs(dir.z)
if ax > az then
if dir.x < 0 then
return "270"
end
return "90"
end
if dir.z < 0 then
return "180"
end
return "0"
end
local function spawnstruct_function(name, param)
local player = minetest.get_player_by_name(name)
if not player then return end
if param == "" then
minetest.chat_send_player(name, S("Error: No structure type given. Please use “/spawnstruct <type>”."))
minetest.chat_send_player(name, spawnstruct_hint)
return
end
local struct = registered_structures[param]
if not struct then
struct = registered_structures[name_prefix .. param]
end
if not struct then
minetest.chat_send_player(name, S("Error: Unknown structure type. Please use “/spawnstruct <type>”."))
minetest.chat_send_player(name, spawnstruct_hint)
return
end
local place = struct.place_function
if not place then return end
local pos = player:get_pos()
if not pos then return end
local pr = PseudoRandom(math.floor(pos.x * 333 + pos.y * 19 - pos.z + 4))
pos = vector.round(pos)
local dir = minetest.yaw_to_dir(player:get_look_horizontal())
local rot = dir_to_rotation(dir)
place(pos, rot, pr, player)
minetest.chat_send_player(name, S("Structure placed."))
end
local function update_spawnstruct_chatcommand()
local spawnstruct_params = ""
for _, registered_structure in pairs(registered_structures) do
if spawnstruct_params ~= "" then
spawnstruct_params = spawnstruct_params .. " | "
end
spawnstruct_params = spawnstruct_params .. registered_structure.short_name
end
local def = {
params = spawnstruct_params,
description = S("Generate a pre-defined structure near your position."),
privs = {debug = true},
func = spawnstruct_function,
}
local registered_chatcommands = minetest.registered_chatcommands
if registered_chatcommands["spawnstruct"] then
minetest.override_chatcommand("spawnstruct", def)
else
minetest.register_chatcommand("spawnstruct", def)
end
end
function mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
local s = loadstring(minetest.serialize_schematic(schematic, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")()
if s and s.size then
local x, z = s.size.x, s.size.z
if rotation then
if rotation == "random" and pr then
rotation = rotations[pr:next(1,#rotations)]
end
if rotation == "random" then
x = math.max(x, z)
z = x
elseif rotation == "90" or rotation == "270" then
x, z = z, x
function process_mapgen_block_lvm(vm_context)
local nodes = minetest.find_nodes_in_area(vm_context.minp, vm_context.maxp, {"group:struct"}, true)
for node_name, pos_list in pairs(nodes) do
local lvm_callback = on_finished_block_callbacks[node_name]
if lvm_callback then
lvm_callback(vm_context, pos_list)
end
end
end
function process_mapgen_chunk(minp, maxp, seed, vm_context)
local nodes = minetest.find_nodes_in_area(minp, maxp, {"group:struct"}, true)
for node_name, pos_list in pairs(nodes) do
local chunk_callback = on_finished_chunk_callbacks[node_name]
if chunk_callback then
chunk_callback(minp, maxp, seed, vm_context, pos_list)
end
end
for node_name, pos_list in pairs(nodes) do
for _, pos in pairs(pos_list) do
local node = minetest.get_node(pos)
if string.sub(node.name, 1, 15) == 'mcl_structures:' then
minetest.swap_node(pos, {name = 'air'})
end
end
local p1 = {x=pos.x , y=pos.y , z=pos.z }
local p2 = {x=pos.x+x-1, y=pos.y+s.size.y-1, z=pos.z+z-1}
minetest.log("verbose", "[mcl_structures] size=" ..minetest.pos_to_string(s.size) .. ", rotation=" .. tostring(rotation) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
local param = {pos=vector.new(pos), schematic=s, rotation=rotation, replacements=replacements, force_placement=force_placement, flags=flags, p1=p1, p2=p2, after_placement_callback = after_placement_callback, size=vector.new(s.size), pr=pr, callback_param=callback_param}
minetest.emerge_area(p1, p2, ecb_place, param)
end
end
--------------------------------------------------------------------------------------
-- mcl_structures.register_structure(struct_def)
-- struct_def:
-- name - name, like 'desert_temple'
-- decoration - decoration definition, to use as structure seed (thanks cora for the idea)
-- on_finished_block - callback, if needed, to use with decorations: funcion(vm_context, pos_list)
-- on_finished_chunk - next callback if needed: funcion(minp, maxp, seed, vm_context, pos_list)
-- place_function - callback to place schematic by /spawnstruct debug command: function(pos, rotation, pr, placer)
-- on_placed - useful when you want to process the area after placement: function(pos, rotation, pr, size)
function mcl_structures.register_structure(def)
local short_name = def.name
local name = "mcl_structures:" .. short_name
local decoration = def.decoration
local on_finished_block = def.on_finished_block
local on_finished_chunk = def.on_finished_chunk
local place_function = def.place_function
if not name then
minetest.log('warning', 'Structure name is not passed for registration - ignoring')
return
end
if registered_structures[name] then
minetest.log('warning', 'Structure '..name..' is already registered - owerwriting')
end
local decoration_id
if decoration then
minetest.register_node(':' .. name, {
drawtype = "airlike",
sunlight_propagates = true,
pointable = false,
walkable = false,
diggable = false,
buildable_to = true,
groups = {
struct = 1,
not_in_creative_inventory = 1,
},
})
decoration_id = minetest.register_decoration({
deco_type = decoration.deco_type,
place_on = decoration.place_on,
sidelen = decoration.sidelen,
fill_ratio = decoration.fill_ratio,
noise_params = decoration.noise_params,
biomes = decoration.biomes,
y_min = decoration.y_min,
y_max = decoration.y_max,
spawn_by = decoration.spawn_by,
num_spawn_by = decoration.num_spawn_by,
flags = decoration.flags,
decoration = name,
height = decoration.height,
height_max = decoration.height_max,
param2 = decoration.param2,
param2_max = decoration.param2_max,
place_offset_y = decoration.place_offset_y,
schematic = decoration.schematic,
replacements = decoration.replacements,
flags = decoration.flags,
rotation = decoration.rotation,
})
end
registered_structures[name] = {
place_function = place_function,
on_finished_block = on_finished_block,
on_finished_chunk = on_finished_chunk,
decoration_id = decoration_id,
short_name = short_name,
}
update_spawnstruct_chatcommand()
if on_finished_block then
on_finished_block_callbacks[name] = on_finished_block
if not use_process_mapgen_block_lvm then
use_process_mapgen_block_lvm = true
mcl_mapgen.register_mapgen_block_lvm(process_mapgen_block_lvm, mcl_mapgen.order.BUILDINGS)
end
end
if on_finished_chunk then
on_finished_chunk_callbacks[name] = on_finished_chunk
if not use_process_mapgen_chunk then
use_process_mapgen_chunk = true
mcl_mapgen.register_mapgen(process_mapgen_chunk, mcl_mapgen.order.BUILDINGS)
end
end
end
-- It doesN'T remove registered node and decoration!
function mcl_structures.unregister_structure(name)
if not registered_structures[name] then
minetest.log('warning','Structure '..name..' is not registered - skipping')
return
end
registered_structures[name] = nil
end
local function ecb_place(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end
local pos = param.pos
local rotation = param.rotation
minetest.place_schematic(pos, param.schematic, rotation, param.replacements, param.force_placement, param.flags)
local on_placed = param.on_placed
if not on_placed then
return
end
on_placed(pos, rotation, param.pr, param.size)
end
function mcl_structures.place_schematic(def)
local pos = def.pos
local schematic = def.schematic
local rotation = def.rotation
local pr = def.pr
local on_placed = def.on_placed -- on_placed(pos, rotation, pr, size)
local emerge = def.emerge
if not pos then
minetest.log('warning', '[mcl_structures] No pos. specified to place schematic')
return
end
if not schematic then
minetest.log('warning', '[mcl_structures] No schematic specified to place at ' .. minetest.pos_to_string(pos))
return
end
if not rotation or rotation == 'random' then
if pr then
rotation = rotations[pr:next(1,#rotations)]
else
rotation = rotations[math.random(1,#rotations)]
end
end
if not emerge and not on_placed then
minetest.place_schematic(pos, schematic, rotation, def.replacements, def.force_placement, def.flags)
return
end
local serialized_schematic = minetest.serialize_schematic(schematic, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
local loaded_schematic = loadstring(serialized_schematic)()
if not loaded_schematic then
minetest.log('warning', '[mcl_structures] Schematic ' .. schematic .. ' load serialized string problem at ' .. minetest.pos_to_string(pos))
return
end
local size = loaded_schematic.size
if not size then
minetest.log('warning', '[mcl_structures] Schematic ' .. schematic .. ' has no size at ' .. minetest.pos_to_string(pos))
return
end
local size_x, size_y, size_z = size.x, size.y, size.z
if rotation == "90" or rotation == "270" then
size_x, size_z = size_z, size_x
end
local x, y, z = pos.x, pos.y, pos.z
local p1 = {x = x, y = y, z = z}
local p2 = {x = x + size_x - 1, y = y + size_y - 1, z = size_z - 1}
local ecb_param = {
pos = vector.new(pos),
schematic = loaded_schematic,
rotation = rotation,
replacements = replacements,
force_placement = force_placement,
flags = flags,
size = vector.new(size),
pr = pr,
on_placed = on_placed,
}
if not emerge then
ecb_place(p1, nil, 0, ecb_param)
return
end
minetest.log("verbose", "[mcl_structures] Emerge area " .. minetest.pos_to_string(p1) .. " - " .. minetest.pos_to_string(p2)
.. " of size " ..minetest.pos_to_string(size) .. " to place " .. schematic .. ", rotation " .. tostring(rotation))
minetest.emerge_area(p1, p2, ecb_place, ecb_param)
end
function mcl_structures.get_struct(file)
local localfile = modpath.."/schematics/"..file
local file, errorload = io.open(localfile, "rb")
@ -58,7 +321,7 @@ end
-- Call on_construct on pos.
-- Useful to init chests from formspec.
local function init_node_construct(pos)
function mcl_structures.init_node_construct(pos)
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
if def and def.on_construct then
@ -69,200 +332,42 @@ local function init_node_construct(pos)
end
-- The call of Struct
function mcl_structures.call_struct(pos, struct_style, rotation, pr)
function mcl_structures.call_struct(pos, struct_style, rotation, pr, callback)
minetest.log("action","[mcl_structures] call_struct " .. struct_style.." at "..minetest.pos_to_string(pos))
if not rotation then
rotation = "random"
end
if struct_style == "desert_temple" then
return mcl_structures.generate_desert_temple(pos, rotation, pr)
elseif struct_style == "desert_well" then
return mcl_structures.generate_desert_well(pos, rotation)
elseif struct_style == "igloo" then
return mcl_structures.generate_igloo(pos, rotation, pr)
elseif struct_style == "witch_hut" then
if struct_style == "witch_hut" then
return mcl_structures.generate_witch_hut(pos, rotation)
elseif struct_style == "ice_spike_small" then
return mcl_structures.generate_ice_spike_small(pos, rotation)
elseif struct_style == "ice_spike_large" then
return mcl_structures.generate_ice_spike_large(pos, rotation)
elseif struct_style == "boulder" then
return mcl_structures.generate_boulder(pos, rotation, pr)
elseif struct_style == "fossil" then
return mcl_structures.generate_fossil(pos, rotation, pr)
elseif struct_style == "end_exit_portal" then
return mcl_structures.generate_end_exit_portal(pos, rotation)
return mcl_structures.generate_end_exit_portal(pos, rotation, pr, callback)
elseif struct_style == "end_exit_portal_open" then
return mcl_structures.generate_end_exit_portal_open(pos, rotation)
elseif struct_style == "end_gateway_portal" then
return mcl_structures.generate_end_gateway_portal(pos, rotation)
elseif struct_style == "end_portal_shrine" then
return mcl_structures.generate_end_portal_shrine(pos, rotation, pr)
elseif struct_style == "end_portal" then
return mcl_structures.generate_end_portal(pos, rotation, pr)
end
end
function mcl_structures.generate_desert_well(pos, rot)
local newpos = {x=pos.x,y=pos.y-2,z=pos.z}
local path = modpath.."/schematics/mcl_structures_desert_well.mts"
return mcl_structures.place_schematic(newpos, path, rot or "0", nil, true)
end
function mcl_structures.generate_igloo(pos, rotation, pr)
-- Place igloo
local success, rotation = mcl_structures.generate_igloo_top(pos, pr)
-- Place igloo basement with 50% chance
local r = pr:next(1,2)
if r == 1 then
-- Select basement depth
local dim = mcl_worlds.pos_to_dimension(pos)
--local buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10)
local buffer
if dim == "nether" then
buffer = pos.y - (mcl_vars.mg_lava_nether_max + 10)
elseif dim == "end" then
buffer = pos.y - (mcl_vars.mg_end_min + 1)
elseif dim == "overworld" then
buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10)
else
return success
end
if buffer <= 19 then
return success
end
local depth = pr:next(19, buffer)
local bpos = {x=pos.x, y=pos.y-depth, z=pos.z}
-- trapdoor position
local tpos
local dir, tdir
if rotation == "0" then
dir = {x=-1, y=0, z=0}
tdir = {x=1, y=0, z=0}
tpos = {x=pos.x+7, y=pos.y-1, z=pos.z+3}
elseif rotation == "90" then
dir = {x=0, y=0, z=-1}
tdir = {x=0, y=0, z=-1}
tpos = {x=pos.x+3, y=pos.y-1, z=pos.z+1}
elseif rotation == "180" then
dir = {x=1, y=0, z=0}
tdir = {x=-1, y=0, z=0}
tpos = {x=pos.x+1, y=pos.y-1, z=pos.z+3}
elseif rotation == "270" then
dir = {x=0, y=0, z=1}
tdir = {x=0, y=0, z=1}
tpos = {x=pos.x+3, y=pos.y-1, z=pos.z+7}
else
return success
end
local function set_brick(pos)
local c = pr:next(1, 3) -- cracked chance
local m = pr:next(1, 10) -- chance for monster egg
local brick
if m == 1 then
if c == 1 then
brick = "mcl_monster_eggs:monster_egg_stonebrickcracked"
else
brick = "mcl_monster_eggs:monster_egg_stonebrick"
function mcl_structures.generate_end_portal(pos, rotation, pr)
-- todo: proper facedir
local x0, y0, z0 = pos.x - 2, pos.y, pos.z - 2
for x = 0, 4 do
for z = 0, 4 do
if x % 4 == 0 or z % 4 == 0 then
if x % 4 ~= 0 or z % 4 ~= 0 then
minetest.swap_node({x = x0 + x, y = y0, z = z0 + z}, {name = "mcl_portals:end_portal_frame_eye"})
end
else
if c == 1 then
brick = "mcl_core:stonebrickcracked"
else
brick = "mcl_core:stonebrick"
end
end
minetest.set_node(pos, {name=brick})
end
local ladder_param2 = minetest.dir_to_wallmounted(tdir)
local real_depth = 0
-- Check how deep we can actuall dig
for y=1, depth-5 do
real_depth = real_depth + 1
local node = minetest.get_node({x=tpos.x,y=tpos.y-y,z=tpos.z})
local def = minetest.registered_nodes[node.name]
if (not def) or (not def.walkable) or (def.liquidtype ~= "none") or (not def.is_ground_content) then
bpos.y = tpos.y-y+1
break
minetest.swap_node({x = x0 + x, y = y0, z = z0 + z}, {name = "mcl_portals:portal_end"})
end
end
if real_depth <= 6 then
return success
end
-- Generate ladder to basement
for y=1, real_depth-1 do
set_brick({x=tpos.x-1,y=tpos.y-y,z=tpos.z })
set_brick({x=tpos.x+1,y=tpos.y-y,z=tpos.z })
set_brick({x=tpos.x ,y=tpos.y-y,z=tpos.z-1})
set_brick({x=tpos.x ,y=tpos.y-y,z=tpos.z+1})
minetest.set_node({x=tpos.x,y=tpos.y-y,z=tpos.z}, {name="mcl_core:ladder", param2=ladder_param2})
end
-- Place basement
mcl_structures.generate_igloo_basement(bpos, rotation, pr)
-- Place hidden trapdoor
minetest.after(5, function(tpos, dir)
minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2
end, tpos, dir)
end
return success
end
function mcl_structures.generate_igloo_top(pos, pr)
-- FIXME: This spawns bookshelf instead of furnace. Fix this!
-- Furnace does ot work atm because apparently meta is not set. :-(
local newpos = {x=pos.x,y=pos.y-1,z=pos.z}
local path = modpath.."/schematics/mcl_structures_igloo_top.mts"
local rotation = tostring(pr:next(0,3)*90)
return mcl_structures.place_schematic(newpos, path, rotation, nil, true), rotation
end
local function igloo_placement_callback(p1, p2, size, orientation, pr)
local chest_offset
if orientation == "0" then
chest_offset = {x=5, y=1, z=5}
elseif orientation == "90" then
chest_offset = {x=5, y=1, z=3}
elseif orientation == "180" then
chest_offset = {x=3, y=1, z=1}
elseif orientation == "270" then
chest_offset = {x=1, y=1, z=5}
else
return
end
--local size = {x=9,y=5,z=7}
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 1,
stacks_max = 1,
items = {
{ itemstring = "mcl_core:apple_gold", weight = 1 },
}
},
{
stacks_min = 2,
stacks_max = 8,
items = {
{ itemstring = "mcl_core:coal_lump", weight = 15, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:wheat_item", weight = 10, amount_min = 2, amount_max = 3 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10 },
{ itemstring = "mcl_tools:axe_stone", weight = 2 },
{ itemstring = "mcl_core:emerald", weight = 1 },
}
}}, pr)
local chest_pos = vector.add(p1, chest_offset)
init_node_construct(chest_pos)
local meta = minetest.get_meta(chest_pos)
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
function mcl_structures.generate_igloo_basement(pos, orientation, pr)
-- TODO: Add brewing stand
-- TODO: Add monster eggs
-- TODO: Spawn villager and zombie villager
local path = modpath.."/schematics/mcl_structures_igloo_basement.mts"
mcl_structures.place_schematic(pos, path, orientation, nil, true, nil, igloo_placement_callback, pr)
end
function mcl_structures.generate_boulder(pos, rotation, pr)
@ -284,7 +389,7 @@ 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_vars.get_node({x=legs[i].x, y=legs[i].y-1, z=legs[i].z}, true, 333333).name, "water") ~= 0 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
@ -296,37 +401,14 @@ function mcl_structures.generate_witch_hut(pos, rotation, pr)
mcl_structures.place_schematic(pos, path, rotation, nil, true, nil, hut_placement_callback, pr)
end
function mcl_structures.generate_ice_spike_small(pos, rotation)
local path = modpath.."/schematics/mcl_structures_ice_spike_small.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_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_fossil(pos, rotation, pr)
-- Generates one out of 8 possible fossil pieces
local newpos = {x=pos.x,y=pos.y-1,z=pos.z}
local fossils = {
"mcl_structures_fossil_skull_1.mts", -- 4×5×5
"mcl_structures_fossil_skull_2.mts", -- 5×5×5
"mcl_structures_fossil_skull_3.mts", -- 5×5×7
"mcl_structures_fossil_skull_4.mts", -- 7×5×5
"mcl_structures_fossil_spine_1.mts", -- 3×3×13
"mcl_structures_fossil_spine_2.mts", -- 5×4×13
"mcl_structures_fossil_spine_3.mts", -- 7×4×13
"mcl_structures_fossil_spine_4.mts", -- 8×5×13
}
local r = pr:next(1, #fossils)
local path = modpath.."/schematics/"..fossils[r]
return mcl_structures.place_schematic(newpos, path, rotation or "random", nil, true)
end
function mcl_structures.generate_end_exit_portal(pos, rot)
function mcl_structures.generate_end_exit_portal(pos, rot, pr, callback)
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)
return mcl_structures.place_schematic(pos, path, rot or "0", {["mcl_portals:portal_end"] = "air"}, true, nil, callback)
end
function mcl_structures.generate_end_exit_portal_open(pos, rot)
@ -339,275 +421,15 @@ function mcl_structures.generate_end_gateway_portal(pos, rot)
return mcl_structures.place_schematic(pos, path, rot or "0", nil, true)
end
local function shrine_placement_callback(p1, p2, size, rotation, pr)
-- Find and setup spawner with silverfish
local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner")
for s=1, #spawners do
--local meta = minetest.get_meta(spawners[s])
mcl_mobspawners.setup_spawner(spawners[s], "mobs_mc:silverfish")
end
local chunk_square = mcl_mapgen.CS_NODES * mcl_mapgen.CS_NODES
local block_square = mcl_mapgen.BS * mcl_mapgen.BS
-- Shuffle stone brick types
local bricks = minetest.find_nodes_in_area(p1, p2, "mcl_core:stonebrick")
for b=1, #bricks do
local r_bricktype = pr:next(1, 100)
local r_infested = pr:next(1, 100)
local bricktype
if r_infested <= 5 then
if r_bricktype <= 30 then -- 30%
bricktype = "mcl_monster_eggs:monster_egg_stonebrickmossy"
elseif r_bricktype <= 50 then -- 20%
bricktype = "mcl_monster_eggs:monster_egg_stonebrickcracked"
else -- 50%
bricktype = "mcl_monster_eggs:monster_egg_stonebrick"
end
else
if r_bricktype <= 30 then -- 30%
bricktype = "mcl_core:stonebrickmossy"
elseif r_bricktype <= 50 then -- 20%
bricktype = "mcl_core:stonebrickcracked"
end
-- 50% stonebrick (no change necessary)
end
if bricktype then
minetest.set_node(bricks[b], { name = bricktype })
end
end
-- Also replace stairs
local stairs = minetest.find_nodes_in_area(p1, p2, {"mcl_stairs:stair_stonebrick", "mcl_stairs:stair_stonebrick_outer", "mcl_stairs:stair_stonebrick_inner"})
for s=1, #stairs do
local stair = minetest.get_node(stairs[s])
local r_type = pr:next(1, 100)
if r_type <= 30 then -- 30% mossy
if stair.name == "mcl_stairs:stair_stonebrick" then
stair.name = "mcl_stairs:stair_stonebrickmossy"
elseif stair.name == "mcl_stairs:stair_stonebrick_outer" then
stair.name = "mcl_stairs:stair_stonebrickmossy_outer"
elseif stair.name == "mcl_stairs:stair_stonebrick_inner" then
stair.name = "mcl_stairs:stair_stonebrickmossy_inner"
end
minetest.set_node(stairs[s], stair)
elseif r_type <= 50 then -- 20% cracky
if stair.name == "mcl_stairs:stair_stonebrick" then
stair.name = "mcl_stairs:stair_stonebrickcracked"
elseif stair.name == "mcl_stairs:stair_stonebrick_outer" then
stair.name = "mcl_stairs:stair_stonebrickcracked_outer"
elseif stair.name == "mcl_stairs:stair_stonebrick_inner" then
stair.name = "mcl_stairs:stair_stonebrickcracked_inner"
end
minetest.set_node(stairs[s], stair)
end
-- 50% no change
end
-- Randomly add ender eyes into end portal frames, but never fill the entire frame
local frames = minetest.find_nodes_in_area(p1, p2, "mcl_portals:end_portal_frame")
local eyes = 0
for f=1, #frames do
local r_eye = pr:next(1, 10)
if r_eye == 1 then
eyes = eyes + 1
if eyes < #frames then
local frame_node = minetest.get_node(frames[f])
frame_node.name = "mcl_portals:end_portal_frame_eye"
minetest.set_node(frames[f], frame_node)
end
end
end
function mcl_structures.from_16x16_to_chunk_inverted_chance(x)
return math.floor(x * 256 / chunk_square + 0.5)
end
function mcl_structures.generate_end_portal_shrine(pos, rotation, pr)
local offset = {x=6, y=4, z=6}
--local size = {x=13, y=8, z=13}
local newpos = { x = pos.x - offset.x, y = pos.y, z = pos.z - offset.z }
local path = modpath.."/schematics/mcl_structures_end_portal_room_simple.mts"
mcl_structures.place_schematic(newpos, path, rotation or "0", nil, true, nil, shrine_placement_callback, pr)
function mcl_structures.from_16x16_to_block_inverted_chance(x)
return math.floor(x * 256 / block_square + 0.5)
end
local function temple_placement_callback(p1, p2, size, rotation, pr)
-- Delete cacti leftovers:
local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus")
if cactus_nodes and #cactus_nodes > 0 then
for _, pos in pairs(cactus_nodes) do
local node_below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
if node_below and node_below.name == "mcl_core:sandstone" then
minetest.swap_node(pos, {name="air"})
end
end
end
-- Find chests.
-- FIXME: Searching this large area just for the chets is not efficient. Need a better way to find the chests;
-- probably let's just infer it from newpos because the schematic always the same.
local chests = minetest.find_nodes_in_area(p1, p2, "mcl_chests:chest")
-- Add desert temple loot into chests
for c=1, #chests do
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 25, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 25, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_mobitems:spider_eye", weight = 25, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_books:book", weight = 20, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr)
end },
{ itemstring = "mcl_mobitems:saddle", weight = 20, },
{ itemstring = "mcl_core:apple_gold", weight = 20, },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:emerald", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "", weight = 15, },
{ itemstring = "mobs_mc:iron_horse_armor", weight = 15, },
{ itemstring = "mobs_mc:gold_horse_armor", weight = 10, },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 5, },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:gunpowder", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_core:sand", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:string", weight = 10, amount_min = 1, amount_max = 8 },
}
}}, pr)
init_node_construct(chests[c])
local meta = minetest.get_meta(chests[c])
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
-- Initialize pressure plates and randomly remove up to 5 plates
local pplates = minetest.find_nodes_in_area(p1, p2, "mesecons_pressureplates:pressure_plate_stone_off")
local pplates_remove = 5
for p=1, #pplates do
if pplates_remove > 0 and pr:next(1, 100) >= 50 then
-- Remove plate
minetest.remove_node(pplates[p])
pplates_remove = pplates_remove - 1
else
-- Initialize plate
minetest.registered_nodes["mesecons_pressureplates:pressure_plate_stone_off"].on_construct(pplates[p])
end
end
end
function mcl_structures.generate_desert_temple(pos, rotation, pr)
-- No Generating for the temple ... Why using it ? No Change
local path = modpath.."/schematics/mcl_structures_desert_temple.mts"
local newpos = {x=pos.x,y=pos.y-12,z=pos.z}
--local size = {x=22, y=24, z=22}
if newpos == nil then
return
end
mcl_structures.place_schematic(newpos, path, rotation or "random", nil, true, nil, temple_placement_callback, pr)
end
local registered_structures = {}
--[[ Returns a table of structure of the specified type.
Currently the only valid parameter is "stronghold".
Format of return value:
{
{ pos = <position>, generated=<true/false> }, -- first structure
{ pos = <position>, generated=<true/false> }, -- second structure
-- and so on
}
TODO: Implement this function for all other structure types as well.
]]
function mcl_structures.get_registered_structures(structure_type)
if registered_structures[structure_type] then
return table.copy(registered_structures[structure_type])
else
return {}
end
end
-- Register a structures table for the given type. The table format is the same as for
-- mcl_structures.get_registered_structures.
function mcl_structures.register_structures(structure_type, structures)
registered_structures[structure_type] = structures
end
local function dir_to_rotation(dir)
local ax, az = math.abs(dir.x), math.abs(dir.z)
if ax > az then
if dir.x < 0 then
return "270"
end
return "90"
end
if dir.z < 0 then
return "180"
end
return "0"
end
-- Debug command
minetest.register_chatcommand("spawnstruct", {
params = "desert_temple | desert_well | igloo | witch_hut | boulder | ice_spike_small | ice_spike_large | fossil | end_exit_portal | end_exit_portal_open | end_gateway_portal | end_portal_shrine | nether_portal | dungeon",
description = S("Generate a pre-defined structure near your position."),
privs = {debug = true},
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then return end
local pos = player:get_pos()
if not pos then return end
pos = vector.round(pos)
local dir = minetest.yaw_to_dir(player:get_look_horizontal())
local rot = dir_to_rotation(dir)
local pr = PseudoRandom(pos.x+pos.y+pos.z)
local errord = false
local message = S("Structure placed.")
if param == "desert_temple" then
mcl_structures.generate_desert_temple(pos, rot, pr)
elseif param == "desert_well" then
mcl_structures.generate_desert_well(pos, rot)
elseif param == "igloo" then
mcl_structures.generate_igloo(pos, rot, pr)
elseif param == "witch_hut" then
mcl_structures.generate_witch_hut(pos, rot, pr)
elseif param == "boulder" then
mcl_structures.generate_boulder(pos, rot, pr)
elseif param == "fossil" then
mcl_structures.generate_fossil(pos, rot, pr)
elseif param == "ice_spike_small" then
mcl_structures.generate_ice_spike_small(pos, rot, pr)
elseif param == "ice_spike_large" then
mcl_structures.generate_ice_spike_large(pos, rot, pr)
elseif param == "end_exit_portal" then
mcl_structures.generate_end_exit_portal(pos, rot, pr)
elseif param == "end_exit_portal_open" then
mcl_structures.generate_end_exit_portal_open(pos, rot, pr)
elseif param == "end_gateway_portal" then
mcl_structures.generate_end_gateway_portal(pos, rot, pr)
elseif param == "end_portal_shrine" then
mcl_structures.generate_end_portal_shrine(pos, rot, pr)
elseif param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then
mcl_dungeons.spawn_dungeon(pos, rot, pr)
elseif param == "nether_portal" and mcl_portals and mcl_portals.spawn_nether_portal then
mcl_portals.spawn_nether_portal(pos, rot, pr, name)
elseif param == "" then
message = S("Error: No structure type given. Please use “/spawnstruct <type>”.")
errord = true
else
message = S("Error: Unknown structure type. Please use “/spawnstruct <type>”.")
errord = true
end
minetest.chat_send_player(name, message)
if errord then
minetest.chat_send_player(name, S("Use /help spawnstruct to see a list of avaiable types."))
end
end
})
dofile(modpath .. "/structures.lua")

View File

@ -0,0 +1,203 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 9
local noise_multiplier = 1.3
local random_offset = 132
local struct_threshold = chance_per_chunk - 1
local scanning_ratio = 0.0003
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local node_list = {"mcl_core:dirt_with_grass", "mcl_core:dirt", "mcl_core:stone", "mcl_core:granite", "mcl_core:gravel", "mcl_core:diorite"}
local schematic_file = modpath .. "/schematics/mcl_structures_jungle_temple.mts"
local temple_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
local temple_schematic = loadstring(temple_schematic_lua)()
local size = temple_schematic.size
local sx = size.x
local sy = size.y
local sz = size.z
local offset = vector.round(vector.divide(size, 2))
offset.y = 5
local ox = offset.x
local oy = offset.y
local oz = offset.z
local corner_x = sx - 3
local corner_z = sz - 3
local air_offset_x = ox - 6
local air_offset_z = oz - 6
local function is_air(pos)
local node = minetest.get_node(pos)
return node.name == "air"
end
local stair_support_node = {
{name = "mcl_core:cobble"},
{name = "mcl_core:mossycobble"},
{name = "mcl_core:stonebrick"},
{name = "mcl_core:stonebrickmossy"},
{name = "mcl_core:stonebrickcracked"},
}
local function on_placed(p1, rotation, pr, size)
local p2
if rotation == "90" or rotation == "270" then
p2 = {x = p1.x + sz - 1, y = p1.y + sy - 1, z = p1.z + sx - 1}
else
p2 = {x = p1.x + sx - 1, y = p1.y + sy - 1, z = p1.z + sz - 1}
end
-- Support stairs
local y = p1.y + 5
local bottom = mcl_mapgen.get_chunk_beginning(y)
local stair_list = minetest.find_nodes_in_area({x = p1.x, y = y, z = p1.z}, {x = p2.x, y = y, z = p2.z}, {"mcl_stairs:stair_cobble"}, false)
for i = 1, #stair_list do
local pos = stair_list[i]
pos.y = y - 1
while is_air(pos) and pos.y > bottom do
minetest.swap_node(pos, stair_support_node[pr:next(1, #stair_support_node)])
pos.y = pos.y - 1
end
end
-- Initialize some nodes
local chest_node = "mcl_chests:trapped_chest_small"
local lever_node = "mesecons_walllever:wall_lever_off"
local nodes = minetest.find_nodes_in_area(p1, {x = p2.x, y = p1.y + 5, z = p2.z}, {chest_node, lever_node}, true)
local levers = nodes[lever_node]
for _, pos in pairs(levers) do
mcl_structures.init_node_construct(pos)
end
-- Add loot into chests TODO: fix items
local chests = nodes[chest_node]
for c=1, #chests do
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 25, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 25, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_mobitems:spider_eye", weight = 25, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_books:book", weight = 20, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr)
end },
{ itemstring = "mcl_mobitems:saddle", weight = 20, },
{ itemstring = "mcl_core:apple_gold", weight = 20, },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:emerald", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "", weight = 15, },
{ itemstring = "mobs_mc:iron_horse_armor", weight = 15, },
{ itemstring = "mobs_mc:gold_horse_armor", weight = 10, },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 5, },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:gunpowder", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_core:sand", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:string", weight = 10, amount_min = 1, amount_max = 8 },
}
}}, pr)
mcl_structures.init_node_construct(chests[c])
local meta = minetest.get_meta(chests[c])
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
end
local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, schematic = temple_schematic, pr = pr, on_placed = on_placed})
end
local mcl_mapgen_clamp_to_chunk = mcl_mapgen.clamp_to_chunk
local function process_pos(pos)
return {
x = mcl_mapgen_clamp_to_chunk(pos.x - ox, sx),
y = mcl_mapgen_clamp_to_chunk(pos.y - oy, sy),
z = mcl_mapgen_clamp_to_chunk(pos.z - oz, sz),
}
end
local function get_place_rank(pos)
local x1 = pos.x + 1
local x2 = x1 + corner_x
local z1 = pos.z + 1
local z2 = z1 + corner_z
local y2 = pos.y + 1
local y1 = y2 - 2
if is_air({x = x1, y = y1, z = z1}) then return -1 end
if is_air({x = x2, y = y1, z = z1}) then return -1 end
if is_air({x = x1, y = y1, z = z2}) then return -1 end
if is_air({x = x2, y = y1, z = z2}) then return -1 end
local p1 = {x = x1 + air_offset_x, y = y2, z = z1 + air_offset_z}
local p2 = {x = x2 - air_offset_x, y = y2, z = z2 + air_offset_z}
local pos_counter_air = #minetest.find_nodes_in_area(p1, p2, {"air", "group:buildable_to", "group:deco_block"}, false)
local pos_counter_air = pos_counter_air - 2 * (#minetest.find_nodes_in_area(p1, p2, {"group:tree"}, false))
local p1 = {x = x1 + 1, y = y1, z = z1 + 1}
local p2 = {x = x2 - 1, y = y1, z = z2 - 1}
local pos_counter_ground = #minetest.find_nodes_in_area(p1, p2, node_list, false)
return pos_counter_ground + pos_counter_air
end
mcl_structures.register_structure({
name = "jungle_temple",
decoration = {
deco_type = "simple",
place_on = node_list,
flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = -13,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes =
mcl_mapgen.v6 and {
"Jungle"
} or {
"Jungle",
"JungleEdge",
"JungleEdgeM",
"JungleEdgeM_ocean",
"JungleEdge_ocean",
"JungleM",
"JungleM_ocean",
"JungleM_shore",
"Jungle_ocean",
"Jungle_shore",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
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 (random_number + noise) < struct_threshold then return end
local pos
local count = -1
for i = 1, #pos_list do
local pos_i = process_pos(pos_list[i])
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
if count < 0 then return end
place(pos, nil, pr)
end,
place_function = place,
})

View File

@ -1,4 +1,4 @@
name = mcl_structures
author = Wuzzy
description = Structures for MCL2
depends = mcl_loot
author = Wuzzy, kay27, cora
description = Structures for MineClone 2/5
depends = mcl_loot, mcl_mapgen, mcl_worlds

View File

@ -0,0 +1,209 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 15
local noise_multiplier = 1
local random_offset = 133
local struct_threshold = chance_per_chunk - 1
local scanning_ratio = 0.00021
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local node_list = {"mcl_core:dirt_with_grass", "mcl_core:dirt", "mcl_core:stone", "mcl_core:granite", "mcl_core:gravel", "mcl_core:diorite"}
local schematic_file = modpath .. "/schematics/mcl_structures_nice_jungle_temple.mts"
local temple_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
local temple_schematic = loadstring(temple_schematic_lua)()
local size = temple_schematic.size
local sx = size.x
local sy = size.y
local sz = size.z
local offset = vector.round(vector.divide(size, 2))
offset.y = 5
local ox = offset.x
local oy = offset.y
local oz = offset.z
local corner_x = sx - 3
local corner_z = sz - 3
local air_offset_x = ox - 6
local air_offset_z = oz - 6
local function is_air(pos)
local node = minetest.get_node(pos)
return node.name == "air"
end
local stair_support_node = {
{name = "mcl_core:cobble"},
{name = "mcl_core:mossycobble"},
{name = "mcl_core:stonebrick"},
{name = "mcl_core:stonebrickmossy"},
{name = "mcl_core:stonebrickcracked"},
}
local nodes_to_be_supported = {
"mcl_stairs:stair_cobble",
"mcl_stairs:stair_stonebrickmossy",
"mcl_stairs:stair_stonebrickcracked",
}
local function on_placed(p1, rotation, pr, size)
local p2
if rotation == "90" or rotation == "270" then
p2 = {x = p1.x + sz - 1, y = p1.y + sy - 1, z = p1.z + sx - 1}
else
p2 = {x = p1.x + sx - 1, y = p1.y + sy - 1, z = p1.z + sz - 1}
end
-- Support stairs
local y = p1.y + 5
local bottom = mcl_mapgen.get_chunk_beginning(y)
local stair_list = minetest.find_nodes_in_area({x = p1.x, y = y, z = p1.z}, {x = p2.x, y = y, z = p2.z}, nodes_to_be_supported, false)
for i = 1, #stair_list do
local pos = stair_list[i]
pos.y = y - 1
while is_air(pos) and pos.y > bottom do
minetest.swap_node(pos, stair_support_node[pr:next(1, #stair_support_node)])
pos.y = pos.y - 1
end
end
-- Initialize some nodes
local chest_node = "mcl_chests:trapped_chest_small"
local lever_node = "mesecons_walllever:wall_lever_off"
local nodes = minetest.find_nodes_in_area(p1, {x = p2.x, y = p1.y + 5, z = p2.z}, {chest_node, lever_node}, true)
local levers = nodes[lever_node]
for _, pos in pairs(levers) do
mcl_structures.init_node_construct(pos)
end
-- Add loot into chests TODO: fix items
local chests = nodes[chest_node]
for c=1, #chests do
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 25, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 25, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_mobitems:spider_eye", weight = 25, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_books:book", weight = 20, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr)
end },
{ itemstring = "mcl_mobitems:saddle", weight = 20, },
{ itemstring = "mcl_core:apple_gold", weight = 20, },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:emerald", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "", weight = 15, },
{ itemstring = "mobs_mc:iron_horse_armor", weight = 15, },
{ itemstring = "mobs_mc:gold_horse_armor", weight = 10, },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 5, },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:gunpowder", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_core:sand", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:string", weight = 10, amount_min = 1, amount_max = 8 },
}
}}, pr)
mcl_structures.init_node_construct(chests[c])
local meta = minetest.get_meta(chests[c])
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
end
local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, schematic = temple_schematic, pr = pr, on_placed = on_placed})
end
local mcl_mapgen_clamp_to_chunk = mcl_mapgen.clamp_to_chunk
local function process_pos(pos)
return {
x = mcl_mapgen_clamp_to_chunk(pos.x - ox, sx),
y = mcl_mapgen_clamp_to_chunk(pos.y - oy, sy),
z = mcl_mapgen_clamp_to_chunk(pos.z - oz, sz),
}
end
local function get_place_rank(pos)
local x1 = pos.x + 1
local x2 = x1 + corner_x
local z1 = pos.z + 1
local z2 = z1 + corner_z
local y2 = pos.y + 1
local y1 = y2 - 2
if is_air({x = x1, y = y1, z = z1}) then return -1 end
if is_air({x = x2, y = y1, z = z1}) then return -1 end
if is_air({x = x1, y = y1, z = z2}) then return -1 end
if is_air({x = x2, y = y1, z = z2}) then return -1 end
local p1 = {x = x1 + air_offset_x, y = y2, z = z1 + air_offset_z}
local p2 = {x = x2 - air_offset_x, y = y2, z = z2 + air_offset_z}
local pos_counter_air = #minetest.find_nodes_in_area(p1, p2, {"air", "group:buildable_to", "group:deco_block"}, false)
local pos_counter_air = pos_counter_air - 2 * (#minetest.find_nodes_in_area(p1, p2, {"group:tree"}, false))
local p1 = {x = x1 + 1, y = y1, z = z1 + 1}
local p2 = {x = x2 - 1, y = y1, z = z2 - 1}
local pos_counter_ground = #minetest.find_nodes_in_area(p1, p2, node_list, false)
return pos_counter_ground + pos_counter_air
end
mcl_structures.register_structure({
name = "nice_jungle_temple",
decoration = {
deco_type = "simple",
place_on = node_list,
flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = -20,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes =
mcl_mapgen.v6 and {
"Jungle"
} or {
"Jungle",
"JungleEdge",
"JungleEdgeM",
"JungleEdgeM_ocean",
"JungleEdge_ocean",
"JungleM",
"JungleM_ocean",
"JungleM_shore",
"Jungle_ocean",
"Jungle_shore",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
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 (random_number + noise) < struct_threshold then return end
local pos
local count = -1
for i = 1, #pos_list do
local pos_i = process_pos(pos_list[i])
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
if count < 0 then return end
place(pos, nil, pr)
end,
place_function = place,
})

View File

@ -0,0 +1,60 @@
local step = 1
local chunk_borders = false
local levels = {
[-9] = "black",
[-8] = "brown",
[-7] = "brown",
[-6] = "gray",
[-5] = "gray",
[-4] = "red",
[-3] = "orange",
[-2] = "purple",
[-1] = "magenta",
[0] = "pink",
[1] = "yellow",
[2] = "green",
[3] = "lime",
[4] = "blue",
[5] = "cyan",
[6] = "light_blue",
[7] = "silver",
[8] = "silver",
[9] = "white",
}
local math_min, math_max = math.min, math.max
local math_floor, math_ceil = math.floor, math.ceil
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local noise_offset_x_and_z = math_floor(mcl_mapgen.CS_NODES/2)
mcl_mapgen.register_mapgen(function(minp, maxp, seed, vm_context)
local y0 = minp.y
for x0 = minp.x, maxp.x, step do
for z0 = minp.z, maxp.z, step do
local current_noise_level = mcl_structures_get_perlin_noise_level({x = x0 - noise_offset_x_and_z, y = y0, z = z0 - noise_offset_x_and_z})
local amount
if current_noise_level < 0 then
amount = math_max(math_ceil(current_noise_level * 9), -9)
else
amount = math_min(math_floor(current_noise_level * 9), 9)
end
local y0 = maxp.y - 9 + amount
minetest.set_node({x=x0, y=y0, z=z0}, {name = "mcl_core:glass_"..levels[amount]})
end
end
if chunk_borders then
for x0 = minp.x, maxp.x, step do
for y0 = minp.y, maxp.y, step do
minetest.set_node({x=x0, y=y0, z=maxp.z}, {name = "mcl_core:glass"})
end
end
for z0 = minp.z, maxp.z, step do
for y0 = minp.y, maxp.y, step do
minetest.set_node({x=maxp.x, y=y0, z=z0}, {name = "mcl_core:glass"})
end
end
end
end, -1)

View File

@ -0,0 +1,184 @@
-- Generate strongholds.
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
-- A total of 128 strongholds are generated in rings around the world origin.
-- This is the list of rings, starting with the innermost ring first.
local stronghold_rings = {
-- amount: Number of strongholds in ring.
-- min, max: Minimum and maximum distance from (X=0, Z=0).
{ amount = 3, min = 1408, max = 2688 },
{ amount = 6, min = 4480, max = 5760 },
{ amount = 10, min = 7552, max = 8832 },
{ amount = 15, min = 10624, max = 11904 },
{ amount = 21, min = 13696, max = 14976 },
{ amount = 28, min = 16768, max = 18048 },
{ amount = 36, min = 19840, max = 21120 },
{ amount = 9, min = 22912, max = 24192 },
}
local strongholds = {}
local strongholds_inited = false
local superflat = mcl_mapgen.superflat
local size = {x = 13, y = 8, z = 13}
local offset = vector.round(vector.divide(size, 2))
local function place(pos, rotation, pr)
local p1 = { x = pos.x - offset.x, y = pos.y - offset.y, z = pos.z - offset.z }
local p2 = vector.add(p1, vector.subtract(size, 1))
local path = modpath.."/schematics/mcl_structures_end_portal_room_simple.mts"
mcl_structures.place_schematic({
pos = p1,
schematic = path,
rotation = rotation or "0",
pr = pr,
})
-- Find and setup spawner with silverfish
local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner")
for s=1, #spawners do
mcl_mobspawners.setup_spawner(spawners[s], "mobs_mc:silverfish")
end
-- Shuffle stone brick types
local bricks = minetest.find_nodes_in_area(p1, p2, "mcl_core:stonebrick")
for b=1, #bricks do
local r_bricktype = pr:next(1, 100)
local r_infested = pr:next(1, 100)
local bricktype
if r_infested <= 5 then
if r_bricktype <= 30 then -- 30%
bricktype = "mcl_monster_eggs:monster_egg_stonebrickmossy"
elseif r_bricktype <= 50 then -- 20%
bricktype = "mcl_monster_eggs:monster_egg_stonebrickcracked"
else -- 50%
bricktype = "mcl_monster_eggs:monster_egg_stonebrick"
end
else
if r_bricktype <= 30 then -- 30%
bricktype = "mcl_core:stonebrickmossy"
elseif r_bricktype <= 50 then -- 20%
bricktype = "mcl_core:stonebrickcracked"
end
-- 50% stonebrick (no change necessary)
end
if bricktype then
minetest.set_node(bricks[b], { name = bricktype })
end
end
-- Also replace stairs
local stairs = minetest.find_nodes_in_area(p1, p2, {"mcl_stairs:stair_stonebrick", "mcl_stairs:stair_stonebrick_outer", "mcl_stairs:stair_stonebrick_inner"})
for s=1, #stairs do
local stair = minetest.get_node(stairs[s])
local r_type = pr:next(1, 100)
if r_type <= 30 then -- 30% mossy
if stair.name == "mcl_stairs:stair_stonebrick" then
stair.name = "mcl_stairs:stair_stonebrickmossy"
elseif stair.name == "mcl_stairs:stair_stonebrick_outer" then
stair.name = "mcl_stairs:stair_stonebrickmossy_outer"
elseif stair.name == "mcl_stairs:stair_stonebrick_inner" then
stair.name = "mcl_stairs:stair_stonebrickmossy_inner"
end
minetest.set_node(stairs[s], stair)
elseif r_type <= 50 then -- 20% cracky
if stair.name == "mcl_stairs:stair_stonebrick" then
stair.name = "mcl_stairs:stair_stonebrickcracked"
elseif stair.name == "mcl_stairs:stair_stonebrick_outer" then
stair.name = "mcl_stairs:stair_stonebrickcracked_outer"
elseif stair.name == "mcl_stairs:stair_stonebrick_inner" then
stair.name = "mcl_stairs:stair_stonebrickcracked_inner"
end
minetest.set_node(stairs[s], stair)
end
-- 50% no change
end
-- Randomly add ender eyes into end portal frames, but never fill the entire frame
local frames = minetest.find_nodes_in_area(p1, p2, "mcl_portals:end_portal_frame")
local eyes = 0
for f=1, #frames do
local r_eye = pr:next(1, 10)
if r_eye == 1 then
eyes = eyes + 1
if eyes < #frames then
local frame_node = minetest.get_node(frames[f])
frame_node.name = "mcl_portals:end_portal_frame_eye"
minetest.set_node(frames[f], frame_node)
end
end
end
end
-- Determine the stronghold positions and store them into the strongholds table.
-- The stronghold positions are based on the world seed.
-- The actual position might be offset by a few blocks because it might be shifted
-- to make sure the end portal room is completely within the boundaries of a mapchunk.
local function init_strongholds()
if strongholds_inited then
return
end
-- Don't generate strongholds in singlenode
if mcl_mapgen.singlenode then
strongholds_inited = true
return
end
local pr = PseudoRandom(mcl_mapgen.seed)
for s=1, #stronghold_rings do
local ring = stronghold_rings[s]
-- Get random angle
local angle = pr:next()
-- Scale angle to 0 .. 2*math.pi
angle = (angle / 32767) * (math.pi*2)
for a=1, ring.amount do
local dist = pr:next(ring.min, ring.max)
local y
if superflat then
y = mcl_mapgen.overworld.bedrock_max + offset.y
else
y = pr:next(mcl_mapgen.overworld.bedrock_max+1+offset.y, mcl_mapgen.overworld.bedrock_min+48+offset.y)
end
local pos = {
x = mcl_mapgen.clamp_to_chunk(math.floor(math.cos(angle) * dist) - offset.x, size.x) + offset.x,
y = mcl_mapgen.clamp_to_chunk(y - offset.y, size.y) + offset.y,
z = mcl_mapgen.clamp_to_chunk(math.floor(math.sin(angle) * dist) - offset.z, size.z) + offset.z,
}
table.insert(strongholds, { pos = pos, generated = false })
-- Rotate angle by (360 / amount) degrees.
-- This will cause the angles to be evenly distributed in the stronghold ring
angle = math.fmod(angle + ((math.pi*2) / ring.amount), math.pi*2)
end
end
mcl_structures.strongholds = strongholds
mcl_structures.register_structure({
name = "stronghold",
place_function = place,
})
strongholds_inited = true
end
init_strongholds()
-- Stronghold generation for register_on_generated.
mcl_mapgen.register_mapgen(function(minp, maxp, blockseed)
local pr = PseudoRandom(blockseed)
for s=1, #strongholds do
if not strongholds[s].generated then
local pos = strongholds[s].pos
if minp.x <= pos.x and maxp.x >= pos.x and minp.z <= pos.z and maxp.z >= pos.z and minp.y <= pos.y and maxp.y >= pos.y then
place(pos, nil, pr)
strongholds[s].generated = true
end
end
end
end, mcl_mapgen.order.STRONGHOLDS)

View File

@ -0,0 +1,17 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
if not mcl_mapgen.singlenode then
dofile(modpath .. "/desert_temple.lua")
dofile(modpath .. "/desert_well.lua")
dofile(modpath .. "/end_exit_portal.lua")
dofile(modpath .. "/fossil.lua")
dofile(modpath .. "/igloo.lua")
dofile(modpath .. "/ice_spike_small.lua")
dofile(modpath .. "/ice_spike_large.lua")
dofile(modpath .. "/jungle_temple.lua")
dofile(modpath .. "/nice_jungle_temple.lua")
-- dofile(modpath .. "/noise_indicator.lua")
dofile(modpath .. "/stronghold.lua")
dofile(modpath .. "/witch_hut.lua")
end

View File

@ -0,0 +1,136 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local chance_per_chunk = 3
local noise_multiplier = -0.9
local random_offset = 8
local scanning_ratio = 0.01
local struct_threshold = chance_per_chunk - 1
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local schematic_file = modpath .. "/schematics/mcl_structures_witch_hut.mts"
local witch_hut_schematic_lua = minetest.serialize_schematic(schematic_file, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic"
local witch_hut_schematic = loadstring(witch_hut_schematic_lua)()
local node_list = {"mcl_core:dirt_with_grass", "mcl_core:dirt"}
local WITCH_HUT_HEIGHT = 2 -- Exact Y level to spawn witch huts at. This height refers to the height of the floor
local witch_hut_offsets = {
["0"] = {
{x=1, y=0, z=1}, {x=1, y=0, z=5}, {x=6, y=0, z=1}, {x=6, y=0, z=5},
},
["180"] = {
{x=2, y=0, z=1}, {x=2, y=0, z=5}, {x=7, y=0, z=1}, {x=7, y=0, z=5},
},
["270"] = {
{x=1, y=0, z=1}, {x=5, y=0, z=1}, {x=1, y=0, z=6}, {x=5, y=0, z=6},
},
["90"] = {
{x=1, y=0, z=2}, {x=5, y=0, z=2}, {x=1, y=0, z=7}, {x=5, y=0, z=7},
},
}
local function on_placed(place, rotation, pr, size)
local offsets = witch_hut_offsets[rotation]
if not offsets then return end
for _, offset in pairs(offsets) do
local tpos = vector.add(place, offset)
for y = place.y - 1, mcl_mapgen.get_chunk_beginning(place.y - 1), -1 do
tpos.y = y
local nn = minetest.get_node(tpos).name
if not nn then break end
local node = minetest.registered_nodes[nn]
local groups = node.groups
if nn == "mcl_flowers:waterlily" or nn == "mcl_core:water_source" or nn == "mcl_core:water_flowing" or nn == "air" or groups.deco_block then
minetest.swap_node(tpos, {name="mcl_core:tree"})
else
break
end
end
end
end
local function place(pos, rotation, pr)
mcl_structures.place_schematic({pos = pos, rotaton = rotation, schematic = witch_hut_schematic, pr = pr, on_placed = on_placed})
end
local function get_place_rank(pos)
local x, y, z = pos.x, pos.y, pos.z
local p1 = {x = x + 1, y = y + 1, z = z + 1}
local p2 = {x = x + 4, y = y + 4, z = z + 4}
local counter = #minetest.find_nodes_in_area(p1, p2, {"air", "group:buildable_to", "group:deco_block"}, false)
return counter
end
local function tune_pos(pos)
local pos = table.copy(pos)
local y = pos.y - 1
if y >= WITCH_HUT_HEIGHT - 5 and y <= WITCH_HUT_HEIGHT + 5 then
pos.y = WITCH_HUT_HEIGHT
return pos
end
local x = pos.x
local z = pos.z
local p1 = {x = x - 3, y = y , z = z - 3}
local p2 = {x = x + 3, y = y + 2, z = z + 3}
local water_list = minetest.find_nodes_in_area(p1, p2, {"group:water"}, false)
if not water_list or #water_list < 1 then
pos.y = y
return pos
end
local top = -1
for _, pos in pairs(water_list) do
if pos.y > top then
top = pos.y
end
end
pos.y = top
return pos
end
mcl_structures.register_structure({
name = "witch_hut",
decoration = {
deco_type = "simple",
place_on = node_list,
spawn_by = {"mcl_core:water_source", "group:frosted_ice"},
num_spawn_by = 1,
-- flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = mcl_mapgen.overworld.min,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes = mcl_mapgen.v6 and {
"Normal",
} or {
"Swampland",
"Swampland_shore",
"Swampland_ocean",
"Swampland_deep_ocean",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
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 (random_number + noise) < struct_threshold then return end
local pos = tune_pos(pos_list[1])
if #pos_list > 1 then
local count = get_place_rank(pos)
for i = 2, #pos_list do
local pos_i = pos_list[i]
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
end
place(pos, nil, pr)
end,
place_function = place,
})

View File

@ -1,6 +1,6 @@
MCL_Villages:
============================
A fork of Rochambeau's "Settlements" mod converted for use in MineClone2.
A fork of Rochambeau's "Settlements" mod converted for use in MineClone5.
--------------
Using the mod:

View File

@ -88,7 +88,7 @@ function settlements.create_site_plan(maxp, minp, pr)
-- find center_surface of chunk
local center_surface , surface_material = settlements.find_surface(center, true)
local chunks = {}
chunks[mcl_vars.get_chunk_number(center)] = true
chunks[mcl_mapgen.get_chunk_number(center)] = true
-- go build settlement around center
if not center_surface then return false end
@ -124,7 +124,7 @@ function settlements.create_site_plan(maxp, minp, pr)
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_vars.get_chunk_number(pos1)
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)
@ -268,15 +268,13 @@ function settlements.place_schematics(settlement_info, pr)
local schematic = loadstring(schem_lua)()
-- build foundation for the building an make room above
-- place schematic
mcl_structures.place_schematic(
pos,
schematic,
rotation,
nil,
true,
nil,
init_nodes,
pr
)
mcl_structures.place_schematic({
pos = pos,
schematic = schematic,
rotation = rotation,
force_placement = true,
on_place = init_nodes,
pr = pr,
})
end
end

View File

@ -1,6 +1,8 @@
settlements = {}
settlements.modpath = minetest.get_modpath(minetest.get_current_modname())
local minetest_get_spawn_level = minetest.get_spawn_level
dofile(settlements.modpath.."/const.lua")
dofile(settlements.modpath.."/utils.lua")
dofile(settlements.modpath.."/foundation.lua")
@ -53,6 +55,7 @@ end
-- on map generation, try to build a settlement
--
local function build_a_settlement(minp, maxp, blockseed)
minetest.log("action","[mcl_villages] Building village at mapchunk " .. minetest.pos_to_string(minp) .. "..." .. minetest.pos_to_string(maxp) .. ", blockseed = " .. tostring(blockseed))
local pr = PseudoRandom(blockseed)
-- fill settlement_info with buildings and their data
@ -69,29 +72,38 @@ local function build_a_settlement(minp, maxp, blockseed)
settlements.place_schematics(settlement_info, pr)
end
local function ecb_village(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end
local minp, maxp, blockseed = param.minp, param.maxp, param.blockseed
build_a_settlement(minp, maxp, blockseed)
end
-- Disable natural generation in singlenode.
local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name ~= "singlenode" then
mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed)
mcl_mapgen.register_mapgen(function(minp, maxp, blockseed)
-- local str1 = (maxp.y >= 0 and blockseed % 77 == 17) and "YES" or "no"
-- minetest.log("action","[mcl_villages] " .. str1 .. ": minp=" .. minetest.pos_to_string(minp) .. ", maxp=" .. minetest.pos_to_string(maxp) .. ", blockseed=" .. tostring(blockseed))
-- don't build settlement underground
if maxp.y < 0 then return end
-- randomly try to build settlements
if blockseed % 77 ~= 17 then return end
-- needed for manual and automated settlement building
-- don't build settlements on (too) uneven terrain
--local heightmap = minetest.get_mapgen_object("heightmap")
local height_difference = settlements.evaluate_heightmap()
-- 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 height_difference, min, max
local pr1=PseudoRandom(blockseed)
for i=1,pr1:next(5,10) do
local x = pr1:next(0, 40) + minp.x + 19
local z = pr1:next(0, 40) + minp.z + 19
local y = minetest_get_spawn_level(x, z)
if not y then return end
if y < (min or y+1) then min = y end
if y > (max or y-1) then max = y end
end
height_difference = max - min + 1
--------------------------------------------------------------------------
if height_difference > max_height_difference then return end
local param={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}
minetest.emerge_area(minp, maxp, ecb_village, param)
end)
build_a_settlement(minp, maxp, blockseed)
end, mcl_mapgen.order.VILLAGES)
end
-- manually place villages
if minetest.is_creative_enabled("") then

View File

@ -1,4 +1,4 @@
local get_node = mcl_vars.get_node
local get_node = mcl_mapgen.get_far_node
-------------------------------------------------------------------------------
-- function to copy tables
@ -207,44 +207,6 @@ function shuffle(tbl, pr)
return table
end
-------------------------------------------------------------------------------
-- evaluate heightmap
-------------------------------------------------------------------------------
function settlements.evaluate_heightmap()
local heightmap = minetest.get_mapgen_object("heightmap")
-- max height and min height, initialize with impossible values for easier first time setting
local max_y = -50000
local min_y = 50000
-- only evaluate the center square of heightmap 40 x 40
local square_start = 1621
local square_end = 1661
for j = 1 , 40, 1 do
for i = square_start, square_end, 1 do
-- skip buggy heightmaps, return high value
if heightmap[i] == -31000 or heightmap[i] == 31000 then
return max_height_difference + 1
end
if heightmap[i] < min_y then
min_y = heightmap[i]
end
if heightmap[i] > max_y then
max_y = heightmap[i]
end
end
-- set next line
square_start = square_start + 80
square_end = square_end + 80
end
-- return the difference between highest and lowest pos in chunk
local height_diff = max_y - min_y
-- filter buggy heightmaps
if height_diff <= 1 then
return max_height_difference + 1
end
-- debug info
settlements.debug("heightdiff ".. height_diff)
return height_diff
end
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------

View File

@ -19,8 +19,7 @@ end
local probability_railcaves_in_mapchunk = P(0.33333)
setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_railcaves_in_mapchunk"))
-- Extra check to prevent mod griefing in singlenode, mcimported worlds.
local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name == "singlenode" then
if mcl_mapgen.singlenode then
probability_railcaves_in_mapchunk = P(0)
elseif setting then
probability_railcaves_in_mapchunk = P(setting)
@ -96,10 +95,10 @@ end
-- Max. and min. heights between rail corridors are generated
local height_min
if mcl_vars.mg_lava then
height_min = mcl_vars.mg_lava_overworld_max + 2
if mcl_mapgen.lava then
height_min = mcl_mapgen.overworld.lava_max + 2
else
height_min = mcl_vars.mg_bedrock_overworld_max + 2
height_min = mcl_mapgen.overworld.bedrock_max + 2
end
local height_max = mcl_worlds.layer_to_y(60)
@ -1093,7 +1092,7 @@ local function create_corridor_system(main_cave_coords)
end
-- The rail corridor algorithm starts here
mcl_mapgen_core.register_generator("railcorridors", nil, function(minp, maxp, blockseed, _pr)
mcl_mapgen.register_mapgen(function(minp, maxp, blockseed)
-- We re-init the randomizer for every mapchunk as we start generating in the middle of each mapchunk.
-- We can't use the mapgen seed as this would make the algorithm depending on the order the mapchunk generate.
InitRandomizer(blockseed)

View File

@ -81,7 +81,7 @@ local dir_step = storage:get_int("mcl_spawn_dir_step") or 0
local dir_ind = storage:get_int("mcl_spawn_dir_ind") or 1
local emerge_pos1, emerge_pos2
local spawn_limit = mcl_vars.mapgen_edge_max
local spawn_limit = mcl_mapgen.EDGE_MAX
--Functions
@ -500,7 +500,11 @@ function mcl_spawn.shadow_worker()
if success then
local wsp_node = minetest.get_node(wsp)
if not (wsp_node and wsp_node.name == "ignore")
if wsp_node and
(
(minetest.compare_block_status and (minetest.compare_block_status(wsp, "loaded") or minetest.compare_block_status(wsp, "active")))
or minetest.get_node_or_nil(wsp)
)
and ((not good_for_respawn(wsp)) or ((no_trees_area_counter >= 0) and not can_find_tree(wsp))) then
success = false
minetest.log("action", "[mcl_spawn] World spawn position isn't safe anymore: "..minetest.pos_to_string(wsp))

View File

@ -1,4 +1,4 @@
name = mcl_spawn
author = Wuzzy
description = Set and get the player's respawn position
depends = mcl_init
depends = mcl_mapgen