Add basic part of new mapgen api
parent
9b881df465
commit
b3e8f24876
|
@ -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
|
||||
|
|
|
@ -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).
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_mapgen
|
||||
author = kay27
|
||||
description = MineClone 2/5 MapGen Basic Stuff
|
||||
depends = mcl_init
|
|
@ -176,15 +176,15 @@ end
|
|||
-- 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
|
||||
end
|
||||
end
|
||||
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
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
Binary file not shown.
|
@ -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)
|
|
@ -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
|
|
@ -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,
|
||||
})
|
|
@ -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,
|
||||
})
|
|
@ -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})
|
|
@ -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})
|
|
@ -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)
|
|
@ -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)
|
|
@ -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,
|
||||
})
|
|
@ -2,44 +2,307 @@ 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
|
||||
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
|
||||
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)]
|
||||
end
|
||||
if rotation == "random" then
|
||||
x = math.max(x, z)
|
||||
z = x
|
||||
elseif rotation == "90" or rotation == "270" then
|
||||
x, z = z, x
|
||||
else
|
||||
rotation = rotations[math.random(1,#rotations)]
|
||||
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)
|
||||
|
||||
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)
|
||||
|
@ -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"
|
||||
minetest.swap_node({x = x0 + x, y = y0, z = z0 + z}, {name = "mcl_portals:portal_end"})
|
||||
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
|
||||
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")
|
||||
|
|
|
@ -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,
|
||||
})
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
})
|
|
@ -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)
|
Binary file not shown.
Binary file not shown.
|
@ -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)
|
|
@ -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
|
|
@ -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,
|
||||
})
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
-------------------------------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_spawn
|
||||
author = Wuzzy
|
||||
description = Set and get the player's respawn position
|
||||
depends = mcl_init
|
||||
depends = mcl_mapgen
|
||||
|
|
Loading…
Reference in New Issue