Compare commits

...

25 Commits

Author SHA1 Message Date
kay27 888251e3ec #1976 Use Perlin noise to initialize chorus growth 2022-03-18 01:03:40 +04:00
kay27 b91d8875f3 #1976 Make river water 20%-transparent 2022-03-17 04:39:53 +04:00
kay27 8eb0edaf51 #1976 Fix dirt_with_snow in biomes and dirt in nether v6 2022-03-16 21:50:08 +04:00
kay27 4bd06723b4 Add mcl_info HUD to display current biome 2022-03-14 20:46:12 +04:00
kay27 b565de7122 Enable noise/chunk edge indicator (see game settings) 2022-03-14 20:35:36 +04:00
kay27 74257f5ff2 Update mcl_mapgen and mcl_time 2022-03-14 20:32:36 +04:00
kay27 ca37c60511 Increase spawn probability of some structures 2022-03-14 20:25:02 +04:00
kay27 9dadbb2bb4 Merge master into new_mapgen_api 2022-03-14 20:21:41 +04:00
kay27 18c815fbf1 Fix compatibility with 5.4 2022-02-19 17:23:10 +04:00
kay27 cc064636a9 Fix Nether Wart growth again 2022-02-19 02:34:47 +04:00
kay27 398f51b1da Update mcl_time to v2 2022-02-19 01:54:06 +04:00
kay27 9fe692fb6f Fix mcl_time node time update 2022-02-19 00:13:31 +04:00
kay27 605bb8c619 Make mapgen compatible with mt 5.3 2022-02-18 18:29:10 +04:00
kay27 53cecf7b41 Reduce the number of time_speed less than 1 warnings logged 2022-02-18 17:43:28 +04:00
kay27 792c740a4d Fix End stuff 2022-02-18 04:17:27 +04:00
kay27 34ab356193 Localise a var 2022-02-18 04:06:45 +04:00
kay27 03371421d8 Merge remote-tracking branch 'origin/master' into new_mapgen_api 2022-02-18 03:48:47 +04:00
kay27 54ea8ba6b1 Update mapgen api to v3 from mcl5 2022-02-18 03:48:03 +04:00
kay27 229f821758 Remove ancient debris 2022-01-20 05:20:45 +04:00
kay27 bb008d6341 Retarget mcl_worlds mod dependency 2022-01-20 05:12:44 +04:00
kay27 682d2b3229 Fix mcl_worlds to work with mapgen api 2022-01-20 05:11:21 +04:00
kay27 f592437e8a Change MineClone 5 to MineClone 2 in mcl_villages readme 2022-01-20 02:14:51 +04:00
kay27 a639630b32 Add empty lines between functions in mcl_mapgen mod 2022-01-20 00:53:55 +04:00
kay27 679a1db436 Apply markdown format to mcl_mapgen api doc 2022-01-20 00:44:23 +04:00
kay27 b3e8f24876 Add basic part of new mapgen api 2022-01-19 19:09:37 +04:00
88 changed files with 5472 additions and 3488 deletions

View File

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

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

@ -0,0 +1,120 @@
# 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"}`.
### mcl_mapgen.clamp_to_chunk(x, size)
--------------------------------------
Returns new `x`, slighty tuned to make structure of size `size` be within single chunk side of 80 nodes.
### function mcl_mapgen.get_chunk_beginning(x)
----------------------------------------------
Returns chunk beginning of `x`. It is the same as `minp.axis` for per-chunk callbacks, but we don't always have `minp`.
## Constants:
* `mcl_mapgen.EDGE_MIN`, `mcl_mapgen.EDGE_MAX` - world edges, min & max.
* `mcl_mapgen.seed`, `mcl_mapgen.name` - mapgen seed & name.
* `mcl_mapgen.v6`, `mcl_mapgen.superflat`, `mcl_mapgen.singlenode` - is mapgen v6, superflat, singlenode.
* `mcl_mapgen.normal` is mapgen normal (not superflat or singlenode).

View File

@ -0,0 +1,574 @@
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,
}
-- Begin of Compatibility stuff
local function unsigned(v)
if v < 0 then
v = 0x100000000 - (math.abs(v) % 0x100000000)
end
return v % 0x100000000
end
if bit == nil then
bit = {}
function bit.bxor(a, b)
local a = unsigned(a)
local b = unsigned(b)
local c = 0
for n = 31, 0, -1 do
local mask = math.floor(2^n)
if (a >= mask) ~= (b >= mask) then
c = c + mask
end
a = a % mask
b = b % mask
end
return c
end
end
if vector.metatable == nil then
function math.round(x)
if x >= 0 then
return math.floor(x + 0.5)
end
return math.ceil(x - 0.5)
end
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/vector.lua")
end
-- End of compatibility stuff
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 = 31000 -- MAX_MAP_GENERATION_LIMIT, 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
mcl_mapgen.LAST_BLOCK = mcl_mapgen.CS - 1
mcl_mapgen.LAST_NODE_IN_BLOCK = mcl_mapgen.BS - 1
mcl_mapgen.LAST_NODE_IN_CHUNK = mcl_mapgen.CS_NODES - 1
mcl_mapgen.HALF_CS_NODES = math_floor(mcl_mapgen.CS_NODES / 2)
mcl_mapgen.HALF_BS = math_floor(mcl_mapgen.BS / 2)
mcl_mapgen.CS_3D = mcl_mapgen.CS^3
mcl_mapgen.CHUNK_WITH_SHELL = mcl_mapgen.CS + 2
mcl_mapgen.CHUNK_WITH_SHELL_3D = mcl_mapgen.CHUNK_WITH_SHELL^3
local central_chunk_min_pos = mcl_mapgen.OFFSET * mcl_mapgen.BS
local central_chunk_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.flat = mcl_mapgen.name == "flat"
mcl_mapgen.superflat = mcl_mapgen.flat and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
mcl_mapgen.singlenode = mcl_mapgen.name == "singlenode"
mcl_mapgen.normal = not mcl_mapgen.superflat and not mcl_mapgen.singlenode
local flat, superflat, singlenode, normal = mcl_mapgen.flat, mcl_mapgen.superflat, mcl_mapgen.singlenode, mcl_mapgen.normal
minetest_log("action", "[mcl_mapgen] Mapgen mode: " .. (normal and "normal" or (superflat and "superflat" or (flat and "flat" 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 offset = mcl_mapgen.OFFSET -- Central mapchunk offset (in blocks)
local CS_NODES = mcl_mapgen.CS_NODES
local LAST_BLOCK = mcl_mapgen.LAST_BLOCK
local LAST_NODE_IN_BLOCK = mcl_mapgen.LAST_NODE_IN_BLOCK
local LAST_NODE_IN_CHUNK = mcl_mapgen.LAST_NODE_IN_CHUNK
local HALF_CS_NODES = mcl_mapgen.HALF_CS_NODES
local CS_3D = mcl_mapgen.CS_3D
local CHUNK_WITH_SHELL = mcl_mapgen.CHUNK_WITH_SHELL
local CHUNK_WITH_SHELL_3D = mcl_mapgen.CHUNK_WITH_SHELL_3D
local DEFAULT_ORDER = order.DEFAULT
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 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 lvm_buffer, lvm_param2_buffer, lvm_light_buffer = {}, {}, {} -- Static buffer pointers
local all_blocks_in_chunk = {}
for x = -1, LAST_BLOCK+1 do
for y = -1, LAST_BLOCK+1 do
for z = -1, LAST_BLOCK+1 do
all_blocks_in_chunk[CHUNK_WITH_SHELL * (CHUNK_WITH_SHELL * y + z) + x] = vector.new(x, y, z)
end
end
end
local chunk_scan_range = {
[-CS_NODES] = {-1 , -1 },
[ 0 ] = {-1 , LAST_BLOCK+1},
[ CS_NODES] = {LAST_BLOCK+1, LAST_BLOCK+1},
}
local function is_chunk_finished(minp)
local center = vector.add(minp, HALF_CS_NODES)
for check_x = center.x - CS_NODES, center.x + CS_NODES, CS_NODES do
for check_y = center.y - CS_NODES, center.y + CS_NODES, CS_NODES do
for check_z = center.z - CS_NODES, center.z + CS_NODES, CS_NODES do
local pos = vector.new(check_x, check_y, check_z)
if pos ~= center then
minetest_get_voxel_manip():read_from_map(pos, pos)
local node = minetest_get_node(pos)
if node.name == "ignore" then
return
end
end
end
end
end
return true
end
local function uint32_t(v)
if v >= 0 then
return v % 0x100000000
end
return 0x100000000 - (math.abs(v) % 0x100000000)
end
local function get_block_seed(pos, current_seed)
local current_seed = current_seed or uint32_t(tonumber(seed))
return uint32_t(uint32_t(23 * pos.x) + uint32_t(42123 * pos.y) + uint32_t(38134234 * pos.z) + current_seed)
end
local function get_block_seed2(pos, current_seed)
local current_seed = current_seed or uint32_t(tonumber(seed))
local n = uint32_t(uint32_t(1619 * pos.x) + uint32_t(31337 * pos.y) + uint32_t(52591 * pos.z) + uint32_t(1013 * current_seed))
n = bit.bxor(bit.rshift(n, 13), n)
local seed = uint32_t((n * uint32_t(n * n * 60493 + 19990303) + 1376312589))
return seed
end
local function get_block_seed3(pos, current_seed)
local current_seed = uint32_t(current_seed or uint32_t(tonumber(seed)))
local x = uint32_t((pos.x + 32768) * 13)
local y = uint32_t((pos.y + 32767) * 13873)
local z = uint32_t((pos.z + 76705) * 115249)
local seed = uint32_t(bit.bxor(current_seed, x, y, z))
return seed
end
minetest.register_on_generated(function(minp, maxp, chunkseed)
local minp, maxp, chunkseed = minp, maxp, chunkseed
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
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,
}
local current_blocks = {}
local current_chunks = {}
if safe_functions > 0 then
local ready_blocks = table.copy(all_blocks_in_chunk)
local p0 = vector.new(minp)
local center = vector.add(p0, HALF_CS_NODES)
for x = -CS_NODES, CS_NODES, CS_NODES do
for y = -CS_NODES, CS_NODES, CS_NODES do
for z = -CS_NODES, CS_NODES, CS_NODES do
if x ~= 0 or y ~= 0 or z ~= 0 then
local offset = vector.new(x, y, z)
local pos = center + offset
minetest_get_voxel_manip():read_from_map(pos, pos)
local node = minetest_get_node(pos)
local is_generated = node.name ~= "ignore"
if is_generated then
local adjacent_chunk_pos = p0 + offset
if is_chunk_finished(adjacent_chunk_pos) then
current_chunks[#current_chunks + 1] = adjacent_chunk_pos
end
else
local scan_range_x = chunk_scan_range[x]
for cut_x = scan_range_x[1], scan_range_x[2] do
local scan_range_y = chunk_scan_range[y]
for cut_y = scan_range_y[1], scan_range_y[2] do
local scan_range_z = chunk_scan_range[z]
for cut_z = scan_range_z[1], scan_range_z[2] do
ready_blocks[CHUNK_WITH_SHELL * (CHUNK_WITH_SHELL * cut_y + cut_z) + cut_x] = nil
end
end
end
end
end
end
end
end
local number_of_blocks = 0
for k, offset in pairs(ready_blocks) do
if queue_blocks_lvm_counter > 0 or nodes_block > 0 then
local block_minp = p0 + vector.multiply(offset, BS)
local block_maxp = vector.add(block_minp, LAST_NODE_IN_BLOCK)
local blockseed = get_block_seed3(block_minp)
vm_context.minp, vm_context.maxp, vm_context.blockseed = block_minp, block_maxp, blockseed
-- --
-- mcl_mapgen.register_mapgen_block_lvm(function(vm_context), order_number) --
-- --
for _, v in pairs(queue_blocks_lvm) do
v.callback_function(vm_context)
end
if nodes_block > 0 then
current_blocks[#current_blocks + 1] = { minp = block_minp, maxp = block_maxp, blockseed = blockseed }
end
end
number_of_blocks = number_of_blocks + 1
end
if number_of_blocks == CHUNK_WITH_SHELL_3D then
current_chunks[#current_chunks + 1] = p0
end
end
if #queue_unsafe_engine > 0 then
vm_context.minp, vm_context.maxp = minp, maxp
-- * U N S A F E --
-- mcl_mapgen.register_on_generated(function(vm_context), order_number) --
-- * U N S A F E --
for _, v in pairs(queue_unsafe_engine) do
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)
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, chunk_minp in pairs(current_chunks) do
local chunk_maxp = vector.add(chunk_minp, LAST_NODE_IN_CHUNK)
local current_chunk_seed = get_block_seed3(vector.subtract(chunk_minp, BS))
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 = chunk_minp,
emax = chunk_maxp,
minp = chunk_minp,
maxp = chunk_maxp,
chunkseed = current_chunk_seed,
}
-- --
-- mcl_mapgen.register_mapgen_lvm(function(vm_context), order_number) --
-- --
for _, v in pairs(queue_chunks_lvm) do
v.f(vm_context)
end
-- --
-- mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed, vm_context), order_number) --
-- --
for _, v in pairs(queue_chunks_nodes) do
v.f(chunk_minp, chunk_maxp, current_chunk_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
end
for _, b in pairs(current_blocks) do
-- --
-- mcl_mapgen.register_mapgen_block(function(minp, maxp, blockseed), order_number) --
-- --
for _, v in pairs(queue_blocks_nodes) do
v.f(b.minp, b.maxp, b.blockseed)
end
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
-- Overworld
overworld.min = -62
if superflat then
mcl_mapgen.ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
overworld.min = mcl_mapgen.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 superflat then
nether.flat_floor = nether.bedrock_bottom_max + 4
nether.flat_ceiling = nether.bedrock_bottom_max + 52
elseif flat then
nether.flat_floor = nether.lava_max + 4
nether.flat_ceiling = nether.lava_max + 52
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)
if tonumber(x) then
return x - ((x + central_chunk_min_pos) % CS_NODES)
end
if x.x then
return {
x = mcl_mapgen.get_chunk_beginning(x.x),
y = mcl_mapgen.get_chunk_beginning(x.y),
z = mcl_mapgen.get_chunk_beginning(x.z)
}
end
end
function mcl_mapgen.get_chunk_ending(x)
if tonumber(x) then
return mcl_mapgen.get_chunk_beginning(x) + LAST_NODE_IN_CHUNK
end
if x.x then
return {
x = mcl_mapgen.get_chunk_beginning(x.x) + LAST_NODE_IN_CHUNK,
y = mcl_mapgen.get_chunk_beginning(x.y) + LAST_NODE_IN_CHUNK,
z = mcl_mapgen.get_chunk_beginning(x.z) + LAST_NODE_IN_CHUNK
}
end
end
mcl_mapgen.get_block_seed = get_block_seed
mcl_mapgen.get_block_seed2 = get_block_seed2
mcl_mapgen.get_block_seed3 = get_block_seed3

View File

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

View File

@ -0,0 +1,362 @@
--[[
Vector helpers
Note: The vector.*-functions must be able to accept old vectors that had no metatables
]]
-- localize functions
local setmetatable = setmetatable
vector = {}
local metatable = {}
vector.metatable = metatable
local xyz = {"x", "y", "z"}
-- only called when rawget(v, key) returns nil
function metatable.__index(v, key)
return rawget(v, xyz[key]) or vector[key]
end
-- only called when rawget(v, key) returns nil
function metatable.__newindex(v, key, value)
rawset(v, xyz[key] or key, value)
end
-- constructors
local function fast_new(x, y, z)
return setmetatable({x = x, y = y, z = z}, metatable)
end
function vector.new(a, b, c)
if a and b and c then
return fast_new(a, b, c)
end
-- deprecated, use vector.copy and vector.zero directly
if type(a) == "table" then
return vector.copy(a)
else
assert(not a, "Invalid arguments for vector.new()")
return vector.zero()
end
end
function vector.zero()
return fast_new(0, 0, 0)
end
function vector.copy(v)
assert(v.x and v.y and v.z, "Invalid vector passed to vector.copy()")
return fast_new(v.x, v.y, v.z)
end
function vector.from_string(s, init)
local x, y, z, np = string.match(s, "^%s*%(%s*([^%s,]+)%s*[,%s]%s*([^%s,]+)%s*[,%s]" ..
"%s*([^%s,]+)%s*[,%s]?%s*%)()", init)
x = tonumber(x)
y = tonumber(y)
z = tonumber(z)
if not (x and y and z) then
return nil
end
return fast_new(x, y, z), np
end
function vector.to_string(v)
return string.format("(%g, %g, %g)", v.x, v.y, v.z)
end
metatable.__tostring = vector.to_string
function vector.equals(a, b)
return a.x == b.x and
a.y == b.y and
a.z == b.z
end
metatable.__eq = vector.equals
-- unary operations
function vector.length(v)
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
end
-- Note: we can not use __len because it is already used for primitive table length
function vector.normalize(v)
local len = vector.length(v)
if len == 0 then
return fast_new(0, 0, 0)
else
return vector.divide(v, len)
end
end
function vector.floor(v)
return vector.apply(v, math.floor)
end
function vector.round(v)
return fast_new(
math.round(v.x),
math.round(v.y),
math.round(v.z)
)
end
function vector.apply(v, func)
return fast_new(
func(v.x),
func(v.y),
func(v.z)
)
end
function vector.distance(a, b)
local x = a.x - b.x
local y = a.y - b.y
local z = a.z - b.z
return math.sqrt(x * x + y * y + z * z)
end
function vector.direction(pos1, pos2)
return vector.subtract(pos2, pos1):normalize()
end
function vector.angle(a, b)
local dotp = vector.dot(a, b)
local cp = vector.cross(a, b)
local crossplen = vector.length(cp)
return math.atan2(crossplen, dotp)
end
function vector.dot(a, b)
return a.x * b.x + a.y * b.y + a.z * b.z
end
function vector.cross(a, b)
return fast_new(
a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x
)
end
function metatable.__unm(v)
return fast_new(-v.x, -v.y, -v.z)
end
-- add, sub, mul, div operations
function vector.add(a, b)
if type(b) == "table" then
return fast_new(
a.x + b.x,
a.y + b.y,
a.z + b.z
)
else
return fast_new(
a.x + b,
a.y + b,
a.z + b
)
end
end
function metatable.__add(a, b)
return fast_new(
a.x + b.x,
a.y + b.y,
a.z + b.z
)
end
function vector.subtract(a, b)
if type(b) == "table" then
return fast_new(
a.x - b.x,
a.y - b.y,
a.z - b.z
)
else
return fast_new(
a.x - b,
a.y - b,
a.z - b
)
end
end
function metatable.__sub(a, b)
return fast_new(
a.x - b.x,
a.y - b.y,
a.z - b.z
)
end
function vector.multiply(a, b)
if type(b) == "table" then
return fast_new(
a.x * b.x,
a.y * b.y,
a.z * b.z
)
else
return fast_new(
a.x * b,
a.y * b,
a.z * b
)
end
end
function metatable.__mul(a, b)
if type(a) == "table" then
return fast_new(
a.x * b,
a.y * b,
a.z * b
)
else
return fast_new(
a * b.x,
a * b.y,
a * b.z
)
end
end
function vector.divide(a, b)
if type(b) == "table" then
return fast_new(
a.x / b.x,
a.y / b.y,
a.z / b.z
)
else
return fast_new(
a.x / b,
a.y / b,
a.z / b
)
end
end
function metatable.__div(a, b)
-- scalar/vector makes no sense
return fast_new(
a.x / b,
a.y / b,
a.z / b
)
end
-- misc stuff
function vector.offset(v, x, y, z)
return fast_new(
v.x + x,
v.y + y,
v.z + z
)
end
function vector.sort(a, b)
return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)),
fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
end
function vector.check(v)
return getmetatable(v) == metatable
end
local function sin(x)
if x % math.pi == 0 then
return 0
else
return math.sin(x)
end
end
local function cos(x)
if x % math.pi == math.pi / 2 then
return 0
else
return math.cos(x)
end
end
function vector.rotate_around_axis(v, axis, angle)
local cosangle = cos(angle)
local sinangle = sin(angle)
axis = vector.normalize(axis)
-- https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
local dot_axis = vector.multiply(axis, vector.dot(axis, v))
local cross = vector.cross(v, axis)
return vector.new(
cross.x * sinangle + (v.x - dot_axis.x) * cosangle + dot_axis.x,
cross.y * sinangle + (v.y - dot_axis.y) * cosangle + dot_axis.y,
cross.z * sinangle + (v.z - dot_axis.z) * cosangle + dot_axis.z
)
end
function vector.rotate(v, rot)
local sinpitch = sin(-rot.x)
local sinyaw = sin(-rot.y)
local sinroll = sin(-rot.z)
local cospitch = cos(rot.x)
local cosyaw = cos(rot.y)
local cosroll = math.cos(rot.z)
-- Rotation matrix that applies yaw, pitch and roll
local matrix = {
{
sinyaw * sinpitch * sinroll + cosyaw * cosroll,
sinyaw * sinpitch * cosroll - cosyaw * sinroll,
sinyaw * cospitch,
},
{
cospitch * sinroll,
cospitch * cosroll,
-sinpitch,
},
{
cosyaw * sinpitch * sinroll - sinyaw * cosroll,
cosyaw * sinpitch * cosroll + sinyaw * sinroll,
cosyaw * cospitch,
},
}
-- Compute matrix multiplication: `matrix` * `v`
return vector.new(
matrix[1][1] * v.x + matrix[1][2] * v.y + matrix[1][3] * v.z,
matrix[2][1] * v.x + matrix[2][2] * v.y + matrix[2][3] * v.z,
matrix[3][1] * v.x + matrix[3][2] * v.y + matrix[3][3] * v.z
)
end
function vector.dir_to_rotation(forward, up)
forward = vector.normalize(forward)
local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0)
if not up then
return rot
end
assert(vector.dot(forward, up) < 0.000001,
"Invalid vectors passed to vector.dir_to_rotation().")
up = vector.normalize(up)
-- Calculate vector pointing up with roll = 0, just based on forward vector.
local forwup = vector.rotate(vector.new(0, 1, 0), rot)
-- 'forwup' and 'up' are now in a plane with 'forward' as normal.
-- The angle between them is the absolute of the roll value we're looking for.
rot.z = vector.angle(forwup, up)
-- Since vector.angle never returns a negative value or a value greater
-- than math.pi, rot.z has to be inverted sometimes.
-- To determine wether this is the case, we rotate the up vector back around
-- the forward vector and check if it worked out.
local back = vector.rotate_around_axis(up, forward, -rot.z)
-- We don't use vector.equals for this because of floating point imprecision.
if (back.x - forwup.x) * (back.x - forwup.x) +
(back.y - forwup.y) * (back.y - forwup.y) +
(back.z - forwup.z) * (back.z - forwup.z) > 0.0000001 then
rot.z = -rot.z
end
return rot
end

View File

@ -0,0 +1,107 @@
# mcl_time v2.2
## by kay27 for MineClone 5
---------------------------
This mod counts time when all players sleep or some area is inactive.
It depends very much on `time_speed` configuration variable, which could be changed 'on the fly' by a chat command:
* `/set time_speed 72`
If `time_speed` set to 0, this mod logs warnings and returns zeroes.
### mcl_time.get_seconds_irl()
------------------------------
Returns: Integer value of realtime (not in-game) seconds since world creation.
Usually this value grow smoothly. But when you skip the night being in the bed, or leave some area for some time, you may experience value jumps. That's basically the idea of this mod.
### mcl_time.get_number_of_times(last_time, interval, chance)
-------------------------------------------------------------
Returns the number of how many times something would probably happen if the area was active and we didn't skip the nights.
Arguments:
* `last_time` - you pass last known for you value of `seconds_irl`
* `interval` and `chance` - interval and chance like from ABM setup
Returns:
* Integer number of how many times something would probably happen if the area was active all the time and we didn't skip the nights.
* Integer value of in-real-life (not in-game) seconds since world creation.
### mcl_time.touch(pos)
-----------------------
This function 'toches' node at position `pos` by writing `_t` meta variable of `seconds_irl`.
### mcl_time.get_number_of_times_at_pos(pos, interval, chance)
--------------------------------------------------------------
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
Argunments:
* `pos` - node position
* `interval` and `chance` - interval and chance like from ABM setup
Returns:
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `0`.
### mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
-------------------------------------------------------------------
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
Argunments:
* `pos` - node position
* `interval` and `chance` - interval and chance like from ABM setup
Returns:
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `1`.
### mcl_time.get_number_of_times_at_pos_or_nil(pos, interval, chance)
---------------------------------------------------------------------
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
Argunments:
* `pos` - node position
* `interval` and `chance` - interval and chance like from ABM setup
Returns:
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `nil`.
### mcl_time.get_irl_seconds_passed_at_pos(pos)
-----------------------------------------------
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
It uses node meta variable `_t` to calculate this value.
Argunments:
* `pos` - node position
Returns:
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `0`.
### mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
----------------------------------------------------
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
It uses node meta variable `_t` to calculate this value.
Argunments:
* `pos` - node position
Returns:
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `1`.
### mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
----------------------------------------------------
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
It uses node meta variable `_t` to calculate this value.
Argunments:
* `pos` - node position
Returns:
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `nil`.

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

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

View File

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

View File

@ -5,25 +5,25 @@ local get_connected_players = minetest.get_connected_players
-- For a given position, returns a 2-tuple:
-- 1st return value: true if pos is in void
-- 2nd return value: true if it is in the deadly part of the void
local min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
local max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
function mcl_worlds.is_in_void(pos)
local void =
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
(pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
local y = pos.y
local void = not ((y < max1 and y > min1) or (y < max2 and y > min2) or (y < max3 and y > min3))
local void_deadly = false
local deadly_tolerance = 64 -- the player must be this many nodes “deep” into the void to be damaged
if void then
-- Overworld → Void → End → Void → Nether → Void
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
if y < min1 and y > max2 then
void_deadly = y < min1 - deadly_tolerance
elseif y < min2 and y > max3 then
-- The void between End and Nether. Like usual, but here, the void
-- *above* the Nether also has a small tolerance area, so player
-- can fly above the Nether without getting hurt instantly.
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
elseif pos.y < mcl_vars.mg_nether_min then
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
void_deadly = (y < min2 - deadly_tolerance) and (y > max3 + deadly_tolerance)
elseif y < min3 then
void_deadly = y < min3 - deadly_tolerance
end
end
return void, void_deadly
@ -35,12 +35,12 @@ end
-- If the Y coordinate is not located in any dimension, it will return:
-- nil, "void"
function mcl_worlds.y_to_layer(y)
if y >= mcl_vars.mg_overworld_min then
return y - mcl_vars.mg_overworld_min, "overworld"
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
return y - mcl_vars.mg_nether_min, "nether"
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
return y - mcl_vars.mg_end_min, "end"
if y >= min1 then
return y - min1, "overworld"
elseif y >= min3 and y <= max3 then
return y - min3, "nether"
elseif y >= min2 and y <= max2 then
return y - min2, "end"
else
return nil, "void"
end
@ -61,25 +61,25 @@ local pos_to_dimension = mcl_worlds.pos_to_dimension
-- MineClone 2.
-- mc_dimension is one of "overworld", "nether", "end" (default: "overworld").
function mcl_worlds.layer_to_y(layer, mc_dimension)
if mc_dimension == "overworld" or mc_dimension == nil then
return layer + mcl_vars.mg_overworld_min
if not mc_dimension or mc_dimension == "overworld" then
return layer + min1
elseif mc_dimension == "nether" then
return layer + mcl_vars.mg_nether_min
return layer + min3
elseif mc_dimension == "end" then
return layer + mcl_vars.mg_end_min
return layer + min2
end
end
-- Takes a position and returns true if this position can have weather
function mcl_worlds.has_weather(pos)
-- Weather in the Overworld and the high part of the void below
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
return pos.y <= max1 and pos.y >= min1 - 64
end
-- Takes a position and returns true if this position can have Nether dust
function mcl_worlds.has_dust(pos)
-- Weather in the Overworld and the high part of the void below
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
return pos.y <= max3 + 138 and pos.y >= min3 - 10
end
-- Takes a position (pos) and returns true if compasses are working here
@ -89,7 +89,7 @@ function mcl_worlds.compass_works(pos)
if dim == "nether" or dim == "end" then
return false
elseif dim == "void" then
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
return pos.y <= max1 and pos.y >= min1 - 64
else
return true
end
@ -152,4 +152,3 @@ minetest.register_globalstep(function(dtime)
dimtimer = 0
end
end)

View File

@ -1,5 +1,4 @@
name = mcl_worlds
author = Wuzzy
description = Utility functions for worlds and the “dimensions”.
depends = mcl_init
depends = mcl_mapgen

View File

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

View File

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

105
mods/HUD/mcl_info/init.lua Normal file
View File

@ -0,0 +1,105 @@
local refresh_interval = .63
local huds = {}
local default_debug = 3
local after = minetest.after
local get_connected_players = minetest.get_connected_players
local get_biome_name = minetest.get_biome_name
local get_biome_data = minetest.get_biome_data
local format = string.format
local min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
local max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max + 128
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local S = minetest.get_translator(modname)
local storage = minetest.get_mod_storage()
local player_dbg = minetest.deserialize(storage:get_string("player_dbg") or "return {}") or {}
local function get_text(pos, bits)
local bits = bits
if bits == 0 then return "" end
local y = pos.y
if y >= min1 then
y = y - min1
elseif y >= min3 and y <= max3 then
y = y - min3
elseif y >= min2 and y <= max2 then
y = y - min2
end
local biome_data = get_biome_data(pos)
local biome_name = biome_data and get_biome_name(biome_data.biome) or "No biome"
local text
if bits == 1 then
text = biome_name
elseif bits == 2 then
text = format("x:%.1f y:%.1f z:%.1f", pos.x, y, pos.z)
elseif bits == 3 then
text = format("%s x:%.1f y:%.1f z:%.1f", biome_name, pos.x, y, pos.z)
end
return text
end
local function info()
for _, player in pairs(get_connected_players()) do
local name = player:get_player_name()
local pos = player:get_pos()
local text = get_text(pos, player_dbg[name] or default_debug)
local hud = huds[name]
if not hud then
local def = {
hud_elem_type = "text",
alignment = {x = 1, y = -1},
scale = {x = 100, y = 100},
position = {x = 0.0073, y = 0.989},
text = text,
style = 5,
["number"] = 0xcccac0,
z_index = 0,
}
local def_bg = table.copy(def)
def_bg.offset = {x = 2, y = 1}
def_bg["number"] = 0
def_bg.z_index = -1
huds[name] = {
player:hud_add(def),
player:hud_add(def_bg),
text,
}
elseif text ~= hud[3] then
hud[3] = text
player:hud_change(huds[name][1], "text", text)
player:hud_change(huds[name][2], "text", text)
end
end
after(refresh_interval, info)
end
minetest.register_on_authplayer(function(name, ip, is_success)
if is_success then
huds[name] = nil
end
end)
minetest.register_chatcommand("debug",{
description = S("Set debug bit mask: 0 = disable, 1 = biome name, 2 = coordinates, 3 = all"),
func = function(name, params)
local dbg = math.floor(tonumber(params) or default_debug)
if dbg < 0 or dbg > 3 then
minetest.chat_send_player(name, S("Error! Possible values are integer numbers from @1 to @2", 0, 3))
return
end
if dbg == default_dbg then
player_dbg[name] = nil
else
player_dbg[name] = dbg
end
minetest.chat_send_player(name, S("Debug bit mask set to @1", dbg))
end
})
minetest.register_on_shutdown(function()
storage:set_string("player_dbg", minetest.serialize(player_dbg))
end)
info()

View File

@ -0,0 +1,4 @@
# textdomain: mcl_info
Set debug bit mask: 0 @= disable, 1 @= biome name, 2 @= coordinates, 3 @= all=Установка отладочной битовой маски: 0 @= отключить, 1 @= биом, 2 @= координаты, 3 @= всё
Error! Possible values are integer numbers from @1 to @2=Ошибка! Допустимые значения - целые числа от @1 до @2
Debug bit mask set to @1=Отладочной битовой маске присвоено значение @1

View File

@ -0,0 +1,4 @@
# textdomain: mcl_info
Set debug bit mask: 0 @= disable, 1 @= biome name, 2 @= coordinates, 3 @= all=
Error! Possible values are integer numbers from @1 to @2=
Debug bit mask set to @1=

View File

@ -0,0 +1,3 @@
name = mcl_info
description = Prints biome name and player position
optional_depends = mcl_mapgen

View File

@ -4,7 +4,8 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
local mg_name = minetest.get_mapgen_setting("mg_name")
local mg_name = mcl_mapgen.name
local v6 = mcl_mapgen.v6
local math = math
local vector = vector
@ -204,8 +205,23 @@ minetest.register_abm({
action = function(pos, node, active_object_count, active_object_count_wider)
for _, object in pairs(minetest.get_objects_inside_radius(pos, 0.9)) do
local entity = object:get_luaentity()
if entity and entity.name == "__builtin:item" then
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 } }
@ -367,7 +383,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
@ -382,7 +398,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)
@ -394,7 +410,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)
@ -772,7 +788,7 @@ function mcl_core.generate_huge_jungle_tree(pos)
end
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
local grass_spread_randomizer = PseudoRandom(mcl_mapgen.seed)
function mcl_core.get_grass_palette_index(pos)
local biome_data = minetest.get_biome_data(pos)

View File

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

View File

@ -3,8 +3,8 @@
local S = minetest.get_translator(minetest.get_current_modname())
local math = math
local table = table
local math_random = math.random
local math_floor = math.floor
--- Plant parts ---
@ -24,7 +24,7 @@ local chorus_flower_box = {
-- Helper function
local function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
return math_floor(num * mult + 0.5) / mult
end
-- This is a list of nodes that SHOULD NOT call their detach function
@ -344,6 +344,7 @@ end
-- Grow a single step of a chorus plant at pos.
-- Pos must be a chorus flower.
function mcl_end.grow_chorus_plant_step(pos, node, pr)
local pr = pr or PseudoRandom()
local new_flower_buds = {}
local above = { x = pos.x, y = pos.y + 1, z = pos.z }
local node_above = minetest.get_node(above)
@ -452,16 +453,20 @@ function mcl_end.grow_chorus_plant_step(pos, node, pr)
return new_flower_buds
end
local fast_randomizer = {
next = function(self, min, max)
return math_random(min, max)
end
}
--- ABM ---
local seed = minetest.get_mapgen_setting("seed")
local pr = PseudoRandom(seed)
minetest.register_abm({
label = "Chorus plant growth",
nodenames = { "mcl_end:chorus_flower" },
interval = 35.0,
chance = 4.0,
action = function(pos, node, active_object_count, active_object_count_wider)
mcl_end.grow_chorus_plant_step(pos, node, pr)
mcl_end.grow_chorus_plant_step(pos, node, fast_randomizer)
end,
})

View File

@ -10,7 +10,7 @@ minetest.register_entity("mcl_end:ender_eye", {
-- Save and restore age
get_staticdata = function(self)
return tostring(self._age)
return tostring(self._age) or "0"
end,
on_activate = function(self, staticdata, dtime_s)
local age = tonumber(staticdata)
@ -87,7 +87,7 @@ minetest.register_craftitem("mcl_end:ender_eye", {
end
local origin = user:get_pos()
origin.y = origin.y + 1.5
local strongholds = mcl_structures.get_registered_structures("stronghold")
local strongholds = mcl_structures.strongholds
local dim = mcl_worlds.pos_to_dimension(origin)
local is_creative = minetest.is_creative_enabled(user:get_player_name())

View File

@ -2,6 +2,10 @@ local S = minetest.get_translator(minetest.get_current_modname())
local table = table
local interval = 35
local chance = 11
local max_interval = interval * chance
minetest.register_node("mcl_nether:nether_wart_0", {
description = S("Premature Nether Wart (Stage 1)"),
_doc_items_longdesc = S("A premature nether wart has just recently been planted on soul sand. Nether wart slowly grows on soul sand in 4 stages (the second and third stages look identical). Although nether wart is home to the Nether, it grows in any dimension."),
@ -22,6 +26,7 @@ minetest.register_node("mcl_nether:nether_wart_0", {
},
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
sounds = mcl_sounds.node_sound_leaves_defaults(),
on_construct = mcl_time.touch,
})
minetest.register_node("mcl_nether:nether_wart_1", {
@ -44,6 +49,7 @@ minetest.register_node("mcl_nether:nether_wart_1", {
},
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
sounds = mcl_sounds.node_sound_leaves_defaults(),
on_construct = mcl_time.touch,
})
minetest.register_node("mcl_nether:nether_wart_2", {
@ -66,6 +72,7 @@ minetest.register_node("mcl_nether:nether_wart_2", {
},
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
sounds = mcl_sounds.node_sound_leaves_defaults(),
on_construct = mcl_time.touch,
})
minetest.register_node("mcl_nether:nether_wart", {
@ -148,36 +155,59 @@ minetest.register_craftitem("mcl_nether:nether_wart_item", {
local names = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"}
minetest.register_abm({
label = "Nether wart growth",
nodenames = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"},
neighbors = {"group:soil_nether_wart"},
interval = 35,
chance = 11,
action = function(pos, node)
pos.y = pos.y-1
if minetest.get_item_group(minetest.get_node(pos).name, "soil_nether_wart") == 0 then
return
end
pos.y = pos.y+1
local function grow(pos, node)
local step = nil
local node = node or minetest.get_node(pos)
for i, name in ipairs(names) do
if name == node.name then
step = i
break
end
end
if step == nil then
return
end
if not step then return end
local new_node = {name = names[step + 1]}
if new_node.name == nil then
if not new_node.name then
new_node.name = "mcl_nether:nether_wart"
end
new_node.param = node.param
new_node.param2 = node.param2
minetest.set_node(pos, new_node)
end
minetest.register_abm({
label = "Nether wart growth",
nodenames = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"},
neighbors = {"group:soil_nether_wart"},
interval = interval,
chance = 1,
action = function(pos, node)
pos.y = pos.y-1
if minetest.get_item_group(minetest.get_node(pos).name, "soil_nether_wart") == 0 then
return
end
pos.y = pos.y+1
for i = 1, mcl_time.get_number_of_times_at_pos(pos, interval, chance) do
grow(pos)
end
end
})
minetest.register_lbm({
label = "Nether wart growth update",
name = "mcl_nether:growth_warts",
nodenames = {"mcl_nether:nether_wart_0", "mcl_nether:nether_wart_1", "mcl_nether:nether_wart_2"},
run_at_every_load = true,
action = function(pos, node)
pos.y = pos.y-1
if minetest.get_item_group(minetest.get_node(pos).name, "soil_nether_wart") == 0 then
return
end
pos.y = pos.y+1
for i = 1, mcl_time.get_number_of_times_at_pos(pos, interval, chance) do
grow(pos)
end
end
})
if minetest.get_modpath("doc") then

View File

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

View File

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

View File

@ -19,7 +19,7 @@ local W_MIN, W_MAX = 4, 23
local H_MIN, H_MAX = 5, 23
local N_MIN, N_MAX = 6, (W_MAX-2) * (H_MAX-2)
local TRAVEL_X, TRAVEL_Y, TRAVEL_Z = 8, 1, 8
local LIM_MIN, LIM_MAX = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
local LIM_MIN, LIM_MAX = mcl_mapgen.EDGE_MIN, mcl_mapgen.EDGE_MAX
local PLAYER_COOLOFF, MOB_COOLOFF = 3, 14 -- for this many seconds they won't teleported again
local TOUCH_CHATTER_TIME = 1 -- prevent multiple teleportation attempts caused by multiple portal touches, for this number of seconds
local CHATTER_US = TOUCH_CHATTER_TIME * 1000000
@ -27,8 +27,8 @@ local DELAY = 3 -- seconds before teleporting in Nether portal in Survival mo
local DISTANCE_MAX = 128
local PORTAL = "mcl_portals:portal"
local OBSIDIAN = "mcl_core:obsidian"
local O_Y_MIN, O_Y_MAX = max(mcl_vars.mg_overworld_min, -31), min(mcl_vars.mg_overworld_max, 2048)
local N_Y_MIN, N_Y_MAX = mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_top_min - H_MIN
local O_Y_MIN, O_Y_MAX = max(mcl_mapgen.overworld.min, -31), min(mcl_mapgen.overworld.max, 2048)
local N_Y_MIN, N_Y_MAX = mcl_mapgen.nether.bedrock_bottom_min, mcl_mapgen.nether.bedrock_top_min - H_MIN
-- Alpha and particles
local node_particles_allowed = minetest.settings:get("mcl_node_particles") or "none"
@ -66,7 +66,7 @@ minetest.register_on_shutdown(function()
storage:set_string("nether_exits_keys", minetest.serialize(keys))
end)
local get_node = mcl_vars.get_node
local get_node = mcl_mapgen.get_far_node
local set_node = minetest.set_node
local registered_nodes = minetest.registered_nodes
local is_protected = minetest.is_protected
@ -137,19 +137,8 @@ local function find_exit(p, dx, dy, dz)
if not p or not p.y or not p.z or not p.x then return end
local dx, dy, dz = dx or DISTANCE_MAX, dy or DISTANCE_MAX, dz or DISTANCE_MAX
if dx < 1 or dy < 1 or dz < 1 then return false end
--y values aren't used
local x = floor(p.x)
--local y = floor(p.y)
local z = floor(p.z)
local x1 = x-dx+1
--local y1 = y-dy+1
local z1 = z-dz+1
local x2 = x+dx-1
--local y2 = y+dy-1
local z2 = z+dz-1
local x, y, z = floor(p.x), floor(p.y), floor(p.z)
local x1, y1, z1, x2, y2, z2 = x-dx+1, y-dy+1, z-dz+1, x+dx-1, y+dy-1, z+dz-1
local k1x, k2x = floor(x1/256), floor(x2/256)
local k1z, k2z = floor(z1/256), floor(z2/256)
@ -170,11 +159,27 @@ local function find_exit(p, dx, dy, dz)
end
end end
if t and abs(t.x-p.x) <= dx and abs(t.y-p.y) <= dy and abs(t.z-p.z) <= dz then
if t and abs(t.x-x) <= dx and abs(t.y-y) <= dy and abs(t.z-z) <= dz then
return t
end
end
-- This functon searches Nether portal nodes whitin distance specified and checks the node
local function find_exit_with_check(p, dx, dy, dz)
while true do
local pos = find_exit(p, dx, dy, dz)
if not pos then
-- not found:
return
end
if (get_node(pos).name == PORTAL) then
return pos
end
-- I don't know the reason why it can happen, but if we're here, let's log it, remove this record and try again:
log("warning", "[mcl_portals] Found faulty exit from Nether portal at " .. pos_to_string(pos) .. " - removed")
remove_exit(pos)
end
end
-- Ping-Pong the coordinate for Fast Travelling, https://git.minetest.land/Wuzzy/MineClone2/issues/795#issuecomment-11058
local function ping_pong(x, m, l1, l2)
@ -365,7 +370,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
@ -375,6 +380,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
@ -447,7 +456,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
@ -461,9 +470,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)
@ -479,7 +488,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
@ -542,7 +551,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
@ -555,8 +564,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
@ -570,8 +579,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)}
@ -697,11 +706,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
@ -763,6 +773,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},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 508 B

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -2,21 +2,19 @@
mcl_dungeons = {}
local mg_name = minetest.get_mapgen_setting("mg_name")
-- Are dungeons disabled?
if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then
if mcl_mapgen.dungeons == false or mcl_mapgen.singlenode == true then
return
end
--lua locals
--minetest
local minetest_find_nodes_in_area = minetest.find_nodes_in_area
local registered_nodes = minetest.registered_nodes
local swap_node = minetest.swap_node
local set_node = minetest.set_node
local dir_to_facedir = minetest.dir_to_facedir
local get_meta = minetest.get_meta
local emerge_area = minetest.emerge_area
--vector
local vector_add = vector.add
@ -31,16 +29,15 @@ local math_min = math.min
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 +109,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,23 +121,26 @@ 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
local fdef = registered_nodes[get_node({x = tx, y = y_floor , z = tz}).name]
local cdef = registered_nodes[get_node({x = tx, y = y_ceiling, z = tz}).name]
if not fdef or not fdef.walkable or not cdef or not cdef.walkable then return false end
end
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
@ -410,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
@ -419,8 +418,7 @@ function mcl_dungeons.spawn_dungeon(p1, _, pr)
if not p1 or not pr or not p1.x or not p1.y or not p1.z then return end
local dim = dungeonsizes[pr:next(1, #dungeonsizes)]
local p2 = {x = p1.x+dim.x+1, y = p1.y+dim.y+1, z = p1.z+dim.z+1}
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr, dontcheck=true})
spawn_dungeon(p1, p2, dim, pr, true)
end
mcl_mapgen_core.register_generator("dungeons", nil, dungeons_nodes, 999999)
mcl_mapgen.register_mapgen(dungeons_nodes, mcl_mapgen.order.DUNGEONS)

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,104 @@
-- Check: v7 apple 2320,4,-12558
local chance_per_chunk = 5
local noise_multiplier = 1
local random_offset = 12342
local struct_threshold = chance_per_chunk
local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level
local mcl_mapgen_get_far_node = mcl_mapgen.get_far_node
local 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 pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_chunk)
local noise = mcl_structures_get_perlin_noise_level(minp) * noise_multiplier
if not noise or (random_number + noise) < struct_threshold then return end
local x, z = minp.x, minp.z
-- scan the ocean - it should be the ocean:
for i = 1, pr:next(10, 100) do
local pos = {x = pr:next(15, 64) + x, y = pr:next(0, 25) - 25, z = pr:next(15, 64) + z}
local node_name = mcl_mapgen_get_far_node(pos).name
if node_name ~= water then return end
end
-- scan nodes above water level - there should be the air:
for i = 1, pr:next(10, 100) do
local pos = {x = pr:next(0, 79) + x, y = 2, z = pr:next(0,79) + z}
local node_name = mcl_mapgen_get_far_node(pos).name
if node_name ~= air then return end
end
-- scan ocean surface - allow only water and ice:
for i = 1, pr:next(10,100) do
local pos = {x=pr:next(0, 79)+x, y=1, z=pr:next(0,79)+z}
local node_name = mcl_mapgen_get_far_node(pos).name
if node_name ~= water and node_name ~= ice then return end
end
-- random rotation:
local rotation = pr:next(0, 3)
local rotation_str = tostring(rotation * 90)
minetest_place_schematic(minp, path, rotation_str, nil, true)
-- search prismarine legs at base level and continue them up to the bottom:
for x = x, maxp.x do
for z = z, maxp.z do
local pos = {x = x, y = y, z = z}
local node_name = mcl_mapgen_get_far_node(pos).name
if leg_search_quick_index[node_name] then
local node_leg = {name = node_name}
for y = y - 1, y_bottom, -1 do
pos.y = y
local next_name = mcl_mapgen_get_far_node(pos).name
if not leg_replace_quick_index[next_name] then
break
end
minetest_swap_node(pos, node_leg)
end
end
end
end
minetest_log("action", "[mcl_ocean_monument] Placed at " .. minetest_pos_to_string(minp) .. ", " .. rotation_str .. " deg.")
end, mcl_mapgen.order.OCEAN_MONUMENT)

View File

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

View File

@ -1,106 +1 @@
-- 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)
-- moved into mcl_structures

View File

@ -1,4 +1,3 @@
name = mcl_strongholds
author = Wuzzy
description = Generates strongholds with end portals in the Overworld
depends = mcl_init, mcl_structures, mcl_mapgen_core
description = Mod has been moved into mcl_structures. This is a dummy thing to overwrite the old thing, kay27 01/25/22

View File

@ -0,0 +1,176 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
-- Check: v7 apple -21539,27,2404
local chance_per_chunk = 32
local noise_multiplier = 0.9
local random_offset = 999
local scanning_ratio = 0.0003
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 = 1,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes = not mcl_mapgen.v6 and {
"ColdTaiga_beach",
"ColdTaiga_beach_water",
"Desert",
"Desert_ocean",
"ExtremeHills_beach",
"FlowerForest_beach",
"Forest_beach",
"MesaBryce_sandlevel",
"MesaPlateauF_sandlevel",
"MesaPlateauFM_sandlevel",
"Savanna",
"Savanna_beach",
"StoneBeach",
"StoneBeach_ocean",
"Taiga_beach",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
local pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_chunk)
local noise = mcl_structures_get_perlin_noise_level(minp) * noise_multiplier
if (random_number + noise) < struct_threshold then return end
local pos = pos_list[1]
if #pos_list > 1 then
local count = get_place_rank(pos)
for i = 2, #pos_list do
local pos_i = pos_list[i]
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
end
place(pos, nil, pr)
end,
place_function = place,
})

View File

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

View File

@ -0,0 +1,81 @@
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 dragon_spawn_pos = false
local dragon_spawned, portal_generated = false, false
local function spawn_ender_dragon()
local obj = minetest.add_entity(dragon_spawn_pos, "mobs_mc:enderdragon")
if not obj then return false end
local dragon_entity = obj:get_luaentity()
dragon_entity._initial = true
dragon_entity._portal_pos = p0
return obj
end
local function try_to_spawn_ender_dragon()
if spawn_ender_dragon() then
dragon_spawned = true
return
end
minetest.after(2, try_to_spawn_ender_dragon)
minetest.log("warning", "Ender dragon doesn't want to spawn at "..minetest.pos_to_string(dragon_spawn_pos))
end
if portal_generated and not dragon_spawned then
minetest.after(10, try_to_spawn_ender_dragon)
end
local function place(pos, rotation, pr)
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
dragon_spawn_pos = vector.add(p0, vector.new(3, 11, 3))
portal_generated = true
try_to_spawn_ender_dragon()
local p = table.copy(p0)
for y = y2, y1, -1 do
p.y = y
if minetest.get_node(p).name == "mcl_end:end_stone" then
place(p, "0", PseudoRandom(vm_context.chunkseed))
return
end
end
for y = y2, y1, -1 do
p.y = y
if minetest.get_node(p).name ~= "air" then
place(p, "0", PseudoRandom(vm_context.chunkseed))
return
end
end
place(p0, "0", PseudoRandom(vm_context.chunkseed))
end)
mcl_structures.register_structure({name = "end_exit_portal", place_function = place})

View File

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

View File

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

View File

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

View File

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

View File

@ -2,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 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 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
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 pos = param.pos
local rotation = param.rotation
minetest.place_schematic(pos, param.schematic, rotation, param.replacements, param.force_placement, param.flags)
local on_placed = param.on_placed
if not on_placed then
return
end
on_placed(pos, rotation, param.pr, param.size)
end
function mcl_structures.place_schematic(def)
local pos = def.pos
local schematic = def.schematic
local rotation = def.rotation
local pr = def.pr
local on_placed = def.on_placed -- on_placed(pos, rotation, pr, size)
local emerge = def.emerge
if not pos then
minetest.log('warning', '[mcl_structures] No pos. specified to place schematic')
return
end
if not schematic then
minetest.log('warning', '[mcl_structures] No schematic specified to place at ' .. minetest.pos_to_string(pos))
return
end
if not rotation or rotation == 'random' then
if pr then
rotation = rotations[pr:next(1,#rotations)]
else
rotation = rotations[math.random(1,#rotations)]
end
end
function mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
local s = loadstring(minetest.serialize_schematic(schematic, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")()
if s and s.size then
local x, z = s.size.x, s.size.z
if rotation then
if rotation == "random" and pr then
rotation = rotations[pr:next(1,#rotations)]
if not emerge and not on_placed then
minetest.place_schematic(pos, schematic, rotation, def.replacements, def.force_placement, def.flags)
return
end
if rotation == "random" then
x = math.max(x, z)
z = x
elseif rotation == "90" or rotation == "270" then
x, z = z, x
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 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)
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,40 @@ 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
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
if 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 and def.walkable and def.liquidtype == "none" and 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)
@ -280,53 +383,9 @@ function mcl_structures.generate_boulder(pos, rotation, pr)
return minetest.place_schematic(newpos, path, rotation) -- don't serialize schematics for registered biome decorations, for MT 5.4.0, https://github.com/minetest/minetest/issues/10995
end
local function hut_placement_callback(p1, p2, size, orientation, pr)
if not p1 or not p2 then return end
local legs = minetest.find_nodes_in_area(p1, p2, "mcl_core:tree")
for i = 1, #legs do
while minetest.get_item_group(mcl_vars.get_node({x=legs[i].x, y=legs[i].y-1, z=legs[i].z}, true, 333333).name, "water") ~= 0 do
legs[i].y = legs[i].y - 1
minetest.swap_node(legs[i], {name = "mcl_core:tree", param2 = 2})
end
end
end
function mcl_structures.generate_witch_hut(pos, rotation, pr)
local path = modpath.."/schematics/mcl_structures_witch_hut.mts"
mcl_structures.place_schematic(pos, path, rotation, nil, true, nil, hut_placement_callback, pr)
end
function mcl_structures.generate_ice_spike_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 +398,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")
local chunk_square = mcl_mapgen.CS_NODES * mcl_mapgen.CS_NODES
local block_square = mcl_mapgen.BS * mcl_mapgen.BS
function mcl_structures.from_16x16_to_chunk_inverted_chance(x)
return math.floor(x * 256 / chunk_square + 0.5)
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
function mcl_structures.from_16x16_to_block_inverted_chance(x)
return math.floor(x * 256 / block_square + 0.5)
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
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)
end
local function temple_placement_callback(p1, p2, size, rotation, pr)
-- Delete cacti leftovers:
local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus")
if cactus_nodes and #cactus_nodes > 0 then
for _, pos in pairs(cactus_nodes) do
local node_below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
if node_below and node_below.name == "mcl_core:sandstone" then
minetest.swap_node(pos, {name="air"})
end
end
end
-- Find chests.
-- FIXME: Searching this large area just for the chets is not efficient. Need a better way to find the chests;
-- probably let's just infer it from newpos because the schematic always the same.
local chests = minetest.find_nodes_in_area(p1, p2, "mcl_chests:chest")
-- Add desert temple loot into chests
for c=1, #chests do
local lootitems = mcl_loot.get_multi_loot({
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 25, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 25, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_mobitems:spider_eye", weight = 25, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_books:book", weight = 20, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr)
end },
{ itemstring = "mcl_mobitems:saddle", weight = 20, },
{ itemstring = "mcl_core:apple_gold", weight = 20, },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:emerald", weight = 15, amount_min = 1, amount_max = 3 },
{ itemstring = "", weight = 15, },
{ itemstring = "mobs_mc:iron_horse_armor", weight = 15, },
{ itemstring = "mobs_mc:gold_horse_armor", weight = 10, },
{ itemstring = "mobs_mc:diamond_horse_armor", weight = 5, },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:gunpowder", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_core:sand", weight = 10, amount_min = 1, amount_max = 8 },
{ itemstring = "mcl_mobitems:string", weight = 10, amount_min = 1, amount_max = 8 },
}
}}, pr)
init_node_construct(chests[c])
local meta = minetest.get_meta(chests[c])
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
-- Initialize pressure plates and randomly remove up to 5 plates
local pplates = minetest.find_nodes_in_area(p1, p2, "mesecons_pressureplates:pressure_plate_stone_off")
local pplates_remove = 5
for p=1, #pplates do
if pplates_remove > 0 and pr:next(1, 100) >= 50 then
-- Remove plate
minetest.remove_node(pplates[p])
pplates_remove = pplates_remove - 1
else
-- Initialize plate
minetest.registered_nodes["mesecons_pressureplates:pressure_plate_stone_off"].on_construct(pplates[p])
end
end
end
function mcl_structures.generate_desert_temple(pos, rotation, pr)
-- No Generating for the temple ... Why using it ? No Change
local path = modpath.."/schematics/mcl_structures_desert_temple.mts"
local newpos = {x=pos.x,y=pos.y-12,z=pos.z}
--local size = {x=22, y=24, z=22}
if newpos == nil then
return
end
mcl_structures.place_schematic(newpos, path, rotation or "random", nil, true, nil, temple_placement_callback, pr)
end
local registered_structures = {}
--[[ Returns a table of structure of the specified type.
Currently the only valid parameter is "stronghold".
Format of return value:
{
{ pos = <position>, generated=<true/false> }, -- first structure
{ pos = <position>, generated=<true/false> }, -- second structure
-- and so on
}
TODO: Implement this function for all other structure types as well.
]]
function mcl_structures.get_registered_structures(structure_type)
if registered_structures[structure_type] then
return table.copy(registered_structures[structure_type])
else
return {}
end
end
-- Register a structures table for the given type. The table format is the same as for
-- mcl_structures.get_registered_structures.
function mcl_structures.register_structures(structure_type, structures)
registered_structures[structure_type] = structures
end
local function dir_to_rotation(dir)
local ax, az = math.abs(dir.x), math.abs(dir.z)
if ax > az then
if dir.x < 0 then
return "270"
end
return "90"
end
if dir.z < 0 then
return "180"
end
return "0"
end
-- Debug command
minetest.register_chatcommand("spawnstruct", {
params = "desert_temple | desert_well | igloo | witch_hut | boulder | ice_spike_small | ice_spike_large | fossil | end_exit_portal | end_exit_portal_open | end_gateway_portal | end_portal_shrine | nether_portal | dungeon",
description = S("Generate a pre-defined structure near your position."),
privs = {debug = true},
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then return end
local pos = player:get_pos()
if not pos then return end
pos = vector.round(pos)
local dir = minetest.yaw_to_dir(player:get_look_horizontal())
local rot = dir_to_rotation(dir)
local pr = PseudoRandom(pos.x+pos.y+pos.z)
local errord = false
local message = S("Structure placed.")
if param == "desert_temple" then
mcl_structures.generate_desert_temple(pos, rot, pr)
elseif param == "desert_well" then
mcl_structures.generate_desert_well(pos, rot)
elseif param == "igloo" then
mcl_structures.generate_igloo(pos, rot, pr)
elseif param == "witch_hut" then
mcl_structures.generate_witch_hut(pos, rot, pr)
elseif param == "boulder" then
mcl_structures.generate_boulder(pos, rot, pr)
elseif param == "fossil" then
mcl_structures.generate_fossil(pos, rot, pr)
elseif param == "ice_spike_small" then
mcl_structures.generate_ice_spike_small(pos, rot, pr)
elseif param == "ice_spike_large" then
mcl_structures.generate_ice_spike_large(pos, rot, pr)
elseif param == "end_exit_portal" then
mcl_structures.generate_end_exit_portal(pos, rot, pr)
elseif param == "end_exit_portal_open" then
mcl_structures.generate_end_exit_portal_open(pos, rot, pr)
elseif param == "end_gateway_portal" then
mcl_structures.generate_end_gateway_portal(pos, rot, pr)
elseif param == "end_portal_shrine" then
mcl_structures.generate_end_portal_shrine(pos, rot, pr)
elseif param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then
mcl_dungeons.spawn_dungeon(pos, rot, pr)
elseif param == "nether_portal" and mcl_portals and mcl_portals.spawn_nether_portal then
mcl_portals.spawn_nether_portal(pos, rot, pr, name)
elseif param == "" then
message = S("Error: No structure type given. Please use “/spawnstruct <type>”.")
errord = true
else
message = S("Error: Unknown structure type. Please use “/spawnstruct <type>”.")
errord = true
end
minetest.chat_send_player(name, message)
if errord then
minetest.chat_send_player(name, S("Use /help spawnstruct to see a list of avaiable types."))
end
end
})
dofile(modpath .. "/structures.lua")

View File

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

View File

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

View File

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

View File

@ -0,0 +1,69 @@
local step = 1
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)
if minetest.settings:get_bool("mcl_debug_struct_noise", false) then
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
end
if minetest.settings:get_bool("mcl_debug_chunk_borders", false) 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
for z0 = minp.z, maxp.z, step do
for x0 = minp.x, maxp.x, step do
minetest.set_node({x=x0, y=maxp.y, z=z0}, {name = "mcl_core:glass"})
end
end
if not minetest.settings:get_bool("mcl_debug_struct_noise", false) then
end
end
end, 999999999999)

View File

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

View File

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

View File

@ -0,0 +1,140 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
-- Check: v7 apple 30584,5,30356
-- Check: v7 apple 2637,6,-12031
-- Check: v7 apple 2644,6,-17968
local chance_per_chunk = 34
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:river_water_source" 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:river_water_source", "mcl_core:water_source", "group:frosted_ice"},
num_spawn_by = 1,
-- flags = "all_floors",
fill_ratio = scanning_ratio,
y_min = mcl_mapgen.overworld.min,
y_max = mcl_mapgen.overworld.max,
height = 1,
biomes = mcl_mapgen.v6 and {
"Normal",
} or {
"Swampland",
"Swampland_shore",
"Swampland_ocean",
"Swampland_deep_ocean",
},
},
on_finished_chunk = function(minp, maxp, seed, vm_context, pos_list)
local pr = PseudoRandom(seed + random_offset)
local random_number = pr:next(1, chance_per_chunk)
local noise = mcl_structures_get_perlin_noise_level(minp) * noise_multiplier
if (random_number + noise) < struct_threshold then return end
local pos = tune_pos(pos_list[1])
if #pos_list > 1 then
local count = get_place_rank(pos)
for i = 2, #pos_list do
local pos_i = pos_list[i]
local count_i = get_place_rank(pos_i)
if count_i > count then
count = count_i
pos = pos_i
end
end
end
place(pos, nil, pr)
end,
place_function = place,
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -160,3 +160,5 @@ enable_real_maps (Enable Real Maps) bool true
[Debugging]
# If enabled, this will show the itemstring of an item in the description.
mcl_item_id_debug (Item ID Debug) bool false
mcl_debug_struct_noise (Show Structures Perlin Noise) bool false
mcl_debug_chunk_borders (Show Chunk Borders) bool false