Compare commits

...

58 Commits

Author SHA1 Message Date
kno10 787457595f do not use voxelmanip anymore, seems to be smoother without 2024-10-29 21:39:40 +01:00
kno10 6ab0b6bdaa vl_terraforming: add non-vm version 2024-10-29 19:41:33 +01:00
kno10 9b6ea60694 Add cherry grove biome, based on MCLA.
Textures from pixel perfection, optimized with zopfipng. C.f., voxelibre#3463 voxelibre#4113
2024-10-29 19:28:43 +01:00
kno10 c726b327be Register only one decoration per grass color for tallgrass. 2024-10-29 19:28:43 +01:00
kno10 e94b718c95 Unnecessary use of mcl_util.get_node 2024-10-29 19:28:43 +01:00
kno10 7a8b632057 Drop non-persistent tracking of generated chunks. 2024-10-29 19:28:43 +01:00
kno10 38c333981f polishing 2024-10-29 19:28:43 +01:00
kno10 3799f61626 use mcl_util function 2024-10-29 19:28:43 +01:00
kno10 54d1c4f774 template specialization for param2 2024-10-29 19:28:43 +01:00
kno10 c2a1833aeb Split biomes into separate files. 2024-10-29 19:28:43 +01:00
kno10 b2f7c04c85 igloo less slope, more snow, more rare 2024-10-29 19:28:43 +01:00
kno10 30eab42f45 height difference for villages 2024-10-29 19:28:43 +01:00
kno10 182bb9c0d6 better paths, better village layouts 2024-10-29 19:28:43 +01:00
kno10 34a8ebf023 add color tools for minetestmapper 2024-10-29 19:28:43 +01:00
kno10 62bb73539b improve shipwrecks
reduce frequency, trim schematics, disable foundations again
2024-10-29 19:28:43 +01:00
kno10 c6598e1b09 more structures, more to discover. 2024-10-29 19:28:43 +01:00
kno10 ba90d8cb31 hollow fallen trees 2024-10-29 19:28:43 +01:00
kno10 ea2d04609c add command /emerge 512 to emerge an area 2024-10-29 19:28:43 +01:00
kno10 3436e6a545 improvments to mineshaft cart variety + /locate 2024-10-29 19:28:43 +01:00
kno10 9ba0337a36 use swap_node instead of set_node in mapgen, tweaks to end 2024-10-29 19:28:43 +01:00
kno10 faf2408681 eliminate the static_pos kludge except for strongholds for now 2024-10-29 19:28:43 +01:00
kno10 badf041d8c cleanups and fixes 2024-10-29 19:28:43 +01:00
kno10 eaaa2a4dd2 clean up villages code, add biome farming support 2024-10-29 19:28:43 +01:00
kno10 92cffe42b4 More fixes, make shulkers spawn on the floor. 2024-10-29 19:28:43 +01:00
kno10 e2a0afe451 fix, and add small end city from MCLA 2024-10-29 19:28:43 +01:00
kno10 4430d1299e further fixes and improvements. 2024-10-29 19:28:43 +01:00
kno10 99e3aeea4e disable foliage 'fixes' that cause multi-colored trees 2024-10-29 19:28:43 +01:00
kno10 0a8bf42b03 code restructuring and cleanups 2024-10-29 19:28:42 +01:00
kno10 49a90a0dc4 Huge update of new terraforming, structures, and village code 2024-10-29 19:28:42 +01:00
kno10 acb5aef76b Big rewrite of structure spawning using voxel manipulators 2024-10-29 19:28:42 +01:00
kno10 0e98c651f4 fix bamboo biomes, add new schematic 2024-10-29 19:28:42 +01:00
kno10 cfa2780554 allow generating towns on mapblocks emerged with previous versions that did not become full settlements yet 2024-10-29 19:28:42 +01:00
kno10 7eb970e21c fixes and parameter tuning 2024-10-29 19:28:42 +01:00
kno10 347b8b954f more voxelmanipulator, more MCLA 2024-10-29 19:28:42 +01:00
kno10 f9b07adbc2 finetuning 2024-10-29 19:28:42 +01:00
kno10 08b14ecb13 add MCLA schematics 2024-10-29 19:28:42 +01:00
kno10 c1125c7a48 Big villages overhaul 2024-10-29 19:28:42 +01:00
kno10 27802fe2df move structures code from mcl_mapgen_core to mcl_structures 2024-10-29 19:28:42 +01:00
kno10 2a4fe39d5c prioritize map decorations to make the generations more deterministic 2024-10-29 19:28:42 +01:00
kno10 9a305b56a8 Make structure generation more deterministic
Pseudorandom now depends on position hash, not on blockseed + structure table order.
This should make structure generation more deterministic.
2024-10-29 19:28:42 +01:00
kno10 5fe4822f30 Some optimizations for mapgen 2024-10-29 19:28:42 +01:00
kno10 1d00b4cd7d Bigger cleanup of mcl_core/functions 2024-10-27 17:11:13 +01:00
Mikita Wiśniewski 41b188caea Remove "double drop" mechanics for bamboo (fixes #4514) (#4642)
Reviewed-on: VoxeLibre/VoxeLibre#4642
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-committed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
2024-10-27 14:16:06 +01:00
kno10 ae7995d195 Fix axolotl attacking water mobs (#4675)
Also avoid jumping out of the water closes #4644

Reviewed-on: VoxeLibre/VoxeLibre#4675
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: kno10 <kno10@noreply.git.minetest.land>
Co-committed-by: kno10 <kno10@noreply.git.minetest.land>
2024-10-27 14:10:11 +01:00
kno10 e293cbe631 Better handling of touching_ground for bouncing on beds (#4689)
Reviewed-on: VoxeLibre/VoxeLibre#4689
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: kno10 <erich.schubert@gmail.com>
Co-committed-by: kno10 <erich.schubert@gmail.com>
2024-10-27 14:03:50 +01:00
the-real-herowl fd6cac5f0c Merge pull request 'Fix fog tint in overworld, apply memory leak fix, fix rain->clear clouds' (#4669) from weather-fixes into master
Reviewed-on: VoxeLibre/VoxeLibre#4669
Reviewed-by: kno10 <kno10@noreply.git.minetest.land>
2024-10-11 07:14:01 +02:00
teknomunk e864cc19ed Make fog_tint_type = "default" when weather is present to match behavior at 0.87.2 2024-10-09 01:05:20 +02:00
teknomunk 66c3c014a1 Make sure fog tints are preserved during weather is present 2024-10-09 01:05:20 +02:00
teknomunk 7807093b50 Another correction to color interpolation, change day color from layer position 0.15 to 0.50 2024-10-09 01:05:20 +02:00
teknomunk f6c3f4bd16 Correct value clamping 2024-10-09 01:05:20 +02:00
teknomunk 96a03b1923 Remove posibility of nil sky colors in overworld, add line break 2024-10-09 01:05:20 +02:00
teknomunk 2145470f63 Fix clouds during rain->clear weather transition 2024-10-09 01:05:20 +02:00
teknomunk 2ca0ccd8fe Fix fog tint in overworld, apply memory leak fix from rain.lua to snow.lua and thunder.lua 2024-10-09 01:05:20 +02:00
teknomunk 614518c6cd Revert minetest.add_entity() -> mcl_mobs.spawn() from #4445 (#4679)
Reviewed-on: VoxeLibre/VoxeLibre#4679
Reviewed-by: kno10 <kno10@noreply.git.minetest.land>
Co-authored-by: teknomunk <teknomunk@protonmail.com>
Co-committed-by: teknomunk <teknomunk@protonmail.com>
2024-10-08 15:34:30 +02:00
kno10 253a06fa08 Fix mob egg double-spawns (#4657)
If you spawn a mob clicking on a wall, two mobs will be spawned.

To reproduce: face a stack of stones, with a spawn egg click on the side of a stone. It does not happen when you click the top of a node, because spawning below fails and only the second one succeeds.

Reviewed-on: VoxeLibre/VoxeLibre#4657
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: kno10 <kno10@noreply.git.minetest.land>
Co-committed-by: kno10 <kno10@noreply.git.minetest.land>
2024-09-30 19:21:40 +02:00
kno10 dcfd31d17a Avoid random jumps when standing due to gravity (fewer villagers on the roofs) (#4547)
Reviewed-on: VoxeLibre/VoxeLibre#4547
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: kno10 <erich.schubert@gmail.com>
Co-committed-by: kno10 <erich.schubert@gmail.com>
2024-09-30 11:22:31 +02:00
teknomunk c34aecfcab Don't make 'ignore' nodes break bamboo or kelp (#4551)
This modifies the behavior of kelp and bamboo so that neither breaks when an unloaded node is encountered.

Reviewed-on: VoxeLibre/VoxeLibre#4551
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: teknomunk <teknomunk@protonmail.com>
Co-committed-by: teknomunk <teknomunk@protonmail.com>
2024-09-29 13:57:52 +02:00
Mikita Wiśniewski 9cb4f51468 Fix invalid global call in mcl_chests LBM (#4667)
Reviewed-on: VoxeLibre/VoxeLibre#4667
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-committed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
2024-09-29 13:34:20 +02:00
270 changed files with 14750 additions and 11071 deletions

View File

@ -195,48 +195,6 @@ 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
---@param pos Vector
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
---@param pos Vector
---@return boolean
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.
---@param pos Vector Position, if it's wrong, `{name="error"}` node will return.
---@param force? boolean Optional (default: `false`), Do the maximum to still read the node within us_timeout.
@ -253,32 +211,23 @@ function mcl_vars.get_node(pos, force, us_timeout)
return node
end
-- copy vector to get sure it won't changed by other threads
local pos_copy = vector.copy(pos)
-- try LVM
minetest.get_voxel_manip():read_from_map(pos_copy, pos_copy)
node = minetest.get_node(pos_copy)
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)
if mcl_vars.is_generated(pos_copy) then
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
minetest.forceload_block(pos_copy)
else
minetest.emerge_area(pos_copy, pos_copy)
end
-- try async emerge + BUSY wait (a really BAD idea, you should rather accept failure)
minetest.emerge_area(pos, pos) -- runs async!
local t = minetest.get_us_time()
node = minetest.get_node(pos_copy)
node = minetest.get_node(pos)
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < (us_timeout or 244)) do
node = minetest.get_node(pos_copy)
node = minetest.get_node(pos)
end
return node
-- it still can return "ignore", LOL, even if force = true, but only after time out
end

View File

@ -1131,3 +1131,16 @@ if not vector.in_area then
(pos.z >= min.z) and (pos.z <= max.z)
end
end
if not minetest.bulk_swap_node then -- maybe in 5.10 https://github.com/minetest/minetest/pull/15043
minetest.bulk_swap_node = function(pos_list, node)
-- for dense and large sets, we could also try a VManip, but often this enough for now
local swap_node = minetest.swap_node
for _, pos in ipairs(pos_list) do
swap_node(pos, node)
end
end
-- async emerge environment, untested:
if minetest.set_node == minetest.swap_node then minetest.bulk_swap_node = minetest.bulk_set_node end
end

View File

@ -388,7 +388,7 @@ end
local function on_step_work (self, dtime)
local function on_step_work(self, dtime, moveresult)
local pos = self.object:get_pos()
if not pos then return end
@ -402,7 +402,7 @@ local function on_step_work (self, dtime)
-- Do we abandon out of here now?
end
if self:falling(pos) then return end
if self:falling(pos, moveresult) then return end
if self:step_damage (dtime, pos) then return end
if self.state == "die" then return end
@ -502,11 +502,11 @@ end
-- main mob function
function mob_class:on_step(dtime)
function mob_class:on_step(dtime, moveresult)
if not DEVELOPMENT then
-- Removed as bundled Lua (5.1 doesn't support xpcall)
--local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime)
local status, retVal = pcall(on_step_work, self, dtime)
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
if status then
return retVal
else
@ -521,7 +521,7 @@ function mob_class:on_step(dtime)
log_error (dump(retVal), dump(pos), dump(self))
end
else
return on_step_work (self, dtime)
return on_step_work (self, dtime, moveresult)
end
end

View File

@ -105,7 +105,7 @@ end
-- Spawn a child
function mcl_mobs.spawn_child(pos, mob_type)
local child = mcl_mobs.spawn(pos, mob_type)
local child = minetest.add_entity(pos, mob_type)
if not child then
return
end

View File

@ -615,7 +615,7 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
pos.y = pos.y - 1
local mob = mcl_mobs.spawn(pos, mob_name)
if not object then
if not mob then
pos.y = pos.y + 1
mob = mcl_mobs.spawn(pos, mob_name)
if not mob then return end

View File

@ -927,8 +927,7 @@ end
-- falling and fall damage
-- returns true if mob died
function mob_class:falling(pos)
function mob_class:falling(pos, moveresult)
if self.fly and self.state ~= "die" then
return
end
@ -951,7 +950,13 @@ function mob_class:falling(pos)
new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0)
elseif v.y <= 0 and v.y > self.fall_speed then
-- fall downwards at set speed
new_acceleration = vector.new(0, self.fall_speed, 0)
if moveresult and moveresult.touching_ground then
-- when touching ground, retain a minimal gravity to keep the touching_ground flag
-- but also to not get upwards acceleration with large dtime when on bouncy ground
new_acceleration = vector.new(0, self.fall_speed * 0.01, 0)
else
new_acceleration = vector.new(0, self.fall_speed, 0)
end
else
-- stop accelerating once max fall speed hit
new_acceleration =vector.zero()

View File

@ -72,18 +72,24 @@ local axolotl = {
fly = true,
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
breathes_in_water = true,
jump = true,
jump = false, -- would get them out of the water too often
damage = 2,
reach = 2,
attack_type = "dogfight",
attack_animals = true,
specific_attack = {
"extra_mobs_cod",
"extra_mobs_glow_squid",
"extra_mobs_salmon",
"extra_mobs_tropical_fish",
"mobs_mc_squid"
},
"mobs_mc:cod",
"mobs_mc:glow_squid",
"mobs_mc:salmon",
"mobs_mc:tropical_fish",
"mobs_mc:squid",
"mobs_mc:zombie", -- todo: only drowned?
"mobs_mc:baby_zombie",
"mobs_mc:husk",
"mobs_mc:baby_husk",
"mobs_mc:guardian_elder",
"mobs_mc:guardian",
},
runaway = true,
}

View File

@ -128,7 +128,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
on_die = function(self, pos, cmi_cause)
if self._portal_pos then
mcl_portals.spawn_gateway_portal()
mcl_structures.place_structure(self._portal_pos,mcl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
mcl_structures.spawn_end_exit_portal(self._portal_pos)
if self._initial then
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000
minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"})

View File

@ -114,10 +114,9 @@ mcl_mobs.register_mob("mobs_mc:shulker", {
for n=1, math.min(64, #nodes) do
local r = pr:next(1, #nodes)
local nodepos = nodes[r]
local tg = vector.offset(nodepos,0,1,0)
local tg = vector.offset(nodepos,0,0.5,0)
if check_spot(tg) then
telepos = tg
node_ok = true
end
end
if telepos then

View File

@ -1244,7 +1244,7 @@ local function retrieve_my_jobsite (self)
mcl_log("find_jobsite. Invalid params. Should not happen")
return
end
local n = mcl_vars.get_node(self._jobsite)
local n = minetest.get_node(self._jobsite)
local m = minetest.get_meta(self._jobsite)
if m:get_string("villager") == self._id then
--mcl_log("find_jobsite. is my job.")
@ -1411,7 +1411,7 @@ local function validate_bed(self)
if not self or not self._bed then
return false
end
local n = mcl_vars.get_node(self._bed)
local n = minetest.get_node(self._bed)
if not n then
self._bed = nil
return false
@ -2328,6 +2328,16 @@ mcl_mobs.register_mob("mobs_mc:villager", {
end,
})
-- HACK: for compatibility with the new mcl_villages code, but will not allow easy modding yet
mobs_mc.jobsites = {}
for _,p in pairs(professions) do
if p.jobsite then
table.insert(mobs_mc.jobsites, p.jobsite)
end
end
function villager_employ(v, jobsite_pos)
if jobsite_pos then employ(v, jobsite_pos) end
end
--[[
Villager spawning in mcl_villages

View File

@ -157,6 +157,7 @@ function mcl_weather.rain.clear()
mcl_weather.rain.remove_sound(player)
mcl_weather.rain.remove_player(player)
mcl_weather.remove_spawners_player(player)
player:set_clouds({color="#FFF0EF"})
end
end

View File

@ -2,6 +2,7 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local NIGHT_VISION_RATIO = 0.45
local DEBUG = false
-- Settings
local minimum_update_interval = { 250e3 }
@ -190,8 +191,8 @@ end
function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
-- Clamp current_val to valid range
current_val = math.min(minval, current_val)
current_val = math.max(maxval, current_val)
current_val = math.max(minval, current_val)
current_val = math.min(maxval, current_val)
-- Rescale current_val from a number between minval and maxval to a number between 1 and #colors
local scaled_value = (current_val - minval) / (maxval - minval) * (#colors - 1) + 1.0
@ -199,7 +200,7 @@ function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
-- Get the first color's values
local index1 = math.floor(scaled_value)
local color1 = colors[index1]
local frac1 = scaled_value - index1
local frac1 = 1.0 - (scaled_value - index1)
-- Get the second color's values
local index2 = math.min(index1 + 1, #colors) -- clamp to maximum color index (will occur if index1 == #colors)
@ -207,11 +208,32 @@ function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
local color2 = colors[index2]
-- Interpolate between color1 and color2
return {
local res = {
r = math.floor(frac1 * color1.r + frac2 * color2.r),
g = math.floor(frac1 * color1.g + frac2 * color2.g),
b = math.floor(frac1 * color1.b + frac2 * color2.b),
}
if DEBUG then
minetest.log(dump({
minval = minval,
maxval = maxval,
current_val = current_val,
colors = colors,
res = res,
scaled_value = scaled_value,
frac1 = frac1,
index1 = index1,
color1 = color1,
frac2 = frac2,
index2 = index2,
color2 = color2,
}))
end
return res
end
-- Simple getter. Either returns user given players list or get all connected players if none provided

View File

@ -40,18 +40,21 @@ function dimension_handlers.overworld(player, sky_data)
end
-- Use overworld defaults
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.15)
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27)
local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1)
sky_data.sky = {
type = "regular",
sky_color = {
day_sky = day_color,
day_horizon = day_color,
dawn_sky = dawn_color,
dawn_horizon = dawn_color,
night_sky = night_color,
night_horizon = night_color,
day_sky = day_color or "#7BA4FF",
day_horizon = day_color or "#C0D8FF",
dawn_sky = dawn_color or "7BA4FF",
dawn_horizon = dawn_color or "#C0D8FF",
night_sky = night_color or "000000",
night_horizon = night_color or "4A6790",
fog_sun_tint = "#ff5f33",
fog_moon_tint = nil,
fog_tint_type = "custom",
},
clouds = true,
}
@ -75,18 +78,15 @@ function dimension_handlers.overworld(player, sky_data)
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75)
local night_color = mcl_weather.skycolor.get_sky_layer_color(0)
sky_data.sky = {
type = "regular",
sky_color = {
day_sky = day_color,
day_horizon = day_color,
dawn_sky = dawn_color,
dawn_horizon = dawn_color,
night_sky = night_color,
night_horizon = night_color,
},
clouds = true,
}
table.update(sky_data.sky.sky_color,{
day_sky = day_color or "#7BA4FF",
day_horizon = day_color or "#C0D8FF",
dawn_sky = dawn_color or "7BA4FF",
dawn_horizon = dawn_color or "#C0D8FF",
night_sky = night_color or "000000",
night_horizon = night_color or "4A6790",
fog_tint_type = "default",
})
sky_data.sun = {visible = false, sunrise_visible = false}
sky_data.moon = {visible = false}
sky_data.stars = {visible = false}
@ -164,7 +164,8 @@ function dimension_handlers.nether(player, sky_data)
end
function dimension_handlers.void(player, sky_data)
sky_data.sky = { type = "plain",
sky_data.sky = {
type = "plain",
base_color = "#000000",
clouds = false,
}

View File

@ -75,13 +75,15 @@ function mcl_weather.has_snow(pos)
end
function mcl_weather.snow.set_sky_box()
mcl_weather.skycolor.add_layer(
"weather-pack-snow-sky",
{{r=0, g=0, b=0},
{r=85, g=86, b=86},
{r=135, g=135, b=135},
{r=85, g=86, b=86},
{r=0, g=0, b=0}})
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-snow-sky" then
mcl_weather.skycolor.add_layer(
"weather-pack-snow-sky",
{{r=0, g=0, b=0},
{r=85, g=86, b=86},
{r=135, g=135, b=135},
{r=85, g=86, b=86},
{r=0, g=0, b=0}})
end
mcl_weather.skycolor.active = true
for _, player in pairs(get_connected_players()) do
player:set_clouds({color="#ADADADE8"})

View File

@ -23,13 +23,15 @@ minetest.register_globalstep(function(dtime)
mcl_weather.rain.make_weather()
if mcl_weather.thunder.init_done == false then
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
{r=0, g=0, b=0},
{r=40, g=40, b=40},
{r=85, g=86, b=86},
{r=40, g=40, b=40},
{r=0, g=0, b=0},
})
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-thunder-sky" then
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
{r=0, g=0, b=0},
{r=40, g=40, b=40},
{r=85, g=86, b=86},
{r=40, g=40, b=40},
{r=0, g=0, b=0},
})
end
mcl_weather.skycolor.active = true
for _, player in pairs(get_connected_players()) do
player:set_clouds({color="#3D3D3FE8"})

View File

@ -5,9 +5,6 @@
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
-- CONSTS
local DOUBLE_DROP_CHANCE = 8
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap"
local BAMBOO_PLANK = BAMBOO .. "_plank"
@ -16,7 +13,7 @@ local BAMBOO_PLANK = BAMBOO .. "_plank"
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local node_sound = mcl_sounds.node_sound_wood_defaults()
local pr = PseudoRandom((os.time() + 15766) * 12) -- switched from math.random() to PseudoRandom because the random wasn't very random.
local pr = PseudoRandom((os.time() + 15766) * 12)
local on_rotate
if minetest.get_modpath("screwdriver") then
@ -31,33 +28,7 @@ local bamboo_def = {
paramtype = "light",
groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3},
sounds = node_sound,
drop = {
max_items = 1,
-- From the API:
-- max_items: Maximum number of item lists to drop.
-- The entries in 'items' are processed in order. For each:
-- Item filtering is applied, chance of drop is applied, if both are
-- successful the entire item list is dropped.
-- Entry processing continues until the number of dropped item lists
-- equals 'max_items'.
-- Therefore, entries should progress from low to high drop chance.
items = {
-- Examples:
{
-- 1 in DOUBLE_DROP_CHANCE chance of dropping.
-- Default rarity is '1'.
rarity = DOUBLE_DROP_CHANCE,
items = {BAMBOO .. " 2"},
},
{
-- 1 in 1 chance of dropping. (Note: this means that it will drop 100% of the time.)
-- Default rarity is '1'.
rarity = 1,
items = {BAMBOO},
},
},
},
drop = BAMBOO,
inventory_image = "mcl_bamboo_bamboo_shoot.png",
wield_image = "mcl_bamboo_bamboo_shoot.png",
@ -86,7 +57,6 @@ local bamboo_def = {
on_rotate = on_rotate,
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing then
return itemstack
end
@ -241,9 +211,6 @@ local bamboo_def = {
if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then
minetest.remove_node(new_pos)
minetest.sound_play(node_sound.dug, sound_params, true)
if pr:next(1, DOUBLE_DROP_CHANCE) == 1 then
minetest.add_item(new_pos, istack)
end
minetest.add_item(new_pos, istack)
end
end,

View File

@ -9,8 +9,6 @@ local SIDE_SCAFFOLDING = false
local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal"
-- ---------------------------------------------------------------------------
local SCAFFOLDING_NAME = "mcl_bamboo:scaffolding"
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
local BAMBOO_PLANK = BAMBOO .. "_plank"

View File

@ -74,7 +74,7 @@ function mcl_bamboo.break_orphaned(pos)
local node_name = node_below.name
-- short circuit checks.
if mcl_bamboo.is_dirt(node_name) or mcl_bamboo.is_bamboo(node_name) or mcl_bamboo.is_bamboo(minetest.get_node(pos).name) == false then
if node_name == "ignore" or mcl_bamboo.is_dirt(node_name) or mcl_bamboo.is_bamboo(node_name) or mcl_bamboo.is_bamboo(minetest.get_node(pos).name) == false then
return
end

View File

@ -7,8 +7,6 @@
-- LOCALS
local modname = minetest.get_current_modname()
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
mcl_bamboo = {}

View File

@ -5,8 +5,6 @@
--- These are all of the fuel recipes and all of the crafting recipes, consolidated into one place.
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
local BAMBOO_PLANK = BAMBOO .. "_plank"
-- Craftings

View File

@ -101,3 +101,42 @@ mesecon.register_button(
true,
nil,
"mesecons_button_push_wood")
minetest.register_node("mcl_cherry_blossom:pink_petals",{
description = S("Pink Petals"),
doc_items_longdesc = S("Pink Petals are ground decoration of cherry grove biomes"),
doc_items_hidden = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
sunlight_propagates = true,
floodable = true,
pointable = true,
drawtype = "nodebox",
node_box = {type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -7.9/16, 1/2}},
collision_box = {type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -7.9/16, 1/2}},
groups = {
shearsy=1,
handy=1,
flammable=3,
attached_node=1,
dig_by_piston=1,
compostability = 30,
deco_block=1
--not_in_creative_inventory=1,
},
use_texture_alpha = "clip",
sounds = mcl_sounds.node_sound_leaves_defaults(),
tiles = {
"mcl_cherry_blossom_pink_petals.png",
"mcl_cherry_blossom_pink_petals.png^[transformFY", -- mirror
"blank.png" -- empty
},
inventory_image = "mcl_cherry_blossom_pink_petals_inv.png",
_mcl_hardness = 0,
_mcl_blast_resistance = 0,
_on_bone_meal = function(_, _, _ , pos, n)
minetest.add_item(pos,n.name)
end
})

View File

@ -79,7 +79,7 @@ minetest.register_lbm({
local node_name = node.name
node.name = node_name .. "_small"
minetest.swap_node(pos, node)
select_and_spawn_entity(pos, node)
mcl_chests.select_and_spawn_entity(pos, node)
if node_name == "mcl_chests:trapped_chest_on" then
minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos))
mcl_chests.chest_update_after_close(pos)

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
-- Cactus and Sugar Cane
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_node("mcl_core:cactus", {
@ -42,9 +41,8 @@ minetest.register_node("mcl_core:cactus", {
},
-- Only allow to place cactus on sand or cactus
on_place = mcl_util.generate_on_place_plant_function(function(pos, node)
local node_below = minetest.get_node_or_nil({x=pos.x,y=pos.y-1,z=pos.z})
if not node_below then return false end
return (node_below.name == "mcl_core:cactus" or minetest.get_item_group(node_below.name, "sand") == 1)
local node_below = minetest.get_node_or_nil(vector.offset(pos, 0, -1, 0))
return node_below and (node_below.name == "mcl_core:cactus" or minetest.get_item_group(node_below.name, "sand") == 1)
end),
_mcl_blast_resistance = 0.4,
_mcl_hardness = 0.4,
@ -90,7 +88,7 @@ minetest.register_node("mcl_core:reeds", {
node_placement_prediction = "",
drop = "mcl_core:reeds", -- to prevent color inheritation
on_place = mcl_util.generate_on_place_plant_function(function(place_pos, place_node)
local soil_pos = {x=place_pos.x, y=place_pos.y-1, z=place_pos.z}
local soil_pos = vector.new(place_pos.x, place_pos.y-1, place_pos.z)
local soil_node = minetest.get_node_or_nil(soil_pos)
if not soil_node then return false end
local snn = soil_node.name -- soil node name
@ -113,16 +111,12 @@ minetest.register_node("mcl_core:reeds", {
-- Legal water position rules are the same as for decoration spawn_by rules.
-- This differs from MC, which does not allow diagonal neighbors
-- and neighbors 1 layer above.
local np1 = {x=soil_pos.x-1, y=soil_pos.y, z=soil_pos.z-1}
local np2 = {x=soil_pos.x+1, y=soil_pos.y+1, z=soil_pos.z+1}
if #minetest.find_nodes_in_area(np1, np2, {"group:water", "group:frosted_ice"}) > 0 then
if #minetest.find_nodes_in_area(vector.offset(soil_pos, -1, 0, -1), vector.offset(soil_pos, 1, 1, 1), {"group:water", "group:frosted_ice"}) > 0 then
-- Water found! Sugar canes are happy! :-)
return true
end
-- No water found! Sugar canes are not amuzed and refuses to be placed. :-(
return false
end),
on_construct = function(pos)
local node = minetest.get_node(pos)
@ -135,4 +129,4 @@ minetest.register_node("mcl_core:reeds", {
end,
_mcl_blast_resistance = 0,
_mcl_hardness = 0,
})
})

View File

@ -9,11 +9,11 @@ local wood_slab_groups = {handy = 1, axey = 1, material_wood = 1, wood_slab = 1}
local wood_stair_groups = {handy = 1, axey = 1, material_wood = 1, wood_stairs = 1}
local function generate_warped_tree(pos)
minetest.place_schematic(pos,modpath.."/schematics/warped_fungus_1.mts","random",nil,false,"place_center_x,place_center_z")
minetest.place_schematic(pos,modpath.."/schematics/warped_fungus_"..math.random(1,3)..".mts","random",nil,false,"place_center_x,place_center_z")
end
function generate_crimson_tree(pos)
minetest.place_schematic(pos,modpath.."/schematics/crimson_fungus_1.mts","random",nil,false,"place_center_x,place_center_z")
minetest.place_schematic(pos,modpath.."/schematics/crimson_fungus_"..math.random(1,3)..".mts","random",nil,false,"place_center_x,place_center_z")
end
function grow_vines(pos, moreontop ,vine, dir)

View File

@ -80,7 +80,7 @@ local function spawn_crystal(pos)
crystal_explode(crystal)
end
local portal_pos = vector.add(portal_center, vector.new(0, -1, 0))
mcl_structures.place_structure(portal_pos,mcl_structures.registered_structures["end_exit_portal"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
vl_structures.place_structure(portal_pos,vl_structures.registered_structures["end_exit_portal"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
end
minetest.register_entity("mcl_end:crystal", {

View File

@ -89,7 +89,7 @@ minetest.register_craftitem("mcl_end:ender_eye", {
local player_name = user:get_player_name()
local origin = user:get_pos()
origin.y = origin.y + 1.5
local strongholds = mcl_structures.registered_structures["end_shrine"].static_pos
local strongholds = vl_structures.registered_structures["end_shrine"].static_pos
local dim = mcl_worlds.pos_to_dimension(origin)
local is_creative = minetest.is_creative_enabled(player_name)

View File

@ -11,20 +11,6 @@ mcl_flowers.registered_simple_flowers = {}
local smallflowerlongdesc = S("This is a small flower. Small flowers are mainly used for dye production and can also be potted.")
local plant_usage_help = S("It can only be placed on a block on which it would also survive.")
local get_palette_color_from_pos = function(pos)
local biome_data = minetest.get_biome_data(pos)
local index = 0
if biome_data then
local biome = biome_data.biome
local biome_name = minetest.get_biome_name(biome)
local reg_biome = minetest.registered_biomes[biome_name]
if reg_biome then
index = reg_biome._mcl_grass_palette_index
end
end
return index
end
-- on_place function for flowers
local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos, node, itemstack)
local below = {x=pos.x, y=pos.y-1, z=pos.z}
@ -32,13 +18,7 @@ local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos,
if not soil_node then return false end
local has_palette = minetest.registered_nodes[itemstack:get_name()].palette ~= nil
local colorize
if has_palette then
colorize = get_palette_color_from_pos(pos)
end
if not colorize then
colorize = 0
end
local colorize = has_palette and mcl_util.get_palette_indexes_from_pos(pos).grass_palette_index or 0
--[[ Placement requirements:
* Dirt or grass block
@ -47,10 +27,7 @@ local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos,
]]
local light_night = minetest.get_node_light(pos, 0.0)
local light_day = minetest.get_node_light(pos, 0.5)
local light_ok = false
if (light_night and light_night >= 8) or (light_day and light_day >= minetest.LIGHT_MAX) then
light_ok = true
end
local light_ok = (light_night and light_night >= 8) or (light_day and light_day >= minetest.LIGHT_MAX)
if itemstack:get_name() == "mcl_flowers:wither_rose" and ( minetest.get_item_group(soil_node.name, "grass_block") > 0 or soil_node.name == "mcl_core:dirt" or soil_node.name == "mcl_core:coarse_dirt" or soil_node.name == "mcl_mud:mud" or soil_node.name == "mcl_moss:moss" or soil_node.name == "mcl_nether:netherrack" or minetest.get_item_group(soil_node.name, "soul_block") > 0 ) then
return true,colorize
end
@ -359,10 +336,7 @@ local function add_large_plant(name, desc, longdesc, bottom_img, top_img, inv_im
-- * Only with light level >= 8
-- * Only if two enough space
if (floor.name == "mcl_core:dirt" or minetest.get_item_group(floor.name, "grass_block") == 1 or (not is_flower and (floor.name == "mcl_core:coarse_dirt" or floor.name == "mcl_core:podzol" or floor.name == "mcl_core:podzol_snow"))) and bottom_buildable and top_buildable and light_ok then
local param2
if grass_color then
param2 = get_palette_color_from_pos(bottom)
end
local param2 = grass_color and mcl_util.get_palette_indexes_from_pos(bottom).grass_palette_index
-- Success! We can now place the flower
minetest.sound_play(minetest.registered_nodes[itemstring].sounds.place, {pos = bottom, gain=1}, true)
minetest.set_node(bottom, {name=itemstring, param2=param2})

View File

@ -196,7 +196,7 @@ function kelp.find_unsubmerged(pos, node, height)
for i=1,height do
walk_pos.y = y + i
local walk_node = mt_get_node(walk_pos)
if not kelp.is_submerged(walk_node) then
if walk_node.name ~= "ignore" and not kelp.is_submerged(walk_node) then
return walk_pos, walk_node, height, i
end
end

View File

@ -4,34 +4,38 @@ local storage = mcl_portals.storage
local vector = vector
local gateway_positions = {
{x = 96, y = -26925, z = 0},
{x = 91, y = -26925, z = 29},
{x = 77, y = -26925, z = 56},
{x = 56, y = -26925, z = 77},
{x = 29, y = -26925, z = 91},
{x = 0, y = -26925, z = 96},
{x = -29, y = -26925, z = 91},
{x = -56, y = -26925, z = 77},
{x = -77, y = -26925, z = 56},
{x = -91, y = -26925, z = 29},
{x = -96, y = -26925, z = 0},
{x = -91, y = -26925, z = -29},
{x = -77, y = -26925, z = -56},
{x = -56, y = -26925, z = -77},
{x = -29, y = -26925, z = -91},
{x = 0, y = -26925, z = -96},
{x = 29, y = -26925, z = -91},
{x = 56, y = -26925, z = -77},
{x = 77, y = -26925, z = -56},
{x = 91, y = -26925, z = -29},
vector.new(96, -26925, 0),
vector.new(91, -26925, 29),
vector.new(77, -26925, 56),
vector.new(56, -26925, 77),
vector.new(29, -26925, 91),
vector.new(0, -26925, 96),
vector.new(-29, -26925, 91),
vector.new(-56, -26925, 77),
vector.new(-77, -26925, 56),
vector.new(-91, -26925, 29),
vector.new(-96, -26925, 0),
vector.new(-91, -26925, -29),
vector.new(-77, -26925, -56),
vector.new(-56, -26925, -77),
vector.new(-29, -26925, -91),
vector.new(0, -26925, -96),
vector.new(29, -26925, -91),
vector.new(56, -26925, -77),
vector.new(77, -26925, -56),
vector.new(91, -26925, -29),
}
local path_gateway_portal = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_gateway_portal.mts"
local function spawn_gateway_portal(pos, dest_str)
return mcl_structures.place_schematic(vector.add(pos, vector.new(-1, -2, -1)), path_gateway_portal, "0", nil, true, nil, dest_str and function()
minetest.get_meta(pos):set_string("mcl_portals:gateway_destination", dest_str)
end)
return vl_structures.place_schematic(vector.offset(pos, -1, -2, -1), 0, path_gateway_portal, "0", {
force_placement = true,
prepare = false,
after_place = dest_str and function()
minetest.get_meta(pos):set_string("mcl_portals:gateway_destination", dest_str)
end
}, nil)
end
function mcl_portals.spawn_gateway_portal()

View File

@ -183,12 +183,12 @@ local dimension_to_teleport = { nether = "overworld", overworld = "nether" }
local limits = {
nether = {
pmin = {x=LIM_MIN, y = N_Y_MIN, z = LIM_MIN},
pmax = {x=LIM_MAX, y = N_Y_MAX, z = LIM_MAX},
pmin = vector.new(LIM_MIN, N_Y_MIN, LIM_MIN),
pmax = vector.new(LIM_MAX, N_Y_MAX, LIM_MAX),
},
overworld = {
pmin = {x=LIM_MIN, y = O_Y_MIN, z = LIM_MIN},
pmax = {x=LIM_MAX, y = O_Y_MAX, z = LIM_MAX},
pmin = vector.new(LIM_MIN, O_Y_MIN, LIM_MIN),
pmax = vector.new(LIM_MAX, O_Y_MAX, LIM_MAX),
},
}
@ -206,12 +206,10 @@ end
-- for old portals, so that players don't get surprises. New portals, or portals that lost
-- node storage due to destruction should use the lookup table.
local function get_portal_pos(pos)
local p1 = vector.offset(pos,-5,-1,-5)
local p2 = vector.offset(pos,5,5,5)
local nn = find_nodes_in_area(p1,p2,{"mcl_portals:portal"})
local nn = find_nodes_in_area(vector.offset(pos,-5,-1,-5), vector.offset(pos,5,5,5), {"mcl_portals:portal"})
for _,p in pairs(nn) do
local m = minetest.get_meta(p):get_string("target_portal")
if m and m ~= "" and mcl_vars.get_node(p).name == "mcl_portals:portal" then
if m and m ~= "" and minetest.get_node(p).name == "mcl_portals:portal" then
return minetest.get_position_from_hash(m)
end
end
@ -229,15 +227,13 @@ end
local function add_exit(p)
local retval = {key=false, new=false}
if not p or not p.y or not p.z or not p.x then
return retval
end
if not p or not p.y or not p.z or not p.x then return retval end
local x, y, z = floor(p.x), floor(p.y), floor(p.z)
local p = {x = x, y = y, z = z}
local p = vector.new(x, y, z)
if get_node({x=x,y=y-1,z=z}).name ~= OBSIDIAN
if get_node(vector.new(x, y-1, z)).name ~= OBSIDIAN
or get_node(p).name ~= PORTAL
or get_node({x=x,y=y+1,z=z}).name ~= PORTAL
or get_node(vector.new(x, y+1, z)).name ~= PORTAL
then
return retval
end
@ -301,7 +297,7 @@ local function remove_exit(p)
end
local x, y, z = floor(p.x), floor(p.y), floor(p.z)
local p = {x = x, y = y, z = z}
local p = vector.new(x, y, z)
local k = get_exit_key(p)
if not exits[k] then
@ -529,14 +525,14 @@ local function destroy_nether_portal(pos, node)
while i <= #nodes do
pos = nodes[i]
if orientation == 0 then
check_remove({x = pos.x - 1, y = pos.y, z = pos.z})
check_remove({x = pos.x + 1, y = pos.y, z = pos.z})
check_remove(vector.offset(pos, -1, 0, 0))
check_remove(vector.offset(pos, 1, 0, 0))
else
check_remove({x = pos.x, y = pos.y, z = pos.z - 1})
check_remove({x = pos.x, y = pos.y, z = pos.z + 1})
check_remove(vector.offset(pos, 0, 0, -1))
check_remove(vector.offset(pos, 0, 0, 1))
end
check_remove({x = pos.x, y = pos.y - 1, z = pos.z})
check_remove({x = pos.x, y = pos.y + 1, z = pos.z})
check_remove(vector.offset(pos, 0, -1, 0))
check_remove(vector.offset(pos, 0, 1, 0))
remove_exits({pos})
i = i + 1
end
@ -624,7 +620,7 @@ local function build_and_light_frame(x1, y1, z1, x2, y2, z2, name)
else
set_node(pos, {name = PORTAL, param2 = orientation})
add_exits({
{x=pos.x, y=pos.y-1, z=pos.z}
vector.new(pos.x, pos.y-1, pos.z)
})
end
end
@ -701,8 +697,8 @@ function build_nether_portal(cube_pos1, width, height, orientation, name, clear_
-- Build obsidian platform:
for x = pos.x - orientation, pos.x + orientation + (width_inner - 1) * (1 - orientation), 1 + orientation do
for z = pos.z - 1 + orientation, pos.z + 1 - orientation + (width_inner - 1) * orientation, 2 - orientation do
local pp = {x = x, y = pos.y - 1, z = z}
local pp_1 = {x = x, y = pos.y - 2, z = z}
local pp = vector.new(x, pos.y - 1, z)
local pp_1 = vector.new(x, pos.y - 2, z)
local nn = get_node(pp).name
local nn_1 = get_node(pp_1).name
if ((nn=="air" and nn_1 == "air") or not registered_nodes[nn].is_ground_content) and not is_protected(pp, name) then
@ -823,7 +819,7 @@ local function finalize_teleport(obj, exit)
local _, dim = mcl_worlds.y_to_layer(exit.y)
-- If player stands, player is at ca. something+0.5 which might cause precision problems, so we used ceil for objpos.y
objpos = {x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)}
objpos = vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5))
if get_node(objpos).name ~= PORTAL then
log("action", "Entity no longer standing in portal")
return
@ -1085,19 +1081,19 @@ local function search_for_build_location(blockpos, action, calls_remaining, para
if nc2 >= (W_MIN*(H_MIN-1)*W_MIN) - ACCEPTABLE_PORTAL_REPLACES then
-- We have sorted the candidates by distance, this is the best location.
distance = distance0
pos0 = {x=node.x, y=node.y, z=node.z}
pos0 = vector.new(node.x, node.y, node.z)
log("verbose", "Found acceptable location at "..pos_to_string(pos0)..", distance "..distance0..", air nodes "..nc2)
break
elseif not most_airy_pos or nc2>most_airy_count then
-- Remember the cube with the most amount of air as a fallback.
most_airy_count = nc2
most_airy_distance = distance0
most_airy_pos = {x=node.x, y=node.y, z=node.z}
most_airy_pos = vector.new(node.x, node.y, node.z)
log("verbose", "Found fallback location at "..pos_to_string(most_airy_pos)..", distance "..distance0..", air nodes "..nc2)
elseif most_airy_pos and nc2==most_airy_count and distance0<most_airy_distance then
-- Use distance as a tiebreaker.
most_airy_distance = distance0
most_airy_pos = {x=node.x, y=node.y, z=node.z}
most_airy_pos = vector.new(node.x, node.y, node.z)
log("verbose", "Found fallback location at "..pos_to_string(most_airy_pos)..", distance "..distance0..", air nodes "..nc2)
end
@ -1128,7 +1124,7 @@ local function search_for_build_location(blockpos, action, calls_remaining, para
log("verbose", "No space found, emerging one chunk below")
end
local new_target = {x=target.x, y=target.y + direction * mcl_vars.chunk_size_in_nodes, z=target.z}
local new_target = vector.new(target.x, target.y + direction * mcl_vars.chunk_size_in_nodes, target.z)
pos1, pos2 = find_build_limits(new_target, param.target_dim)
local diff = add(pos2, mul(pos1, -1))
@ -1202,7 +1198,7 @@ local function create_portal(origin, target, target_dim, name, obj)
origin = origin,
target = target,
target_dim = target_dim,
ideal_target = vector.new(target.x, target.y, target.z), -- copy
ideal_target = vector.copy(target),
pos1 = pos1,
pos2 = pos2,
name=name,
@ -1223,13 +1219,12 @@ local function available_for_nether_portal(p)
end
local function check_and_light_shape(pos, orientation)
local stack = {{x = pos.x, y = pos.y, z = pos.z}}
local stack = {vector.copy(pos)}
local node_list = {}
local index_list = {}
local node_counter = 0
-- Search most low node from the left (pos1) and most right node from the top (pos2)
local pos1 = {x = pos.x, y = pos.y, z = pos.z}
local pos2 = {x = pos.x, y = pos.y, z = pos.z}
local pos1, pos2 = vector.copy(pos), vector.copy(pos)
local kx, ky, kz = pos.x - 1999, pos.y - 1999, pos.z - 1999
while #stack > 0 do
@ -1247,22 +1242,22 @@ local function check_and_light_shape(pos, orientation)
return false
end
node_counter = node_counter + 1
node_list[node_counter] = {x = x, y = y, z = z}
node_list[node_counter] = vector.new(x, y, z)
index_list[k] = true
stack[i].y = y - 1
stack[i + 1] = {x = x, y = y + 1, z = z}
stack[i + 1] = vector.new(x, y + 1, z)
if orientation == 0 then
stack[i + 2] = {x = x - 1, y = y, z = z}
stack[i + 3] = {x = x + 1, y = y, z = z}
stack[i + 2] = vector.new(x - 1, y, z)
stack[i + 3] = vector.new(x + 1, y, z)
else
stack[i + 2] = {x = x, y = y, z = z - 1}
stack[i + 3] = {x = x, y = y, z = z + 1}
stack[i + 2] = vector.new(x, y, z - 1)
stack[i + 3] = vector.new(x, y, z + 1)
end
if (y < pos1.y) or (y == pos1.y and (x < pos1.x or z < pos1.z)) then
pos1 = {x = x, y = y, z = z}
pos1 = vector.new(x, y, z)
end
if (x > pos2.x or z > pos2.z) or (x == pos2.x and z == pos2.z and y > pos2.y) then
pos2 = {x = x, y = y, z = z}
pos2 = vector.new(x, y, z)
end
end
end
@ -1344,7 +1339,7 @@ local function check_portal_then_teleport(obj, origin, exit)
remove_exits({exit})
-- Also remove from structure storage, otherwise ABM will try the same bad exit again.
local objpos = obj:get_pos()
delete_portal_pos({x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)})
delete_portal_pos(vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5)))
origin_flush(origin, nil)
return
@ -1367,7 +1362,7 @@ local function teleport_no_delay(obj, portal_pos)
local target_dim = dimension_to_teleport[current_dim]
-- If player stands, player is at ca. something+0.5 which might cause precision problems, so we used ceil for objpos.y
origin = {x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)}
origin = vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5))
if get_node(origin).name ~= PORTAL then return end
local target = get_target(origin)
@ -1450,8 +1445,8 @@ local function animation(player, playername)
end
minetest.add_particlespawner({
amount = 1,
minpos = {x = pos.x - 0.1, y = pos.y + 1.4, z = pos.z - 0.1},
maxpos = {x = pos.x + 0.1, y = pos.y + 1.6, z = pos.z + 0.1},
minpos = vector.offset(pos, -0.1, 1.4, -0.1),
maxpos = vector.offset(pos, 0.1, 1.6, 0.1),
minvel = 0,
maxvel = 0,
minacc = 0,
@ -1496,11 +1491,11 @@ minetest.register_abm({
local time = random() * 1.9 + 0.5
local velocity, acceleration
if o == 1 then
velocity = {x = random() * 0.7 + 0.3, y = random() - 0.5, z = random() - 0.5}
acceleration = {x = random() * 1.1 + 0.3, y = random() - 0.5, z = random() - 0.5}
velocity = vector.new(random() * 0.7 + 0.3, random() - 0.5, random() - 0.5)
acceleration = vector.new(random() * 1.1 + 0.3, random() - 0.5, random() - 0.5)
else
velocity = {x = random() - 0.5, y = random() - 0.5, z = random() * 0.7 + 0.3}
acceleration = {x = random() - 0.5, y = random() - 0.5, z = random() * 1.1 + 0.3}
velocity = vector.new(random() - 0.5, random() - 0.5, random() * 0.7 + 0.3)
acceleration = vector.new(random() - 0.5, random() - 0.5, random() * 1.1 + 0.3)
end
local distance = add(mul(velocity, time), mul(acceleration, time * time / 2))
if d == 1 then
@ -1566,12 +1561,12 @@ minetest.override_item(OBSIDIAN, {
end
-- check each of 6 sides of it and destroy every portal
check_remove({x = pos.x - 1, y = pos.y, z = pos.z})
check_remove({x = pos.x + 1, y = pos.y, z = pos.z})
check_remove({x = pos.x, y = pos.y, z = pos.z - 1})
check_remove({x = pos.x, y = pos.y, z = pos.z + 1})
check_remove({x = pos.x, y = pos.y - 1, z = pos.z})
check_remove({x = pos.x, y = pos.y + 1, z = pos.z})
check_remove(vector.offset(pos, -1, 0, 0))
check_remove(vector.offset(pos, 1, 0, 0))
check_remove(vector.offset(pos, 0, 0, -1))
check_remove(vector.offset(pos, 0, 0, 1))
check_remove(vector.offset(pos, 0, -1, 0))
check_remove(vector.offset(pos, 0, 1, 0))
end,
_on_ignite = function(user, pointed_thing)
@ -1579,16 +1574,16 @@ minetest.override_item(OBSIDIAN, {
-- Check empty spaces around obsidian and light all frames found.
-- Permit igniting of portals that are partly protected to maintain integrity.
local portals_placed =
mcl_portals.light_nether_portal({x = x - 1, y = y, z = z}) or mcl_portals.light_nether_portal({x = x + 1, y = y, z = z}) or
mcl_portals.light_nether_portal({x = x, y = y - 1, z = z}) or mcl_portals.light_nether_portal({x = x, y = y + 1, z = z}) or
mcl_portals.light_nether_portal({x = x, y = y, z = z - 1}) or mcl_portals.light_nether_portal({x = x, y = y, z = z + 1})
mcl_portals.light_nether_portal(vector.new(x - 1, y, z)) or mcl_portals.light_nether_portal(vector.new(x + 1, y, z)) or
mcl_portals.light_nether_portal(vector.new(x, y - 1, z)) or mcl_portals.light_nether_portal(vector.new(x, y + 1, z)) or
mcl_portals.light_nether_portal(vector.new(x, y, z - 1)) or mcl_portals.light_nether_portal(vector.new(x, y, z + 1))
if portals_placed then
log("verbose", "Nether portal activated at "..pos_to_string({x=x,y=y,z=z})..".")
log("verbose", "Nether portal activated at "..pos_to_string(vector.new(x, y, z))..".")
if minetest.get_modpath("doc") then
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", PORTAL)
-- Achievement for finishing a Nether portal TO the Nether
local dim = mcl_worlds.pos_to_dimension({x=x, y=y, z=z})
local dim = mcl_worlds.pos_to_dimension(vector.new(x, y, z))
if minetest.get_modpath("awards") and dim ~= "nether" and user:is_player() then
awards.unlock(user:get_player_name(), "mcl:buildNetherPortal")
end
@ -1600,13 +1595,13 @@ minetest.override_item(OBSIDIAN, {
end,
})
mcl_structures.register_structure("nether_portal",{
vl_structures.register_structure("nether_portal",{
nospawn = true,
filenames = {
modpath.."/schematics/mcl_portals_nether_portal.mts"
}
})
mcl_structures.register_structure("nether_portal_open",{
vl_structures.register_structure("nether_portal_open",{
nospawn = true,
filenames = {
modpath.."/schematics/mcl_portals_nether_portal_open.mts"

View File

@ -20,7 +20,7 @@ local function connectable(itemstring)
return (minetest.get_item_group(itemstring, "wall") == 1) or (minetest.get_item_group(itemstring, "solid") == 1)
end
local function update_wall(pos)
function mcl_walls.update_wall(pos)
local thisnode = minetest.get_node(pos)
if minetest.get_item_group(thisnode.name, "wall") == 0 then
@ -67,11 +67,12 @@ local function update_wall(pos)
minetest.add_node(pos, {name = basename..sum})
end
local update_wall = mcl_walls.update_wall
local function update_wall_global(pos)
for i = 1,5 do
local dir = directions[i]
update_wall({x = pos.x + dir.x, y = pos.y + dir.y, z = pos.z + dir.z})
update_wall(vector.offset(pos, dir.x, dir.y, dir.z))
end
end
@ -269,7 +270,7 @@ function mcl_walls.register_wall(nodename, description, source, tiles, inventory
fixed = {-4/16, -0.5, -4/16, 4/16, 1, 4/16}
},
collisionbox = {-0.2, 0, -0.2, 0.2, 1.4, 0.2},
on_construct = update_wall,
on_construct = mcl_walls.update_wall,
sounds = sounds,
_mcl_blast_resistance = blast_resistance,
_mcl_hardness = hardness,

View File

@ -1 +0,0 @@
Biomes mod. By Wuzzy and maikerumine.

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
mcl_dungeons = {}
local logging = minetest.settings:get_bool("mcl_logging_dungeons", false)
local mg_name = minetest.get_mapgen_setting("mg_name")
-- Are dungeons disabled?
if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then return end
@ -234,7 +235,9 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
-- Check conditions. If okay, start generating
if check and (openings_counter < 1 or openings_counter > 5) then return end
minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string(vector_new(x, y, z)))
if logging then
minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string(vector_new(x, y, z)))
end
-- Okay! Spawning starts!
-- Remember spawner chest positions to set metadata later
@ -253,7 +256,7 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
-- If it failed again, tough luck! We stick with only 1 chest spawned.
local lastRandom
local secondChance = true -- second chance is still available
for i=1, totalChests do
for _ = 1, totalChests do
local r = pr:next(1, totalChestSlots)
if r == lastRandom and secondChance then
-- Oops! Same slot selected. Try again.
@ -368,8 +371,11 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
end
set_node(pos, {name="mcl_chests:chest", param2=facedir})
vl_structures.construct_nodes(pos, pos, {"mcl_chests:chest"})
local meta = get_meta(pos)
minetest.log("action", "[mcl_dungeons] Filling chest " .. tostring(c) .. " at " .. minetest.pos_to_string(pos))
if logging then
minetest.log("action", "[mcl_dungeons] Filling chest " .. tostring(c) .. " at " .. minetest.pos_to_string(pos))
end
mcl_loot.fill_inventory(meta:get_inventory(), "main", mcl_loot.get_multi_loot(loottable, pr), pr)
end
@ -404,7 +410,9 @@ local function dungeons_nodes(minp, maxp, blockseed)
local z = pr:next(minp.z, maxp.z-dim.z-1)
local p1 = vector_new(x, y, z)
local p2 = vector_new(x+dim.x+1, y+dim.y+1, 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))
if logging then
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
end
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr})
end
end
@ -414,7 +422,9 @@ 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 = vector_new(p1.x+dim.x+1, p1.y+dim.y+1, 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))
if logging then
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
end
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr, dontcheck=true})
end

View File

@ -1,77 +1,31 @@
local logging = minetest.settings:get_bool("mcl_logging_mapgen", false)
local log_timing = minetest.settings:get_bool("mcl_logging_mapgen_timing", false) -- detailed, for performance debugging
local registered_generators = {}
local lvm, nodes, param2 = 0, 0, 0
local lvm_buffer = {}
local logging = minetest.settings:get_bool("mcl_logging_mapgen",false)
local function roundN(n, d)
if type(n) ~= "number" then return n end
local m = 10^d
return math.floor(n * m + 0.5) / m
end
local function run_generators (p1, p2, blockseed)
if nodes > 0 then
for _, rec in ipairs(registered_generators) do
if rec.nf then
rec.nf(p1, p2, blockseed)
end
end
end
end
local function update_data (vm, data, data2)
-- Write stuff
vm:set_data(data)
if param2 > 0 then
vm:set_param2_data(data2)
end
end
local function post_generator_processing(vm, minp, maxp, deco_used, deco_table, ore_used, ore_table)
if deco_table then
minetest.generate_decorations(vm,vector.new(minp.x,deco_table.min,minp.z),vector.new(maxp.x,deco_table.max,maxp.z))
elseif deco_used then
minetest.generate_decorations(vm)
end
if ore_table then
minetest.generate_ores(vm,vector.new(minp.x,ore_table.min,minp.z),vector.new(maxp.x,ore_table.max,maxp.z))
elseif ore_used then
minetest.generate_ores(vm)
end
end
local function post_generator_processing_2(vm, p1, p2, shadow)
vm:calc_lighting(p1, p2, shadow)
vm:write_to_map()
vm:update_liquids()
end
local seed = minetest.get_mapgen_setting("seed")
minetest.register_on_generated(function(minp, maxp, blockseed)
local t1 = os.clock()
local p1, p2 = {x=minp.x, y=minp.y, z=minp.z}, {x=maxp.x, y=maxp.y, z=maxp.z}
if lvm > 0 then
local lvm_used, shadow, deco_used, deco_table, ore_used, ore_table = false, false, false, false, false, false
local lb2 = {} -- param2
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local e1, e2 = {x=emin.x, y=emin.y, z=emin.z}, {x=emax.x, y=emax.y, z=emax.z}
local data2
local data = vm:get_data(lvm_buffer)
if param2 > 0 then
data2 = vm:get_param2_data(lb2)
local area = VoxelArea(emin, emax)
local data = vm:get_data()
local data2 = param2 > 0 and vm:get_param2_data()
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "get_data", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - t1)*1000))
end
local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
local lvm_used, shadow, deco_used, deco_table, ore_used, ore_table = false, false, false, false, false, false
for _, rec in ipairs(registered_generators) do
if rec.vf then
local gt1 = os.clock()
local p1, p2 = vector.copy(minp), vector.copy(maxp) -- defensive copies
local e1, e2 = vector.copy(emin), vector.copy(emax) -- defensive copies
local lvm_used0, shadow0, deco, ore = rec.vf(vm, data, data2, e1, e2, area, p1, p2, blockseed)
if lvm_used0 then
lvm_used = true
end
if shadow0 then
shadow = true
end
lvm_used = lvm_used or lvm_used0
shadow = shadow or shadow0
if deco and type(deco) == "table" then
deco_table = deco
elseif deco then
@ -82,26 +36,65 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
elseif deco then
ore_used = true
end
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", rec.id, minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
end
end
if lvm_used then
update_data (vm, data, data2)
post_generator_processing(vm, minp, maxp, deco_used, deco_table, ore_used, ore_table)
post_generator_processing_2(vm, p1, p2, shadow)
local gt1 = os.clock()
vm:set_data(data)
if param2 > 0 then vm:set_param2_data(data2) end
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "set_data", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
local gt1 = os.clock()
if deco_table and #deco_table > 0 then
minetest.generate_decorations(vm,vector.new(minp.x,deco_table.min,minp.z),vector.new(maxp.x,deco_table.max,maxp.z))
elseif deco_used then
minetest.generate_decorations(vm)
end
if log_timing and (deco_table or deco_used) then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "decorations", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
local gt1 = os.clock()
if ore_table and #ore_table > 0 then
minetest.generate_ores(vm,vector.new(minp.x,ore_table.min,minp.z),vector.new(maxp.x,ore_table.max,maxp.z))
elseif ore_used then
minetest.generate_ores(vm)
end
if log_timing and (ore_table or ore_used) then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "ores", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
local gt1 = os.clock()
vm:calc_lighting(minp, maxp, shadow)
vm:write_to_map()
vm:update_liquids()
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "light/write/liquids", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
end
end
run_generators (p1, p2, blockseed)
if nodes > 0 then
for _, rec in ipairs(registered_generators) do
if rec.nf then
local gt1 = os.clock()
local p1, p2 = vector.copy(minp), vector.copy(maxp) -- defensive copies
rec.nf(p1, p2, blockseed)
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", rec.id, minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
end
end
end
mcl_vars.add_chunk(minp)
if logging then
minetest.log("action", "[mcl_mapgen_core] Generating chunk " .. minetest.pos_to_string(minp) .. " ... " .. minetest.pos_to_string(maxp).."..."..tostring(roundN(((os.clock() - t1)*1000),2)).."ms")
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "Generating chunk", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - t1)*1000))
end
end)
function minetest.register_on_generated(node_function)
mcl_mapgen_core.register_generator("mod_"..minetest.get_current_modname().."_"..tostring(#registered_generators+1), nil, node_function)
end
@ -145,3 +138,122 @@ function mcl_mapgen_core.unregister_generator(id)
if rec.needs_param2 then param2 = param2 - 1 end
--if rec.needs_level0 then level0 = level0 - 1 end
end
-- Try to make decorations more deterministic in order, by sorting by priority and name
-- At least for low-priority this should make map seeds more comparable, but
-- adding for example a new structure can still change everything that comes
-- later, because currently decoration blockseeds are incremented sequentially
-- c.f., https://github.com/minetest/minetest/issues/14919
local pending_decorations = {}
local gennotify_map = {}
function mcl_mapgen_core.register_decoration(def, callback)
if def.gen_callback and not def.name then error("gen_callback requires a named decoration.") end
if callback then error("Callbacks have been redesigned.") end
-- customize foliage colors
if (def._mcl_foliage_palette_index or 0) > 0 then
if type(def.schematic) ~= "string" then error("Can currently only be used with file schematics.") end
-- Load the schema and apply foliage palette
local schem = minetest.read_schematic(def.schematic, {})
schem.name = def.schematic -- preserve the file name
local cache = {}
for _, node in ipairs(schem.data) do
local kind = cache[node.name]
if kind == nil then
local ndef = minetest.registered_nodes[node.name]
kind = ndef and (ndef.groups.foliage_palette and "color")
or (ndef.groups.foliage_palette_wallmounted and "colorwallmounted")
or false
cache[node.name] = kind
end
if kind == "color" then
node.param2 = def._mcl_foliage_palette_index
elseif kind == "colorwallmounted" then
node.param2 = def._mcl_foliage_palette_index * 8 + (node.param2 % 8)
end
end
def.schematic = schem
end
if pending_decorations == nil then
minetest.log("warning", "Decoration registered after mapgen core initialization: "..tostring(def.name))
minetest.register_decoration(def)
if def.gen_callback then
def.deco_id = minetest.get_decoration_id(def.name)
if not def.deco_id then
error("Failed to get the decoration id for "..tostring(def.name))
else
minetest.set_gen_notify({decoration = true}, {def.deco_id})
gennotify_map["decoration#" .. def.deco_id] = def
end
end
return
end
table.insert(pending_decorations, def)
end
local function sort_decorations()
local keys, map = {}, {}
for i, def in pairs(pending_decorations) do
local name = def.name
-- we try to generate fallback names to make order more deterministic
name = name or (def.decoration and string.format("%s:%04d", def.decoration, i))
if not name and type(def.schematic) == "string" then
local sc = string.split(def.schematic, "/")
name = string.format("%s:%04d", sc[#sc], i)
end
if not name and type(def.schematic) == "table" and def.schematic.name then
local sc = string.split(def.schematic.name, "/")
name = string.format("%s:%04d", sc[#sc], i)
end
if not name and type(def.schematic) == "table" and def.schematic.data then
name = ""
for _, v in ipairs(def.schematic.data) do
if v.name then name = name .. v.name .. ":" end
end
name = name .. string.format("%04d", i)
end
name = name or string.format("%04d", i)
local prio = (def.priority or 1000) + i/1000
local key = string.format("%08.3f:%s", prio, name)
table.insert(keys, key)
map[key] = def
end
table.sort(keys)
for _, key in ipairs(keys) do
local def = map[key]
if def.name and minetest.get_decoration_id(def.name) then
minetest.log("warning", "Decoration ID not unique: "..def.name)
end
local deco_id = minetest.register_decoration(def)
if not deco_id then
error("Failed to register decoration "..tostring(def.name).." - name not unique?")
end
if def.name then
deco_id = minetest.get_decoration_id(def.name)
if not deco_id then
error("Failed to register decoration "..tostring(def.name).." - name not unique?")
end
if def.gen_callback then
minetest.set_gen_notify({decoration = true}, {deco_id})
gennotify_map["decoration#" .. deco_id] = def
end
end
end
pending_decorations = nil -- as we will not run again
end
mcl_mapgen_core.register_generator("Gennotify callbacks", nil, function(minp, maxp, blockseed)
local pr = PcgRandom(blockseed + seed + 48214) -- constant seed offset
local gennotify = minetest.get_mapgen_object("gennotify")
for key, def in pairs(gennotify_map) do
local t = gennotify[key]
if t and #t > 0 then
-- Fisher-Yates shuffle, using pr
for i = 1, #t-1 do
local r = pr:next(i,#t)
t[i], t[r] = t[r], t[i]
end
def.gen_callback(t, minp, maxp, blockseed)
end
end
end)
minetest.register_on_mods_loaded(sort_decorations)

View File

@ -1,10 +1,4 @@
mcl_mapgen_core = {}
local registered_generators = {}
local lvm, nodes, param2 = 0, 0, 0
local lvm_used = false
local lvm_buffer = {}
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
@ -122,8 +116,6 @@ elseif mg_name == "fractal" then
mg_flags.caverns = true
end
local mg_flags_str = ""
for k,v in pairs(mg_flags) do
if v == false then
@ -210,39 +202,42 @@ end
-- If function(pos_to_check, content_id_at_this_pos), will set node only if returns true.
-- min, max: Minimum and maximum Y levels of the layers to set
-- minp, maxp: minp, maxp of the on_generated
-- lvm_used: Set to true if any node in this on_generated has been set before.
--
-- returns true if any node was set and lvm_used otherwise
local function set_layers(data, area, content_id, check, min, max, minp, maxp, lvm_used, pr)
if (maxp.y >= min and minp.y <= max) then
for y = math.max(min, minp.y), math.min(max, maxp.y) do
for x = minp.x, maxp.x do
for z = minp.z, maxp.z do
local p_pos = area:index(x, y, z)
if check then
if type(check) == "function" and check({x=x,y=y,z=z}, data[p_pos], pr) then
data[p_pos] = content_id
lvm_used = true
elseif check == data[p_pos] then
data[p_pos] = content_id
lvm_used = true
end
else
data[p_pos] = content_id
lvm_used = true
end
end
-- returns true if any node was set
local function set_layers(data, area, content_id, check, min, max, minp, maxp, pr)
if maxp.y < min or minp.y > max then return false end
local lvm_used = false
if not check then
for p_pos in area:iter(minp.x, math.max(min, minp.y), minp.z, maxp.x, math.min(max, maxp.y), maxp.z) do
data[p_pos] = content_id
lvm_used = true
end
elseif type(check) == "function" then
-- slow path, needs vector coordinates (bedrock uses y only)
for p_pos in area:iter(minp.x, math.max(min, minp.y), minp.z, maxp.x, math.min(max, maxp.y), maxp.z) do
if check(area:position(p_pos), data[p_pos], pr) then
data[p_pos] = content_id
lvm_used = true
end
end
else
for p_pos in area:iter(minp.x, math.max(min, minp.y), minp.z, maxp.x, math.min(max, maxp.y), maxp.z) do
if check == data[p_pos] then
data[p_pos] = content_id
lvm_used = true
end
end
end
return lvm_used
end
local function set_grass_palette(minp,maxp,data2,area,biomemap,nodes)
local function set_grass_palette(minp,maxp,data2,area,nodes)
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local biomemap = minetest.get_mapgen_object("biomemap")
if not biomemap then return end
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}})
local aream = VoxelArea(vector.new(minp.x, 0, minp.z), vector.new(maxp.x, 0, maxp.z))
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do
local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z)
@ -259,11 +254,13 @@ local function set_grass_palette(minp,maxp,data2,area,biomemap,nodes)
return lvm_used
end
local function set_foliage_palette(minp,maxp,data2,area,biomemap,nodes)
local function set_foliage_palette(minp,maxp,data2,area,nodes)
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local biomemap = minetest.get_mapgen_object("biomemap")
if not biomemap then return end
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}})
local aream = VoxelArea(vector.new(minp.x, 0, minp.z), vector.new(maxp.x, 0, maxp.z))
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do
local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z)
@ -283,11 +280,14 @@ local function set_foliage_palette(minp,maxp,data2,area,biomemap,nodes)
return lvm_used
end
local function set_water_palette(minp,maxp,data2,area,biomemap,nodes)
local function set_water_palette(minp,maxp,data2,area,nodes)
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local biomemap = minetest.get_mapgen_object("biomemap")
if not biomemap then return end
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}})
local aream = VoxelArea(vector.new(minp.x, 0, minp.z), vector.new(maxp.x, 0, maxp.z))
-- FIXME: this relies on the voxelmanip already being written.
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do
local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z)
@ -304,63 +304,62 @@ local function set_water_palette(minp,maxp,data2,area,biomemap,nodes)
return lvm_used
end
--[[
local function set_seagrass_param2(minp,maxp,data2,area,nodes)
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}})
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do
local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z)
data2[p_pos] = 3
data2[area:index(n.x, n.y, n.z)] = 3
lvm_used = true
end
return lvm_used
end
]]
-- Below the bedrock, generate air/void
local function world_structure(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap --ymin, ymax
local lvm_used = false
local pr = PseudoRandom(blockseed)
local lvm_used = false
-- The Void below the Nether:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mapgen_edge_min , mcl_vars.mg_nether_min -1, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mapgen_edge_min , mcl_vars.mg_nether_min -1, minp, maxp, pr) or lvm_used
-- [[ THE NETHER: mcl_vars.mg_nether_min mcl_vars.mg_nether_max ]]
-- The Air on the Nether roof, https://git.minetest.land/VoxeLibre/VoxeLibre/issues/1186
lvm_used = set_layers(data, area, c_air , nil, mcl_vars.mg_nether_max +1, mcl_vars.mg_nether_max + 128 , minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_air , nil, mcl_vars.mg_nether_max +1, mcl_vars.mg_nether_max + 128 , minp, maxp, pr) or lvm_used
-- The Void above the Nether below the End:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_nether_max + 128 +1, mcl_vars.mg_end_min -1, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_nether_max + 128 +1, mcl_vars.mg_end_min -1, minp, maxp, pr) or lvm_used
-- [[ THE END: mcl_vars.mg_end_min mcl_vars.mg_end_max ]]
-- The Void above the End below the Realm barrier:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_end_max +1, mcl_vars.mg_realm_barrier_overworld_end_min-1, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_end_max +1, mcl_vars.mg_realm_barrier_overworld_end_min-1, minp, maxp, pr) or lvm_used
-- Realm barrier between the Overworld void and the End
lvm_used = set_layers(data, area, c_realm_barrier, nil, mcl_vars.mg_realm_barrier_overworld_end_min , mcl_vars.mg_realm_barrier_overworld_end_max , minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_realm_barrier, nil, mcl_vars.mg_realm_barrier_overworld_end_min , mcl_vars.mg_realm_barrier_overworld_end_max , minp, maxp, pr) or lvm_used
-- The Void above Realm barrier below the Overworld:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_realm_barrier_overworld_end_max+1, mcl_vars.mg_overworld_min -1, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_realm_barrier_overworld_end_max+1, mcl_vars.mg_overworld_min -1, minp, maxp, pr) or lvm_used
if mg_name ~= "singlenode" then
-- Bedrock
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_overworld_min, mcl_vars.mg_bedrock_overworld_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_bottom_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_top_min, mcl_vars.mg_bedrock_nether_top_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_overworld_min, mcl_vars.mg_bedrock_overworld_max, minp, maxp, pr) or lvm_used
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_bottom_max, minp, maxp, pr) or lvm_used
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_top_min, mcl_vars.mg_bedrock_nether_top_max, minp, maxp, pr) or lvm_used
-- Flat Nether
if mg_name == "flat" then
lvm_used = set_layers(data, area, c_air, nil, mcl_vars.mg_flat_nether_floor, mcl_vars.mg_flat_nether_ceiling, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_air, nil, mcl_vars.mg_flat_nether_floor, mcl_vars.mg_flat_nether_ceiling, minp, maxp, pr) or lvm_used
end
-- Big lava seas by replacing air below a certain height
if mcl_vars.mg_lava then
lvm_used = set_layers(data, area, c_lava, c_air, mcl_vars.mg_overworld_min, mcl_vars.mg_lava_overworld_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_nether_lava, c_air, mcl_vars.mg_nether_min, mcl_vars.mg_lava_nether_max, minp, maxp, lvm_used, pr)
lvm_used = set_layers(data, area, c_lava, c_air, mcl_vars.mg_overworld_min, mcl_vars.mg_lava_overworld_max, minp, maxp, pr) or lvm_used
lvm_used = set_layers(data, area, c_nether_lava, c_air, mcl_vars.mg_nether_min, mcl_vars.mg_lava_nether_max, minp, maxp, pr) or lvm_used
end
end
local deco = false
local ores = false
local deco, ores = false, false
if minp.y > mcl_vars.mg_nether_deco_max - 64 and maxp.y < mcl_vars.mg_nether_max + 128 then
deco = {min=mcl_vars.mg_nether_deco_max,max=mcl_vars.mg_nether_max}
end
@ -371,58 +370,40 @@ local function world_structure(vm, data, data2, emin, emax, area, minp, maxp, bl
return lvm_used, lvm_used, deco, ores
end
--[[ replaced with decoration hack to replace grass nodes
local function block_fixes_grass(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap = minetest.get_mapgen_object("biomemap")
local lvm_used = false
local pr = PseudoRandom(blockseed)
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then
-- Set param2 (=color) of nodes which use the grass colour palette.
lvm_used = set_grass_palette(minp,maxp,data2,area,biomemap,{"group:grass_palette"})
end
return lvm_used
end
-- Set param2 (=color) of nodes which use the grass colour palette.
return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
set_grass_palette(minp,maxp,data2,area,{"group:grass_palette"})
end]]
--[[ replaced with schematic specialization per biome
local function block_fixes_foliage(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap = minetest.get_mapgen_object("biomemap")
local lvm_used = false
local pr = PseudoRandom(blockseed)
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then
-- Set param2 (=color) of nodes which use the foliage colour palette.
lvm_used = set_foliage_palette(minp,maxp,data2,area,biomemap,{"group:foliage_palette", "group:foliage_palette_wallmounted"})
end
return lvm_used
end
-- Set param2 (=color) of nodes which use the foliage colour palette.
return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
set_foliage_palette(minp,maxp,data2,area,{"group:foliage_palette", "group:foliage_palette_wallmounted"})
end]]
local function block_fixes_water(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap = minetest.get_mapgen_object("biomemap")
local lvm_used = false
local pr = PseudoRandom(blockseed)
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then
-- Set param2 (=color) of nodes which use the water colour palette.
lvm_used = set_water_palette(minp,maxp,data2,area,biomemap,{"group:water_palette"})
end
return lvm_used
-- Set param2 (=color) of nodes which use the water colour palette.
return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
set_water_palette(minp,maxp,data2,area,{"group:water_palette"})
end
--[[ no longer necessary, we generate them with param2=3
local function block_fixes_seagrass(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local lvm_used = false
local pr = PseudoRandom(blockseed)
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then
-- Set param2 of seagrass to 3.
lvm_used = set_seagrass_param2(minp, maxp, data2, area, {"group:seagrass"})
end
return lvm_used
-- Set param2 of seagrass to 3.
return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
set_seagrass_param2(minp, maxp, data2, area, {"group:seagrass"})
end
]]
-- End block fixes:
local function end_basic(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end
local biomemap --ymin, ymax
local lvm_used = false
local pr = PseudoRandom(blockseed)
local nodes
if mg_name ~= "v6" then
nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source"})
local nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source"})
if #nodes > 0 then
lvm_used = true
for _,n in pairs(nodes) do
@ -430,51 +411,26 @@ local function end_basic(vm, data, data2, emin, emax, area, minp, maxp, blocksee
end
end
end
return true, false
lvm_used = true -- light is broken otherwise
return lvm_used, false
end
mcl_mapgen_core.register_generator("world_structure", world_structure, nil, 1, true)
mcl_mapgen_core.register_generator("end_fixes", end_basic, function(minp,maxp)
if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end
end, 9999, true)
mcl_mapgen_core.register_generator("end_fixes", end_basic, nil, 9999, true)
if mg_name ~= "v6" and mg_name ~= "singlenode" then
mcl_mapgen_core.register_generator("block_fixes_grass", block_fixes_grass, nil, 9999, true)
mcl_mapgen_core.register_generator("block_fixes_foliage", block_fixes_foliage, nil, 9999, true)
-- replaced with decoration hack: mcl_mapgen_core.register_generator("block_fixes_grass", block_fixes_grass, nil, 9999, true)
-- replaced with schema specialization: mcl_mapgen_core.register_generator("block_fixes_foliage", block_fixes_foliage, nil, 9999, true)
mcl_mapgen_core.register_generator("block_fixes_water", block_fixes_water, nil, 9999, true)
mcl_mapgen_core.register_generator("block_fixes_seagrass", block_fixes_seagrass, nil, 9999, true)
-- replaced with using param2=3 during generation mcl_mapgen_core.register_generator("block_fixes_seagrass", block_fixes_seagrass, nil, 9999, true)
end
if mg_name == "v6" then
dofile(modpath.."/v6.lua")
end
-- This should be moved to mcl_structures eventually if the dependencies can be sorted out.
mcl_mapgen_core.register_generator("structures",nil, function(minp, maxp, blockseed)
local gennotify = minetest.get_mapgen_object("gennotify")
local has = false
local poshash = minetest.hash_node_position(minp)
for _,struct in pairs(mcl_structures.registered_structures) do
local pr = PseudoRandom(blockseed + 42)
if struct.deco_id then
for _, pos in pairs(gennotify["decoration#"..struct.deco_id] or {}) do
if struct.chunk_probability == nil or (not has and pr:next(1,struct.chunk_probability) == 1 ) then
mcl_structures.place_structure(vector.offset(pos,0,1,0),struct,pr,blockseed)
has=true
end
end
elseif struct.static_pos then
for _,p in pairs(struct.static_pos) do
if vector.in_area(p,minp,maxp) then
mcl_structures.place_structure(p,struct,pr,blockseed)
end
end
end
end
return false, false, false
end, 100, true)
-- still needed?
--[[
minetest.register_lbm({
label = "Fix grass palette indexes", -- This LBM fixes any incorrect grass palette indexes.
name = "mcl_mapgen_core:fix_grass_palette_indexes",
@ -484,11 +440,12 @@ minetest.register_lbm({
local grass_palette_index = mcl_util.get_palette_indexes_from_pos(pos).grass_palette_index
if node.param2 ~= grass_palette_index then
node.param2 = grass_palette_index
minetest.set_node(pos, node)
minetest.swap_node(pos, node)
end
end
})
})]]
--[[ FIXME: not yet replaced
minetest.register_lbm({
label = "Fix foliage palette indexes", -- Set correct palette indexes of foliage in old mapblocks.
name = "mcl_mapgen_core:fix_foliage_palette_indexes",
@ -503,14 +460,14 @@ minetest.register_lbm({
minetest.place_node(vector.offset(pos, 0, 1, 0), node) -- Offset required, since otherwise the leaves sink one node for some reason.
elseif node.param2 ~= foliage_palette_index and node.name ~= "mcl_core:vine" then
node.param2 = foliage_palette_index
minetest.set_node(pos, node)
minetest.swap_node(pos, node)
elseif node.name == "mcl_core:vine" then
local biome_param2 = foliage_palette_index
local rotation_param2 = mcl_util.get_colorwallmounted_rotation(pos)
local final_param2 = (biome_param2 * 8) + rotation_param2
if node.param2 ~= final_param2 then
node.param2 = final_param2
minetest.set_node(pos, node)
minetest.swap_node(pos, node)
end
end
end
@ -525,7 +482,7 @@ minetest.register_lbm({
local water_palette_index = mcl_util.get_palette_indexes_from_pos(pos).water_palette_index
if node.param2 ~= water_palette_index then
node.param2 = water_palette_index
minetest.set_node(pos, node)
minetest.swap_node(pos, node)
end
end
})
@ -538,15 +495,19 @@ minetest.register_lbm({
action = function(pos, node)
if node.param2 ~= 3 then
node.param2 = 3
minetest.set_node(pos, node)
minetest.swap_node(pos, node)
end
end
})
]]--
-- Can we get rid of this ugly hack?
--[[
-- We go outside x and y for where trees are placed next to a biome that has already been generated.
-- We go above maxp.y because trees can often get placed close to the top of a generated area and folliage may not
-- be coloured correctly.
local function fix_folliage_missed (minp, maxp)
local function fix_foliage_missed(minp, maxp, blockseed)
if maxp.y < 0 then return end
local pos1, pos2 = vector.offset(minp, -6, 0, -6), vector.offset(maxp, 6, 14, 6)
local foliage = minetest.find_nodes_in_area(pos1, pos2, {"group:foliage_palette", "group:foliage_palette_wallmounted"})
for _, fpos in pairs(foliage) do
@ -554,22 +515,19 @@ local function fix_folliage_missed (minp, maxp)
local foliage_palette_index = mcl_util.get_palette_indexes_from_pos(fpos).foliage_palette_index
if fnode.param2 ~= foliage_palette_index and fnode.name ~= "mcl_core:vine" then
fnode.param2 = foliage_palette_index
minetest.set_node(fpos, fnode)
minetest.swap_node(fpos, fnode)
elseif fnode.name == "mcl_core:vine" then
local biome_param2 = foliage_palette_index
local rotation_param2 = mcl_util.get_colorwallmounted_rotation(fpos)
local final_param2 = (biome_param2 * 8) + rotation_param2
if fnode.param2 ~= final_param2 then
fnode.param2 = final_param2
minetest.set_node(fpos, fnode)
minetest.swap_node(fpos, fnode)
end
end
end
end
minetest.register_on_generated(function(minp, maxp, blockseed) -- Set correct palette indexes of missed foliage.
if maxp.y < 0 then
return
end
fix_folliage_missed (minp, maxp)
end)
fix_foliage_missed(minp, maxp)
end)]]

View File

@ -136,7 +136,7 @@ minetest.register_ore({
clust_num_ores = 58,
clust_size = 7,
y_min = deepslate_min,
y_max = deepslate_max,
y_max = deepslate_max,
noise_params = {
offset = 0,
scale = 1,
@ -450,14 +450,15 @@ if minetest.settings:get_bool("mcl_generate_ores", true) then
ore_type = "scatter",
ore = "mcl_nether:ancient_debris",
wherein = ancient_debris_wherein,
clust_scarcity = 25000, -- 0.004% chance
clust_scarcity = 15000,
-- in MC it's 0.004% chance (~= scarcity 25000) but reports and experiments show that ancient debris is unreasonably hard to find in survival with that value
clust_num_ores = 3,
clust_size = 3,
y_min = mcl_vars.mg_nether_min + 8,
y_max = mcl_vars.mg_nether_min + 22,
})
-- Rare spawn (below)
-- Rare spawn (below)
minetest.register_ore({
ore_type = "scatter",
ore = "mcl_nether:ancient_debris",

View File

@ -45,6 +45,7 @@ local function register_mgv6_decorations()
-- Doubletall grass
minetest.register_decoration({
priority = 1500,
deco_type = "schematic",
schematic = {
size = { x=1, y=3, z=1 },
@ -81,6 +82,7 @@ local function register_mgv6_decorations()
},
-- v6 hack: This makes sure large ferns only appear in jungles
spawn_by = { "mcl_core:jungletree", "mcl_flowers:fern" },
priority = 1510, -- larger than fern
num_spawn_by = 1,
place_on = {"group:grass_block_no_snow"},
@ -192,6 +194,7 @@ local function register_mgv6_decorations()
},
-- Small trick to make sure melon spawn in jungles
spawn_by = { "mcl_core:jungletree", "mcl_flowers:fern" },
priority = 1510, -- larger than fern
num_spawn_by = 1,
y_min = 1,
y_max = 40,
@ -214,6 +217,7 @@ local function register_mgv6_decorations()
y_min = 1,
y_max = mcl_vars.overworld_max,
decoration = "mcl_flowers:tallgrass",
priority = 1500,
})
minetest.register_decoration({
deco_type = "simple",
@ -230,6 +234,7 @@ local function register_mgv6_decorations()
y_min = 1,
y_max = mcl_vars.overworld_max,
decoration = "mcl_flowers:tallgrass",
priority = 1500,
})
-- Seagrass and kelp
@ -256,6 +261,7 @@ local function register_mgv6_decorations()
y_min = mcl_vars.overworld_min,
y_max = 0,
decoration = "mcl_ocean:seagrass_"..mat,
priority = 1500,
})
minetest.register_decoration({
deco_type = "simple",
@ -276,6 +282,7 @@ local function register_mgv6_decorations()
y_min = mcl_vars.overworld_min,
y_max = -5,
decoration = "mcl_ocean:seagrass_"..mat,
priority = 1500,
})
minetest.register_decoration({
@ -356,6 +363,7 @@ local function register_mgv6_decorations()
y_min = 1,
y_max = mcl_vars.overworld_max,
decoration = "mcl_flowers:tallgrass",
priority = 1500,
})
local mushrooms = {"mcl_mushrooms:mushroom_red", "mcl_mushrooms:mushroom_brown"}
@ -501,7 +509,7 @@ local function generate_mgv6_structures()
local surface = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:snowblock")
local surface2 = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:dirt_with_grass_snow")
if #surface + #surface2 >= 63 then
mcl_structures.call_struct(p, "igloo", nil, pr)
vl_structures.call_struct(p, "igloo", nil, pr)
chunk_has_igloo = true
end
end
@ -520,7 +528,7 @@ local function generate_mgv6_structures()
local nodes = minetest.find_nodes_in_area(p1, p2, {"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"})
if #nodes >= 100 then -- >= 80%
mcl_structures.call_struct(p1, "fossil", nil, pr)
vl_structures.call_struct(p1, "fossil", nil, pr)
end
end
end
@ -557,15 +565,12 @@ local function generate_mgv6_structures()
if #free_nodes >= ((size.x+1)*(size.y+1)*(size.z+1)) then
local place = {x=p.x, y=WITCH_HUT_HEIGHT-1, z=p.z}
-- FIXME: For some mysterious reason (black magic?) this
-- function does sometimes NOT spawn the witch hut. One can only see the
-- oak wood nodes in the water, but no hut. :-/
mcl_structures.place_structure(place,mcl_structures.registered_structures["witch_hut"],pr)
vl_structures.place_structure(place,vl_structures.registered_structures["witch_hut"],pr)
local function place_tree_if_free(pos, prev_result)
local nn = minetest.get_node(pos).name
if nn == "mcl_flowers:waterlily" or nn == "mcl_core:water_source" or nn == "mcl_core:water_flowing" or nn == "air" then
minetest.set_node(pos, {name="mcl_core:tree", param2=0})
minetest.swap_node(pos, {name="mcl_core:tree", param2=0})
return prev_result
else
return false
@ -629,7 +634,7 @@ local function generate_mgv6_structures()
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}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"})
if #surface >= 9 and #spruce_collisions == 0 then
mcl_structures.place_structure(p,mcl_structures.registered_structures["ice_spike_large"],pr)
vl_structures.place_structure(p,vl_structures.registered_structures["ice_spike_large"],pr)
end
elseif spike < 100 then
-- Check surface
@ -640,7 +645,7 @@ local function generate_mgv6_structures()
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}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"})
if #surface >= 25 and #spruce_collisions == 0 then
mcl_structures.place_structure(p,mcl_structures.registered_structures["ice_spike_small"],pr)
vl_structures.place_structure(p,vl_structures.registered_structures["ice_spike_small"],pr)
end
end
end
@ -671,9 +676,9 @@ local function generate_underground_mushrooms(minp, maxp, seed)
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"})
minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end
end
end
@ -710,14 +715,14 @@ local function generate_nether_decorations(minp, maxp, seed)
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"})
minetest.swap_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"})
minetest.swap_node(bpos, {name = "mcl_fire:eternal_fire"})
end
end)
@ -728,9 +733,9 @@ local function generate_nether_decorations(minp, maxp, seed)
if bpos.y > mcl_vars.mg_lava_nether_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"})
minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end
end
end)
@ -739,7 +744,7 @@ local function generate_nether_decorations(minp, maxp, seed)
-- TODO: Spawn in Nether fortresses
special_deco(ssand, function(bpos)
if pr_nether:next(1, nether_wart_chance) == 1 then
minetest.set_node(bpos, {name = "mcl_nether:nether_wart"})
minetest.swap_node(bpos, {name = "mcl_nether:nether_wart"})
end
end)
end

View File

@ -1,25 +1,20 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local BLAZE_SPAWNER_MAX_LIGHT = 11
mcl_structures.register_structure("nether_outpost",{
vl_structures.register_structure("nether_outpost",{
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"},
fill_ratio = 0.01,
chunk_probability = 900,
flags = "all_floors",
chunk_probability = 23,
flags = "place_center_x, place_center_y, all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
sidelen = 24,
solid_ground = true,
make_foundation = true,
prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear = true, clear_top = 4 },
y_min = mcl_vars.mg_lava_nether_max - 1,
y_max = mcl_vars.mg_nether_max - 30,
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
y_offset = 0,
after_place = function(pos)
local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"})
after_place = function(pos,def,pr,p1,p2)
local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
if not sp[1] then return end
mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0)
end
@ -30,80 +25,101 @@ local nbridges = {
modpath.."/schematics/mcl_nether_fortresses_nether_bridge_3.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bridge_4.mts",
}
mcl_structures.register_structure("nether_bridge",{
place_on = {"mcl_nether:nether_lava_source","mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand","mcl_core:bedrock"},
fill_ratio = 0.01,
chunk_probability = 500,
flags = "all_floors",
sidelen = 38,
solid_ground = false,
make_foundation = false,
y_min = mcl_vars.mg_nether_min - 4,
y_max = mcl_vars.mg_lava_nether_max - 20,
vl_structures.register_structure("nether_bridge",{
place_on = {"mcl_nether:nether_lava_source","mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"},
chunk_probability = 8, -- because of the y restriction these are quite rare
flags = "place_center_x, place_center_y, all_floors",
prepare = { tolerance = 50, padding = -1, corners = 0, clear_bottom = 8, clear_top = 6 }, -- asymmetric padding would be nice to have
force_placement = true,
y_min = mcl_vars.mg_lava_nether_max,
y_max = mcl_vars.mg_lava_nether_max + 25, -- otherwise, we may see some very long legs
filenames = nbridges,
y_offset = function(pr) return pr:next(15,20) end,
after_place = function(pos,def,pr)
local p1 = vector.offset(pos,-14,0,-14)
local p2 = vector.offset(pos,14,24,14)
mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
end
})
mcl_structures.register_structure("nether_outpost_with_bridges",{
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand","mcl_nether:nether_lava_source"},
fill_ratio = 0.01,
chunk_probability = 1300,
flags = "all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
sidelen = 24,
solid_ground = true,
make_foundation = true,
y_min = mcl_vars.mg_lava_nether_max - 1,
y_max = mcl_vars.mg_nether_max - 30,
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
daughters = {{
files = { nbridges[1] },
pos = vector.new(0,-2,-24),
rot = 180,
},
{
files = { nbridges[1] },
pos = vector.new(0,-2,24),
rot = 0,
},
{
files = { nbridges[1] },
pos = vector.new(-24,-2,0),
rot = 270,
},
{
files = { nbridges[1] },
pos = vector.new(24,-2,0),
rot = 90,
},
},
after_place = function(pos,def,pr)
local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"})
if not sp[1] then return end
mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0)
local legs = minetest.find_nodes_in_area(vector.offset(pos,-45,-2,-45),vector.offset(pos,45,0,45), "mcl_nether:nether_brick")
y_offset = function(pr) return pr:next(-8, -5) end,
after_place = function(pos,def,pr,p1,p2)
vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
-- p1.y is not a typo, we want to lowest level only
local legs = minetest.find_nodes_in_area(vector.new(p1.x,p1.y,p1.z),vector.new(p2.x,p1.y,p2.z), "mcl_nether:nether_brick")
local bricks = {}
-- TODO: port leg generation to voxel manipulators?
for _,leg in pairs(legs) do
while minetest.get_item_group(mcl_vars.get_node(vector.offset(leg,0,-1,0), true, 333333).name, "solid") == 0 do
while true do
leg = vector.offset(leg,0,-1,0)
local nodename = minetest.get_node(leg).name
if nodename == "ignore" then break end
if nodename ~= "air" and nodename ~= "mcl_core:lava_source" and minetest.get_item_group(nodename, "solid") ~= 0 then break end
table.insert(bricks,leg)
end
end
minetest.bulk_set_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
local p1 = vector.offset(pos,-45,13,-45)
local p2 = vector.offset(pos,45,13,45)
mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
minetest.bulk_swap_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
end
},true)
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure("nether_outpost_with_bridges",{
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand","mcl_nether:nether_lava_source"},
chunk_probability = 10, -- because of the y restriction, it will still be rare
flags = "place_center_x, place_center_y, all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear_top = 3 },
y_min = mcl_vars.mg_lava_nether_max - 1,
y_max = mcl_vars.mg_lava_nether_max + 40,
-- todo: spawn_by a lot of air?
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
emerge_padding = { vector.new(-38,-8,-38), vector.new(38,0,38) },
daughters = {
{
filenames = { nbridges[1], nbridges[2] },
pos = vector.new(0,-3,24),
rotation= 0,
no_level = true,
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
},
{
filenames = { nbridges[1], nbridges[2] },
pos = vector.new(24,-3,0),
rotation = 90,
no_level = true,
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
},
{
filenames = { nbridges[1], nbridges[2] },
pos = vector.new(0,-3,-25),
rotation = 180,
no_level = true,
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
},
{
filenames = { nbridges[1], nbridges[2] },
pos = vector.new(-25,-3,0),
rotation = 270,
no_level = true,
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
},
},
after_place = function(pos,def,pr,p1,p2)
local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
if not sp[1] then return end
mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0)
-- the -3 offset needs to be carefully aligned with the bridges above
local legs = minetest.find_nodes_in_area(vector.offset(pos,-45,-3,-45),vector.offset(pos,45,-3,45), "mcl_nether:nether_brick")
local bricks = {}
-- TODO: port leg generation to voxel manipulators?
for _,leg in pairs(legs) do
while true do
leg = vector.offset(leg,0,-1,0)
local nodename = minetest.get_node(leg).name
if nodename == "ignore" then break end
if nodename ~= "air" and nodename ~= "mcl_core:lava_source" and minetest.get_item_group(nodename, "solid") ~= 0 then break end
table.insert(bricks,leg)
end
end
minetest.bulk_swap_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
local p1, p2 = vector.offset(pos,-45,12,-45), vector.offset(pos,45,22,45)
vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
end
})
vl_structures.register_structure_spawn({
name = "mobs_mc:witherskeleton",
y_min = mcl_vars.mg_lava_nether_max,
y_max = mcl_vars.mg_nether_max,
@ -113,15 +129,12 @@ mcl_structures.register_structure_spawn({
spawnon = { "mcl_blackstone:blackstone_chiseled_polished" },
})
mcl_structures.register_structure("nether_bulwark",{
vl_structures.register_structure("nether_bulwark",{
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"},
fill_ratio = 0.01,
chunk_probability = 900,
flags = "all_floors",
chunk_probability = 29,
flags = "place_center_x, place_center_y, all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest"},
sidelen = 36,
solid_ground = true,
make_foundation = true,
prepare = { tolerance=10, padding=4, corners=5, foundation=-5, clear_top=0 },
y_min = mcl_vars.mg_lava_nether_max - 1,
y_max = mcl_vars.mg_nether_max - 30,
filenames = {
@ -131,23 +144,24 @@ mcl_structures.register_structure("nether_bulwark",{
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_4.mts",
},
daughters = {{
files = {
filenames = {
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_1.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_2.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_3.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_4.mts",
},
pos = vector.new(0,0,0),
pos = vector.new(0,1,0),
rotation = "random",
force_placement = true,
prepare = { tolerance = -1, foundation = false, clear = false },
},
},
y_offset = 0,
construct_nodes = {"group:wall"},
after_place = function(pos,def,pr)
local p1 = vector.offset(pos,-14,0,-14)
local p2 = vector.offset(pos,14,24,14)
mcl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5)
mcl_structures.spawn_mobs("mobs_mc:piglin_brute",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr)
mcl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4)
after_place = function(pos,def,pr,p1,p2)
vl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5)
vl_structures.spawn_mobs("mobs_mc:piglin_brute",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr)
vl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4)
end,
loot = {
["mcl_chests:chest_small" ] ={
@ -194,22 +208,22 @@ mcl_structures.register_structure("nether_bulwark",{
},
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure_spawn({
name = "mobs_mc:piglin",
y_min = mcl_vars.mg_nether_min,
y_max = mcl_vars.mg_nether_max,
chance = 10,
interval = 60,
limit = 9,
spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},
spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"},
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure_spawn({
name = "mobs_mc:piglin_brute",
y_min = mcl_vars.mg_nether_min,
y_max = mcl_vars.mg_nether_max,
chance = 20,
interval = 60,
limit = 4,
spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},
spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"},
})

View File

@ -83,21 +83,20 @@ local function generate_strongholds(minp, maxp, blockseed)
pos.z = maxp.z - 7
end
--mcl_structures.call_struct(pos, "end_portal_shrine", nil, pr)
--vl_structures.call_struct(pos, "end_portal_shrine", nil, pr)
strongholds[s].generated = true
end
end
end
end
mcl_structures.register_structure("end_shrine",{
vl_structures.register_structure("end_shrine",{
static_pos = init_strongholds(),
prepare = { tolerance = -1, foundation = false, clear = false },
filenames = {
minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_portal_room_simple.mts"
},
after_place = function(pos,def,pr,blockseed,p1,p2,size,rotation)
local p1 = vector.subtract(pos,size)
local p2 = vector.add(pos,size)
after_place = function(pos, def, pr, p1, p2, size, rotation)
local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner")
for s=1, #spawners do
--local meta = minetest.get_meta(spawners[s])

View File

@ -1,38 +1,7 @@
# mcl_structures
Structure placement API for MCL2.
VoxeLibre structures
## mcl_structures.register_structure(name,structure definition,nospawn)
If nospawn is truthy the structure will not be placed by mapgen and the decoration parameters can be omitted. This is intended for secondary structures the placement of which gets triggered by the placement of other structures. It can also be used to register testing structures so they can be used with /spawnstruct.
This module contains standard VoxeLibre structures such as nether portals.
### structure definition
{
fill_ratio = OR noise = {},
biomes = {},
y_min =,
y_max =,
place_on = {},
spawn_by = {},
num_spawn_by =,
flags = (default: "place_center_x, place_center_z, force_placement")
(same as decoration def)
y_offset =, --can be a number or a function returning a number
filenames = {} OR place_func = function(pos,def,pr)
-- filenames can be a list of any schematics accepted by mcl_structures.place_schematic / minetest.place_schematic
on_place = function(pos,def,pr) end,
-- called before placement. denies placement when returning falsy.
after_place = function(pos,def,pr)
-- executed after successful placement
sidelen = int, --length of one side of the structure. used for foundations.
solid_ground = bool, -- structure requires solid ground
make_foundation = bool, -- a foundation is automatically built for the structure. needs the sidelen param
loot = ,
--a table of loot tables for mcl_loot indexed by node names
-- e.g. { ["mcl_chests:chest_small"] = {loot},... }
}
## mcl_structures.registered_structures
Table of the registered structure defintions indexed by name.
The API has been redesigned and moved to the vl_structures module.
## mcl_structures.place_structure(pos, def, pr)
Places a structure using the mapgen placement function
## mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)

View File

@ -1,393 +0,0 @@
mcl_structures.registered_structures = {}
local disabled_structures = minetest.settings:get("mcl_disabled_structures")
if disabled_structures then disabled_structures = disabled_structures:split(",")
else disabled_structures = {} end
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local mob_cap_player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75
local mob_cap_animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10
local logging = minetest.settings:get_bool("mcl_logging_structures",true)
local mg_name = minetest.get_mapgen_setting("mg_name")
local rotations = {
"0",
"90",
"180",
"270"
}
function mcl_structures.is_disabled(structname)
return table.indexof(disabled_structures,structname) ~= -1
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)
end
end
function mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
if type(schematic) ~= "table" and not mcl_util.file_exists(schematic) then
minetest.log("warning","[mcl_structures] schematic file "..tostring(schematic).." does not exist.")
return end
local s = loadstring(minetest.serialize_schematic(schematic, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")()
if s and s.size then
local x, z = s.size.x, s.size.z
if rotation then
if rotation == "random" and pr then
rotation = rotations[pr:next(1,#rotations)]
end
if rotation == "random" then
x = math.max(x, z)
z = x
elseif rotation == "90" or rotation == "270" then
x, z = z, x
end
end
local p1 = {x=pos.x , y=pos.y , z=pos.z }
local p2 = {x=pos.x+x-1, y=pos.y+s.size.y-1, z=pos.z+z-1}
minetest.log("verbose", "[mcl_structures] size=" ..minetest.pos_to_string(s.size) .. ", rotation=" .. tostring(rotation) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
local param = {pos=vector.new(pos), schematic=s, rotation=rotation, replacements=replacements, force_placement=force_placement, flags=flags, p1=p1, p2=p2, after_placement_callback = after_placement_callback, size=vector.new(s.size), pr=pr, callback_param=callback_param}
minetest.emerge_area(p1, p2, ecb_place, param)
return true
end
end
function mcl_structures.get_struct(file)
local localfile = modpath.."/schematics/"..file
local file, errorload = io.open(localfile, "rb")
if errorload then
minetest.log("error", "[mcl_structures] Could not open this struct: "..localfile)
return nil
end
local allnode = file:read("*a")
file:close()
return allnode
end
-- Call on_construct on pos.
-- Useful to init chests from formspec.
local function init_node_construct(pos)
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
if def and def.on_construct then
def.on_construct(pos)
return true
end
return false
end
mcl_structures.init_node_construct = init_node_construct
function mcl_structures.fill_chests(p1,p2,loot,pr)
for it,lt in pairs(loot) do
local nodes = minetest.find_nodes_in_area(p1, p2, it)
for _,p in pairs(nodes) do
local lootitems = mcl_loot.get_multi_loot(lt, pr)
mcl_structures.init_node_construct(p)
local meta = minetest.get_meta(p)
local inv = meta:get_inventory()
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
end
end
end
local function generate_loot(pos, def, pr)
local hl = def.sidelen
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
if def.loot then mcl_structures.fill_chests(p1,p2,def.loot,pr) end
end
function mcl_structures.construct_nodes(p1,p2,nodes)
local nn=minetest.find_nodes_in_area(p1,p2,nodes)
for _,p in pairs(nn) do
mcl_structures.init_node_construct(p)
end
end
local function construct_nodes(pos,def,pr)
return mcl_structures.construct_nodes(vector.offset(pos,-def.sidelen/2,0,-def.sidelen/2),vector.offset(pos,def.sidelen/2,def.sidelen,def.sidelen/2),def.construct_nodes)
end
function mcl_structures.find_lowest_y(pp)
local y = 31000
for _,p in pairs(pp) do
if p.y < y then y = p.y end
end
return y
end
function mcl_structures.find_highest_y(pp)
local y = -31000
for _,p in pairs(pp) do
if p.y > y then y = p.y end
end
return y
end
local function smooth_cube(nn,pos,plane,amnt)
local r = {}
local amnt = amnt or 9
table.sort(nn,function(a, b)
if false or plane then
return vector.distance(vector.new(pos.x,0,pos.z), vector.new(a.x,0,a.z)) < vector.distance(vector.new(pos.x,0,pos.z), vector.new(b.x,0,b.z))
else
return vector.distance(pos, a) < vector.distance(pos, b)
end
end)
for i=1,math.max(1,#nn-amnt) do table.insert(r,nn[i]) end
return r
end
local function find_ground(pos,nn,gn)
local r = 0
for _,v in pairs(nn) do
local p=vector.new(v)
repeat
local n = minetest.get_node(p).name
p = vector.offset(p,0,-1,0)
until not n or n == "mcl_core:bedrock" or n == "ignore" or n == gn
--minetest.log(tostring(pos.y - p.y))
if pos.y - p.y > r then r = pos.y - p.y end
end
return r
end
local function get_foundation_nodes(ground_p1,ground_p2,pos,sidelen,node_stone)
local replace = {"air","group:liquid","mcl_core:snow","group:tree","group:leaves","group:plant","grass_block","group:dirt"}
local depth = find_ground(pos,minetest.find_nodes_in_area(ground_p1,ground_p2,replace),node_stone)
local nn = smooth_cube(minetest.find_nodes_in_area(vector.offset(ground_p1,0,-1,0),vector.offset(ground_p2,0,-depth,0),replace),vector.offset(pos,0,-depth,0),true,sidelen * 64)
local stone = {}
local filler = {}
local top = {}
local dust = {}
for l,v in pairs(nn) do
if v.y == ground_p1.y - 1 then
table.insert(filler,v)
table.insert(top,vector.offset(v,0,1,0))
table.insert(dust,vector.offset(v,0,2,0))
elseif v.y < ground_p1.y -1 and v.y > ground_p2.y -4 then table.insert(filler,v)
elseif v.y < ground_p2.y - 3 and v.y > ground_p2.y -5 then
if math.random(3) == 1 then
table.insert(filler,v)
else
table.insert(stone,v)
end
else
table.insert(stone,v)
end
end
return stone,filler,top,dust
end
local function foundation(ground_p1,ground_p2,pos,sidelen)
local node_stone = "mcl_core:stone"
local node_filler = "mcl_core:dirt"
local node_top = "mcl_core:dirt_with_grass" or minetest.get_node(ground_p1).name
local node_dust = nil
if mg_name ~= "v6" then
local b = minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)]
--minetest.log(dump(b.node_top))
if b then
if b.node_top then node_top = b.node_top end
if b.node_filler then node_filler = b.node_filler end
if b.node_stone then node_stone = b.node_stone end
if b.node_dust then node_dust = b.node_dust end
end
end
local stone,filler,top,dust = get_foundation_nodes(ground_p1,ground_p2,pos,sidelen,node_stone)
minetest.bulk_set_node(top,{name=node_top},node_stone)
if node_dust then
minetest.bulk_set_node(dust,{name=node_dust})
end
minetest.bulk_set_node(filler,{name=node_filler})
minetest.bulk_set_node(stone,{name=node_stone})
end
function mcl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water)
n = n or 1
local sp = {}
if water then
local nn = minetest.find_nodes_in_area(p1,p2,spawnon)
for k,v in pairs(nn) do
if minetest.get_item_group(minetest.get_node(vector.offset(v,0,1,0)).name,"water") > 0 then
table.insert(sp,v)
end
end
else
sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon)
end
table.shuffle(sp)
local count = 0
local mob_def = minetest.registered_entities[mob]
local enabled = (not peaceful) or (mob_def and mob_def.spawn_class ~= "hostile")
for _,node in pairs(sp) do
if enabled and count < n and minetest.add_entity(vector.offset(node, 0, 1, 0), mob) then
count = count + 1
end
minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode!
end
end
function mcl_structures.place_structure(pos, def, pr, blockseed, rot)
if not def then return end
if not rot then rot = "random" end
local log_enabled = logging and not def.terrain_feature
local y_offset = 0
if type(def.y_offset) == "function" then
y_offset = def.y_offset(pr)
elseif def.y_offset then
y_offset = def.y_offset
end
local pp = vector.offset(pos,0,y_offset,0)
if def.solid_ground and def.sidelen then
local ground_p1 = vector.offset(pos,-def.sidelen/2,-1,-def.sidelen/2)
local ground_p2 = vector.offset(pos,def.sidelen/2,-1,def.sidelen/2)
local solid = minetest.find_nodes_in_area(ground_p1,ground_p2,{"group:solid"})
if #solid < ( def.sidelen * def.sidelen ) then
if def.make_foundation then
foundation(vector.offset(pos,-def.sidelen/2 - 3,-1,-def.sidelen/2 - 3),vector.offset(pos,def.sidelen/2 + 3,-1,def.sidelen/2 + 3),pos,def.sidelen)
else
if log_enabled then
minetest.log("warning","[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pp).." not placed. No solid ground.")
end
return false
end
end
end
if def.on_place and not def.on_place(pos,def,pr,blockseed) then
if log_enabled then
minetest.log("warning","[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pp).." not placed. Conditions not satisfied.")
end
return false
end
if def.filenames then
if #def.filenames <= 0 then return false end
local r = pr:next(1,#def.filenames)
local file = def.filenames[r]
if file then
local rot = rotations[pr:next(1,#rotations)]
local ap = function(pos,def,pr,blockseed) end
if def.daughters then
ap = function(pos,def,pr,blockseed)
for _,d in pairs(def.daughters) do
local p = vector.add(pos,d.pos)
local rot = d.rot or 0
mcl_structures.place_schematic(p, d.files[pr:next(1,#d.files)], rot, nil, true, "place_center_x,place_center_z",function()
if def.loot then generate_loot(pp,def,pr,blockseed) end
if def.construct_nodes then construct_nodes(pp,def,pr,blockseed) end
if def.after_place then
def.after_place(pos,def,pr)
end
end,pr)
end
end
elseif def.after_place then
ap = def.after_place
end
mcl_structures.place_schematic(pp, file, rot, def.replacements, true, "place_center_x,place_center_z",function(p1, p2, size, rotation)
if not def.daughters then
if def.loot then generate_loot(pp,def,pr,blockseed) end
if def.construct_nodes then construct_nodes(pp,def,pr,blockseed) end
end
return ap(pp,def,pr,blockseed,p1,p2,size,rotation)
end,pr)
if log_enabled then
minetest.log("action","[mcl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
end
return true
end
elseif def.place_func and def.place_func(pp,def,pr,blockseed) then
if not def.after_place or ( def.after_place and def.after_place(pp,def,pr,blockseed) ) then
if def.loot then generate_loot(pp,def,pr,blockseed) end
if def.construct_nodes then construct_nodes(pp,def,pr,blockseed) end
if log_enabled then
minetest.log("action","[mcl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
end
return true
end
end
if log_enabled then
minetest.log("warning","[mcl_structures] placing "..def.name.." failed at "..minetest.pos_to_string(pos))
end
end
local EMPTY_SCHEMATIC = { size = {x = 0, y = 0, z = 0}, data = { } }
function mcl_structures.register_structure(name,def,nospawn) --nospawn means it will not be placed by mapgen decoration mechanism
if mcl_structures.is_disabled(name) then return end
flags = def.flags or "place_center_x, place_center_z, force_placement"
def.name = name
if not nospawn and def.place_on then
minetest.register_on_mods_loaded(function() --make sure all previous decorations and biomes have been registered
def.deco = minetest.register_decoration({
name = "mcl_structures:deco_"..name,
deco_type = "schematic",
schematic = EMPTY_SCHEMATIC,
place_on = def.place_on,
spawn_by = def.spawn_by,
num_spawn_by = def.num_spawn_by,
sidelen = 80,
fill_ratio = def.fill_ratio,
noise_params = def.noise_params,
flags = flags,
biomes = def.biomes,
y_max = def.y_max,
y_min = def.y_min
})
def.deco_id = minetest.get_decoration_id("mcl_structures:deco_"..name)
minetest.set_gen_notify({decoration=true}, { def.deco_id })
--catching of gennotify happens in mcl_mapgen_core
end)
end
mcl_structures.registered_structures[name] = def
end
local structure_spawns = {}
function mcl_structures.register_structure_spawn(def)
--name,y_min,y_max,spawnon,biomes,chance,interval,limit
minetest.register_abm({
label = "Spawn "..def.name,
nodenames = def.spawnon,
min_y = def.y_min or -31000,
max_y = def.y_max or 31000,
interval = def.interval or 60,
chance = def.chance or 5,
action = function(pos, node, active_object_count, active_object_count_wider)
local limit = def.limit or 7
if active_object_count_wider > limit + mob_cap_animal then return end
if active_object_count_wider > mob_cap_player then return end
local p = vector.offset(pos,0,1,0)
local pname = minetest.get_node(p).name
if def.type_of_spawning == "water" then
if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end
else
if pname ~= "air" then return end
end
if minetest.get_meta(pos):get_string("spawnblock") == "" then return end
if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then
if table.indexof(def.biomes,minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then
return
end
end
local mobdef = minetest.registered_entities[def.name]
if mobdef.can_spawn and not mobdef.can_spawn(p) then return end
minetest.add_entity(p,def.name)
end,
})
end

View File

@ -0,0 +1,71 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("campsite",{
place_on = {"group:grass_block"},
flags = "place_center_x, place_center_z",
chunk_probability = 50,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
prepare = { tolerance = 1, foundation = -2, clear_top = 1, padding = 1, corners = 1 }, -- low tolerance, perform little terraforming
filenames = {
modpath.."/schematics/campsite_1.mts"
},
loot = {
["mcl_chests:trapped_chest_small"] = {
{
stacks_min = 1,
stacks_max = 3,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_farming:wheat_item", weight = 7, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_farming:carrot_item", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_farming:potato_item_poison", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_farming:potato_item", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_bamboo:bamboo", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
},
{
stacks_min = 1,
stacks_max = 2,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 90, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_core:iron_nugget", weight = 50, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:emerald", weight = 40, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:lapis", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:gold_ingot", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
}
},{
stacks_min = 1,
stacks_max = 1,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:paper", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_mobitems:feather", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 5, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
}
},
}
}
})

View File

@ -1,16 +1,12 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local function temple_placement_callback(pos,def, pr)
local hl = def.sidelen / 2
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
local function temple_placement_callback(pos,def,pr,p1,p2)
-- 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})
local node_below = minetest.get_node(vector.offset(pos,0,-1,0))
if node_below and node_below.name == "mcl_core:sandstone" then
minetest.swap_node(pos, {name="air"})
end
@ -32,15 +28,12 @@ local function temple_placement_callback(pos,def, pr)
end
end
mcl_structures.register_structure("desert_temple",{
vl_structures.register_structure("desert_temple",{
place_on = {"group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
solid_ground = true,
make_foundation = true,
sidelen = 18,
y_offset = -12,
chunk_probability = 300,
prepare = { tolerance = 10, padding = 3, corners = 3, foundation = true, clear = false },
chunk_probability = 18,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
biomes = { "Desert" },

View File

@ -0,0 +1,14 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("desert_well",{
place_on = {"group:sand"},
flags = "place_center_x, place_center_z",
chunk_probability = 15,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
y_offset = -2,
biomes = { "Desert" },
filenames = { modpath.."/schematics/mcl_structures_desert_well.mts" },
})

View File

@ -1,42 +1,36 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local spawnon = {"mcl_end:purpur_block"}
local function spawn_shulkers(pos,def,pr)
local p1 = vector.offset(pos,-def.sidelen/2,-1,-def.sidelen/2)
local p2 = vector.offset(pos,def.sidelen/2,def.sidelen,def.sidelen/2)
mcl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1)
local guard = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"})
if guard then
minetest.add_entity(vector.offset(guard,0,-1.5,0),"mobs_mc:shulker")
local function spawn_shulkers(pos,def,pr,p1,p2)
vl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1)
local guard = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"})
if #guard > 0 then
minetest.add_entity(vector.offset(guard[1],0,-1.5,0),"mobs_mc:shulker")
end
end
mcl_structures.register_structure("end_shipwreck",{
vl_structures.register_structure("end_shipwreck",{
place_on = {"mcl_end:end_stone"},
fill_ratio = 0.001,
flags = "place_center_x, place_center_z, all_floors",
y_offset = function(pr) return pr:next(-50,-20) end,
chunk_probability = 800,
y_offset = function(pr) return pr:next(15,40) end,
force_placement = false,
prepare = { foundation = false, clear = false },
chunk_probability = 25,
--y_max = mcl_vars.mg_end_max,
--y_min = mcl_vars.mg_end_min -100,
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
sidelen = 32,
filenames = {
modpath.."/schematics/mcl_structures_end_shipwreck_1.mts",
},
construct_nodes = {"mcl_chests:ender_chest_small","mcl_chests:ender_chest","mcl_brewing:stand_000","mcl_chests:violet_shulker_box_small"},
after_place = function(pos,def,pr)
local fr = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"})
if fr then
if mcl_itemframes then
mcl_itemframes.update_item_entity(fr,minetest.get_node(fr))
end
after_place = function(pos,def,pr,p1,p2)
local fr = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"})
if #fr > 0 and mcl_itemframes then
mcl_itemframes.update_item_entity(fr[1],minetest.get_node(fr[1]))
end
return spawn_shulkers(pos,def,pr)
return spawn_shulkers(pos,def,pr,p1,p2)
end,
loot = {
[ "mcl_itemframes:item_frame" ] ={{
@ -53,7 +47,7 @@ mcl_structures.register_structure("end_shipwreck",{
{ itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_farming:beetroot_seeds", weight = 16, amount_min = 1, amount_max=10 },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
--{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 }, --FIXME BAMBOO
{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_mobitems:saddle", weight = 3, },
@ -86,16 +80,16 @@ mcl_structures.register_structure("end_shipwreck",{
}
})
mcl_structures.register_structure("end_boat",{
vl_structures.register_structure("end_boat",{
place_on = {"mcl_end:end_stone"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z, all_floors",
y_offset = function(pr) return pr:next(15,30) end,
chunk_probability = 900,
y_offset = function(pr) return pr:next(10,20) end,
force_placement = false,
prepare = { foundation = false, clear = false },
chunk_probability = 10,
--y_max = mcl_vars.mg_end_max,
--y_min = mcl_vars.mg_end_min -100,
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
sidelen = 20,
filenames = {
modpath.."/schematics/mcl_structures_end_boat.mts",
},
@ -134,7 +128,7 @@ mcl_structures.register_structure("end_boat",{
}
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure_spawn({
name = "mobs_mc:shulker",
y_min = mcl_vars.mg_end_min,
y_max = mcl_vars.mg_end_max,
@ -143,3 +137,52 @@ mcl_structures.register_structure_spawn({
limit = 6,
spawnon = spawnon,
})
vl_structures.register_structure("small_end_city",{
place_on = {"mcl_end:end_stone"},
flags = "place_center_x, place_center_z, all_floors",
y_offset = 0,
chunk_probability = 30,
prepare = { foundation = -1, tolerance = 15 },
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
sidelen = 20,
filenames = {
modpath.."/schematics/mcl_structures_end_city_simple.mts",
},
after_place = function(pos,def,pr,p1,p2)
-- not on roof
vl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,vector.offset(p2,0,-2,0),pr,1)
end,
construct_nodes = {"mcl_chests:ender_chest_small","mcl_chests:ender_chest","mcl_brewing:stand_000","mcl_chests:violet_shulker_box_small"},
loot = {
[ "mcl_chests:chest_small" ] ={{
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_farming:beetroot_seeds", weight = 16, amount_min = 1, amount_max=10 },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_mobitems:saddle", weight = 3, },
{ itemstring = "mcl_core:emerald", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tools:pick_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:shovel_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:sword_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:helmet_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:chestplate_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:leggings_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:boots_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:pick_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:shovel_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:helmet_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:leggings_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:boots_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_core:emerald", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_mobitems:iron_horse_armor", weight = 1, },
{ itemstring = "mcl_mobitems:gold_horse_armor", weight = 1, },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
}}
}
})

View File

@ -1,123 +1,101 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local worldseed = minetest.get_mapgen_setting("seed")
mcl_structures.register_structure("end_spawn_obsidian_platform",{
static_pos ={mcl_vars.mg_end_platform_pos},
place_func = function(pos,def,pr)
local obby = minetest.find_nodes_in_area(vector.offset(pos,-2,0,-2),vector.offset(pos,2,0,2),{"air","mcl_end:end_stone"})
local air = minetest.find_nodes_in_area(vector.offset(pos,-2,1,-2),vector.offset(pos,2,3,2),{"air","mcl_end:end_stone"})
minetest.bulk_set_node(obby,{name="mcl_core:obsidian"})
minetest.bulk_set_node(air,{name="air"})
return true
end,
})
mcl_structures.register_structure("end_exit_portal",{
static_pos = { mcl_vars.mg_end_exit_portal_pos },
filenames = {
modpath.."/schematics/mcl_structures_end_exit_portal.mts"
},
after_place = function(pos,def,pr,blockseed)
if minetest.settings:get_bool("only_peaceful_mobs", false) then
return
end
local p1 = vector.offset(pos,-16,-16,-16)
local p2 = vector.offset(pos,16,21,16)
minetest.emerge_area(p1,p2,function(blockpos, action, calls_remaining, param)
if calls_remaining > 0 then return end
minetest.bulk_set_node(minetest.find_nodes_in_area(p1,p2,{"mcl_portals:portal_end"}),{name="air"})
local obj = minetest.add_entity(vector.offset(pos,3, 11, 3), "mobs_mc:enderdragon")
if obj then
local dragon_entity = obj:get_luaentity()
dragon_entity._portal_pos = pos
if blockseed ~= -1 then
dragon_entity._initial = true
end
else
minetest.log("error", "[mcl_mapgen_core] ERROR! Ender dragon doesn't want to spawn")
end
minetest.fix_light(p1,p2)
end)
end
})
mcl_structures.register_structure("end_exit_portal_open",{
filenames = {
modpath.."/schematics/mcl_structures_end_exit_portal.mts"
},
after_place = function(pos,def,pr)
local p1 = vector.offset(pos,-16,-16,-16)
local p2 = vector.offset(pos,16,16,16)
minetest.fix_light(p1,p2)
end
})
mcl_structures.register_structure("end_gateway_portal",{
filenames = {
modpath.."/schematics/mcl_structures_end_gateway_portal.mts"
},
})
local function get_tower(p,h,tbl)
for i = 1,h do
table.insert(tbl,vector.offset(p,0,i,0))
end
-- mcl_structures_end_exit_portal open is triggered by mods/ENTITIES/mobs_mc/ender_dragon.lua
mcl_structures.spawn_end_exit_portal = function(pos)
local schematic = vl_structures.load_schematic(modpath.."/schematics/mcl_structures_end_exit_portal.mts")
vl_structures.place_schematic(pos, 0, schematic, "0", { name="end_exit_portal_open", prepare = false })
end
local function make_endspike(pos,width,height)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-width/2,0,-width/2),vector.offset(pos,width/2,0,width/2),{"air","group:solid"})
-- mcl_structures_end_gateway_portal.mts: see mods/ITEMS/mcl_portals/portal_gateway.lua
mcl_structures.spawn_end_gateway_portal = function(pos)
local schematic = vl_structures.load_schematic(modpath.."/schematics/mcl_structures_end_gateway_portal.mts")
vl_structures.place_schematic(pos, 0, schematic, "0", { name="end_gateway_portal", prepare = false })
end
local function make_endspike(pos, rad, height)
-- FIXME: why find_nodes, not just use the circle?
local nn = minetest.find_nodes_in_area(vector.offset(pos, -rad, 0, -rad), vector.offset(pos, rad, 0, rad), {"air", "group:solid"})
table.sort(nn,function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b)
end)
local nodes = {}
for i = 1,math.ceil(#nn*0.55) do
get_tower(nn[i],height,nodes)
for i = 1, math.ceil(#nn * 0.55) do
for j = 1, height do
table.insert(nodes, vector.offset(nn[i], 0, j, 0))
end
end
minetest.bulk_set_node(nodes,{ name="mcl_core:obsidian"} )
return vector.offset(pos,0,height,0)
minetest.bulk_swap_node(nodes, {name = "mcl_core:obsidian"})
return vector.offset(pos, 0, height, 0)
end
function make_cage(pos,width)
function make_cage(pos, rad)
if not xpanes then return end
local nodes = {}
local nodes2 = {}
local r = math.max(1,math.floor(width/2) - 2)
for x=-r,r do for y = 0,width do for z = -r,r do
if x == r or x == -r or z==r or z == -r then
table.insert(nodes,vector.add(pos,vector.new(x,y,z)))
end
end end end
if xpanes then
minetest.bulk_set_node(nodes,{ name="xpanes:bar_flat"} )
for _,p in pairs(nodes) do
xpanes.update_pane(p)
end
end
local r = math.max(1, rad - 2)
for y = 0, rad * 2 do for xz = -r, r do
table.insert(nodes,vector.add(pos,vector.new(xz,y, r)))
table.insert(nodes,vector.add(pos,vector.new(xz,y,-r)))
table.insert(nodes,vector.add(pos,vector.new( r,y,xz)))
table.insert(nodes,vector.add(pos,vector.new(-r,y,xz)))
end end
minetest.bulk_swap_node(nodes, {name = "xpanes:bar_flat"} )
for _,p in pairs(nodes) do xpanes.update_pane(p) end
end
local function get_points_on_circle(pos,r,n)
local rt = {}
local rt, step = {}, 2 * math.pi / n
for i=1, n do
table.insert(rt,vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r* math.sin(((i-1)/n) * (2*math.pi)) ))
table.insert(rt, vector.offset(pos, r * math.cos((i-1)*step), 0, r * math.sin((i-1)*step)))
end
return rt
end
mcl_structures.register_structure("end_spike",{
static_pos =get_points_on_circle(vector.offset(mcl_vars.mg_end_exit_portal_pos,0,-20,0),43,10),
place_func = function(pos,def,pr)
local d = pr:next(6,12)
local h = d * pr:next(4,6)
local p1 = vector.offset(pos, -d / 2, 0, -d / 2)
local p2 = vector.offset(pos, d / 2, h + d, d / 2)
minetest.emerge_area(p1, p2, function(blockpos, action, calls_remaining, param)
minetest.register_on_mods_loaded(function()
-- TODO: use LVM?
mcl_mapgen_core.register_generator("end structures", nil, function(minp, maxp, blockseed)
if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end
-- end spawn obsidian platform
local pos = mcl_vars.mg_end_platform_pos
if vector.in_area(pos, minp, maxp) then
local obby = minetest.find_nodes_in_area(vector.offset(pos,-2,0,-2),vector.offset(pos,2,0,2),{"air","mcl_end:end_stone"})
local air = minetest.find_nodes_in_area(vector.offset(pos,-2,1,-2),vector.offset(pos,2,3,2),{"air","mcl_end:end_stone"})
minetest.bulk_swap_node(obby,{name="mcl_core:obsidian"})
minetest.bulk_swap_node(air,{name="air"})
end
-- end exit portal and pillars
local pos = mcl_vars.mg_end_exit_portal_pos
if vector.in_area(pos, minp, maxp) then
local pr = PcgRandom(worldseed)
-- emerge pillars
local p1, p2 = vector.offset(pos, -43-6, -10, -43-6), vector.offset(pos, 43+6, 12*6-10, 43+6)
minetest.emerge_area(p1, p2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end
local s = make_endspike(pos,d,h)
minetest.set_node(vector.offset(s,0,1,0),{name="mcl_core:bedrock"})
minetest.add_entity(vector.offset(s,0,2,0),"mcl_end:crystal")
if pr:next(1,3) == 1 then
make_cage(vector.offset(s,0,1,0),d)
for _, p in ipairs(get_points_on_circle(vector.offset(pos, 0, -10, 0), 43, 10)) do
local rad = pr:next(3,6)
local top = make_endspike(p, rad, rad * 2 * pr:next(4,6) - 10)
minetest.swap_node(vector.offset(top, 0, 1, 0), {name = "mcl_core:bedrock"})
minetest.add_entity(vector.offset(top, 0, 2, 0), "mcl_end:crystal")
if pr:next(1, 3) == 1 then make_cage(vector.offset(top, 0, 1, 0), rad) end
end
end)
return true
end,
})
-- emerge end portal
local schematic = vl_structures.load_schematic(modpath.."/schematics/mcl_structures_end_exit_portal.mts")
vl_structures.place_schematic(pos, 0, schematic, "0", { name = "end portal", prepare = false,
after_place = function(pos,def,pr,pmin,pmax,size,rot)
-- spawn ender dragon
if minetest.settings:get_bool("only_peaceful_mobs", false) then return end
minetest.bulk_swap_node(minetest.find_nodes_in_area(pmin, pmax, {"mcl_portals:portal_end"}), { name="air" })
local obj = minetest.add_entity(vector.offset(pos, 3, 11, 3), "mobs_mc:enderdragon")
if obj then
local dragon_entity = obj:get_luaentity()
dragon_entity._portal_pos = pos
dragon_entity._initial = true
else
minetest.log("error", "[mcl_mapgen_core] ERROR! Ender dragon doesn't want to spawn")
end
end}, pr)
end
end, 100)
end)

View File

@ -0,0 +1,24 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("fossil",{
place_on = {"group:material_stone","group:sand"},
flags = "place_center_x, place_center_z",
prepare = false,
chunk_probability = 15, -- was 25, FIXME: needs rebalancing
y_offset = function(pr) return pr:next(-32,-16) end,
y_max = 15,
y_min = mcl_vars.mg_overworld_min + 35,
biomes = { "Desert" },
filenames = {
modpath.."/schematics/mcl_structures_fossil_skull_1.mts", -- 4×5×5
modpath.."/schematics/mcl_structures_fossil_skull_2.mts", -- 5×5×5
modpath.."/schematics/mcl_structures_fossil_skull_3.mts", -- 5×5×7
modpath.."/schematics/mcl_structures_fossil_skull_4.mts", -- 7×5×5
modpath.."/schematics/mcl_structures_fossil_spine_1.mts", -- 3×3×13
modpath.."/schematics/mcl_structures_fossil_spine_2.mts", -- 5×4×13
modpath.."/schematics/mcl_structures_fossil_spine_3.mts", -- 7×4×13
modpath.."/schematics/mcl_structures_fossil_spine_4.mts", -- 8×5×13
},
})

View File

@ -14,11 +14,11 @@ local function set_node_no_bedrock(pos, node)
return minetest.set_node(pos,node)
end
local function makegeode(pos,def,pr)
local function makegeode(pos, _, pr)
local size = pr:next(5,7)
local p1 = vector.offset(pos,-size,-size,-size)
local p2 = vector.offset(pos,size,size,size)
minetest.emerge_area(p1, p2, function(blockpos, action, calls_remaining, param)
minetest.emerge_area(p1, p2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end
local calcite = {}
local nn = minetest.find_nodes_in_area(p1,p2,{"group:material_stone","group:dirt","mcl_core:gravel"})
@ -37,9 +37,9 @@ local function makegeode(pos,def,pr)
set_node_no_bedrock(nn[i],{name="mcl_amethyst:amethyst_block"})
end
for k,v in pairs(minetest.find_nodes_in_area(p1,p2,{"mcl_amethyst:amethyst_block"})) do
for _, v in pairs(minetest.find_nodes_in_area(p1,p2,{"mcl_amethyst:amethyst_block"})) do
local all_amethyst = true
for kk,vv in pairs(adjacents) do
for _, vv in pairs(adjacents) do
local pp = vector.add(v,vv)
local an = minetest.get_node(pp)
if an.name ~= "mcl_amethyst:amethyst_block" then
@ -58,13 +58,13 @@ local function makegeode(pos,def,pr)
if all_amethyst then set_node_no_bedrock(v,{name="air"}) end
end
for _,v in pairs(calcite) do
for _,vv in pairs(minetest.find_nodes_in_area(vector.offset(v,-1,-1,-1),vector.offset(v,1,1,1),{"group:material_stone"})) do
for _, v in pairs(calcite) do
for _, vv in pairs(minetest.find_nodes_in_area(vector.offset(v,-1,-1,-1),vector.offset(v,1,1,1),{"group:material_stone"})) do
set_node_no_bedrock(vv,{name="mcl_blackstone:basalt_smooth"})
end
end
for k,v in pairs(minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_amethyst:amethyst_block","mcl_amethyst:budding_amethyst_block"})) do
for _, v in pairs(minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_amethyst:amethyst_block","mcl_amethyst:budding_amethyst_block"})) do
local r = pr:next(1,50)
if r < 10 then
set_node_no_bedrock(vector.offset(v,0,1,0),{name="mcl_amethyst:amethyst_cluster",param2=1})
@ -75,7 +75,7 @@ local function makegeode(pos,def,pr)
return true
end
mcl_structures.register_structure("geode",{
vl_structures.register_structure("geode",{
place_on = {"group:material_stone"},
noise_params = {
offset = 0,

View File

@ -1,24 +1,11 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
function mcl_structures.generate_igloo_top(pos, pr)
-- Furnace does ot work atm because apparently meta is not set. Need a bit of help with fixing this for furnaces, bookshelves, and brewing stands.
local newpos = {x=pos.x,y=pos.y-2,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, nil, function()
local p1 = vector.offset(pos,-5,-5,-5)
local p2 = vector.offset(pos,5,5,5)
mcl_structures.construct_nodes(p1,p2,{"mcl_furnaces:furnace","mcl_books:bookshelf"})
end), rotation
end
local S = minetest.get_translator(modname)
local function spawn_mobs(p1,p2,vi,zv)
local mc = minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_core:stonebrickmossy"})
if #mc == 2 then
local vp = mc[1]
local zp = mc[2]
local vp, zp = mc[1], mc[2]
if not vi and zv and zv:get_pos() and vector.distance(mc[1],zv:get_pos()) < 2 then
vp = mc[2]
elseif not zv and vi and vi:get_pos() and vector.distance(mc[2],vi:get_pos()) < 2 then
@ -26,134 +13,102 @@ local function spawn_mobs(p1,p2,vi,zv)
elseif zv and vi then
return
end
vi = minetest.add_entity(vector.offset(mc[1],0,1,0),"mobs_mc:villager")
zv = minetest.add_entity(vector.offset(mc[2],0,1,0),"mobs_mc:villager_zombie")
minetest.after(1,spawn_mobs,p1,p2,vi,zv)
vi = minetest.add_entity(vector.offset(vp,0,1,0),"mobs_mc:villager")
zv = minetest.add_entity(vector.offset(zp,0,1,0),"mobs_mc:villager_zombie")
if vi and vi:get_pos() and zv and zv:get_pos() then
minetest.after(1,spawn_mobs,p1,p2,vi,zv)
end
end
end
function mcl_structures.generate_igloo_basement(pos, orientation, loot, pr)
-- TODO: Add monster eggs
local path = modpath.."/schematics/mcl_structures_igloo_basement.mts"
mcl_structures.place_schematic(pos, path, orientation, nil, true, nil, function()
local p1 = vector.offset(pos,-5,-5,-5)
local p2 = vector.offset(pos,5,5,5)
mcl_structures.fill_chests(p1,p2,loot,pr)
mcl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"})
spawn_mobs(p1,p2)
end, pr)
end
function mcl_structures.generate_igloo(pos, def, pr)
-- Place igloo
local success, rotation = mcl_structures.generate_igloo_top(pos, pr)
local function igloo_callback(cpos,def,pr,p1,p2,size,rotation)
vl_structures.construct_nodes(p1, p2, {"mcl_furnaces:furnace","mcl_books:bookshelf"})
-- 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-2, 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-2, 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-2, 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-2, 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"
end
else
if c == 1 then
brick = "mcl_core:stonebrickcracked"
else
brick = "mcl_core:stonebrick"
end
end
minetest.set_node(pos, {name=brick})
end
local ladder_param2 = minetest.dir_to_wallmounted(tdir)
local real_depth = 0
-- Check how deep we can actuall dig
for y=1, depth-5 do
real_depth = real_depth + 1
local node = minetest.get_node({x=tpos.x,y=tpos.y-y,z=tpos.z})
local def = minetest.registered_nodes[node.name]
if not (def 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, def.loot, 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)
if pr:next(1,2) == 1 then return end
local pos = p1 -- we use top left as reference
-- Select basement depth
local maxdepth = pos.y - (mcl_vars.mg_lava_overworld_max + 10)
if maxdepth <= 9 then return true end
local depth = pr:next(9, maxdepth)
-- trapdoor position and orientation
local tpos, dir, tdir
if rotation == "0" then
dir = vector.new(-1, 0, 0)
tdir = vector.new(1, 0, 0)
tpos = vector.new(pos.x+7, pos.y, pos.z+3)
elseif rotation == "90" then
dir = vector.new(0, 0, -1)
tdir = vector.new(0, 0, -1)
tpos = vector.new(pos.x+3, pos.y, pos.z+1)
elseif rotation == "180" then
dir = vector.new(1, 0, 0)
tdir = vector.new(-1, 0, 0)
tpos = vector.new(pos.x+1, pos.y, pos.z+3)
elseif rotation == "270" then
dir = vector.new(0, 0, 1)
tdir = vector.new(0, 0, 1)
tpos = vector.new(pos.x+3, pos.y, pos.z+7)
else
minetest.log("bad rotation: "..tostring(rotation))
return false
end
return success
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
brick = (c == 1 and "mcl_monster_eggs:monster_egg_stonebrickcracked") or "mcl_monster_eggs:monster_egg_stonebrick"
else
brick = (c == 1 and "mcl_core:stonebrickcracked") or "mcl_core:stonebrick"
end
minetest.set_node(pos, {name=brick})
end
local real_depth = 2
-- Check how deep we can actually dig
for y=pos.y-real_depth, pos.y-depth, -1 do
real_depth = real_depth + 1
local node = minetest.get_node(vector.new(tpos.x, y, tpos.z))
local def = node and minetest.registered_nodes[node.name]
if not (def and def.walkable and def.liquidtype == "none" and def.is_ground_content) then break end
end
local bpos = vector.new(cpos.x, pos.y-real_depth+1, cpos.z)
if real_depth <= 6 then
minetest.log("action", "Ground not deep enough for igloo basement: "..real_depth)
return false
end
local path = modpath.."/schematics/mcl_structures_igloo_basement.mts"
vl_structures.place_schematic(bpos, -1, path, rotation, {
force_placement = true,
prepare = { tolerance = -1, foundation = false, clear = false },
after_place = function(_, _, pr, p1, p2)
-- Generate ladder to basement
local ladder = {name="mcl_core:ladder", param2=minetest.dir_to_wallmounted(tdir)}
-- TODO: use voxelmanip?
minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2
for y = tpos.y-1, bpos.y+4, -1 do
set_brick(vector.new(tpos.x-1, y, tpos.z ))
set_brick(vector.new(tpos.x+1, y, tpos.z ))
set_brick(vector.new(tpos.x , y, tpos.z-1))
set_brick(vector.new(tpos.x , y, tpos.z+1))
minetest.set_node(vector.new(tpos.x, y, tpos.z), ladder)
end
vl_structures.fill_chests(p1,p2,def.loot,pr)
vl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"})
spawn_mobs(p1,p2)
end
}, pr)
end
mcl_structures.register_structure("igloo",{
vl_structures.register_structure("igloo",{
filenames = { modpath.."/schematics/mcl_structures_igloo_top.mts" },
place_on = {"mcl_core:snowblock","mcl_core:snow","group:grass_block_snow"},
fill_ratio = 0.01,
sidelen = 16,
chunk_probability = 250,
solid_ground = true,
make_foundation = true,
chunk_probability = 15,
prepare = { tolerance = 3, padding = 1, corners = 1, foundation = -6, clear_top = -1 },
y_max = mcl_vars.mg_overworld_max,
y_min = 0,
y_offset = 0,
biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" },
place_func = mcl_structures.generate_igloo,
y_offset = -1,
biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" },
after_place = igloo_callback,
loot = {
["mcl_chests:chest_small"] = {{
stacks_min = 1,

View File

@ -1,135 +1,68 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
mcl_structures = {}
dofile(modpath.."/api.lua")
dofile(modpath.."/shipwrecks.lua")
-- some legacy API adapters
mcl_structures.is_disabled = vl_structures.is_disabled
mcl_structures.init_node_construct = vl_structures.init_node_construct
mcl_structures.construct_nodes = vl_structures.construct_nodes
mcl_structures.fill_chests = vl_structures.fill_chests
mcl_structures.spawn_mobs = vl_structures.spawn_mobs
-- TODO: provide more legacy adapters that translate parameters?
mcl_structures.place_schematic = function(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
vl_structures.place_schematic(pos, yoffset, schematic, rotation, {
replacements = replacements,
force_placement = force_placement,
flags = flags,
after_place = after_placement_callback,
callback_param = callback_param
}, pr)
end
mcl_structures.place_structure = vl_structures.place_structure -- still compatible
mcl_structures.register_structure = function(name, def, nospawn)
-- nospawn: ignored, just pass no place_on!
if not def.solid_ground then def.prepare = def.prepare or {} end
vl_structures.register_structure(name, def)
end
dofile(modpath.."/campsite.lua")
dofile(modpath.."/desert_temple.lua")
dofile(modpath.."/desert_well.lua")
dofile(modpath.."/end_city.lua")
dofile(modpath.."/end_spawn.lua")
dofile(modpath.."/fossil.lua")
dofile(modpath.."/geode.lua")
dofile(modpath.."/igloo.lua")
dofile(modpath.."/jungle_temple.lua")
dofile(modpath.."/ocean_ruins.lua")
dofile(modpath.."/witch_hut.lua")
dofile(modpath.."/igloo.lua")
dofile(modpath.."/woodland_mansion.lua")
dofile(modpath.."/ruined_portal.lua")
dofile(modpath.."/geode.lua")
dofile(modpath.."/ocean_temple.lua")
dofile(modpath.."/pillager_outpost.lua")
dofile(modpath.."/end_spawn.lua")
dofile(modpath.."/end_city.lua")
dofile(modpath.."/ruined_portal.lua")
dofile(modpath.."/shipwrecks.lua")
dofile(modpath.."/spider_cocoon.lua")
dofile(modpath.."/witch_hut.lua")
dofile(modpath.."/woodland_mansion.lua")
mcl_structures.register_structure("desert_well",{
place_on = {"group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
not_near = { "desert_temple_new" },
solid_ground = true,
sidelen = 4,
chunk_probability = 600,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
y_offset = -2,
biomes = { "Desert" },
filenames = { modpath.."/schematics/mcl_structures_desert_well.mts" },
})
mcl_structures.register_structure("fossil",{
place_on = {"group:material_stone","group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
solid_ground = true,
sidelen = 13,
chunk_probability = 1000,
y_offset = function(pr) return ( pr:next(1,16) * -1 ) -16 end,
y_max = 15,
y_min = mcl_vars.mg_overworld_min + 35,
biomes = { "Desert" },
filenames = {
modpath.."/schematics/mcl_structures_fossil_skull_1.mts", -- 4×5×5
modpath.."/schematics/mcl_structures_fossil_skull_2.mts", -- 5×5×5
modpath.."/schematics/mcl_structures_fossil_skull_3.mts", -- 5×5×7
modpath.."/schematics/mcl_structures_fossil_skull_4.mts", -- 7×5×5
modpath.."/schematics/mcl_structures_fossil_spine_1.mts", -- 3×3×13
modpath.."/schematics/mcl_structures_fossil_spine_2.mts", -- 5×4×13
modpath.."/schematics/mcl_structures_fossil_spine_3.mts", -- 7×4×13
modpath.."/schematics/mcl_structures_fossil_spine_4.mts", -- 8×5×13
},
})
mcl_structures.register_structure("boulder",{
vl_structures.register_structure("boulder",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = {
-- small boulder 3x as likely
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder.mts",
-- small boulder 3x as likely
},
},true) --is spawned as a normal decoration. this is just for /spawnstruct
mcl_structures.register_structure("ice_spike_small",{
filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" },
},true) --is spawned as a normal decoration. this is just for /spawnstruct
mcl_structures.register_structure("ice_spike_large",{
sidelen = 6,
filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" },
},true) --is spawned as a normal decoration. this is just for /spawnstruct
-- Debug command
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
minetest.register_chatcommand("spawnstruct", {
params = "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 == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then
mcl_dungeons.spawn_dungeon(pos, rot, pr)
elseif param == "" then
message = S("Error: No structure type given. Please use “/spawnstruct <type>”.")
errord = true
else
for n,d in pairs(mcl_structures.registered_structures) do
if n == param then
mcl_structures.place_structure(pos,d,pr,math.random(),rot)
return true,message
end
end
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
})
minetest.register_on_mods_loaded(function()
local p = ""
for n,_ in pairs(mcl_structures.registered_structures) do
p = p .. " | "..n
end
minetest.registered_chatcommands["spawnstruct"].params = minetest.registered_chatcommands["spawnstruct"].params .. p
end)
vl_structures.register_structure("ice_spike_small",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" },
})
vl_structures.register_structure("ice_spike_large",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" },
})

View File

@ -2,18 +2,14 @@ local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
mcl_structures.register_structure("jungle_temple",{
vl_structures.register_structure("jungle_temple",{
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
solid_ground = true,
make_foundation = true,
y_offset = function(pr) return pr:next(-3,0) -5 end,
chunk_probability = 200,
chunk_probability = 5,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
biomes = { "Jungle" },
sidelen = 18,
filenames = {
modpath.."/schematics/mcl_structures_jungle_temple.mts",
modpath.."/schematics/mcl_structures_jungle_temple_nice.mts",
@ -26,7 +22,7 @@ mcl_structures.register_structure("jungle_temple",{
{ itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 16, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
--{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 }, --FIXME BAMBOO
{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 },
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_mobitems:saddle", weight = 3, },

View File

@ -1,4 +1,4 @@
name = mcl_structures
author = Wuzzy, cora
author = Wuzzy, cora, kno10
description = Structure placement for MCL2
depends = mcl_init, mcl_loot
depends = mcl_init, mcl_util, mcl_loot, vl_terraforming, vl_structures

View File

@ -1,6 +1,6 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local water_level = minetest.get_mapgen_setting("water_level")
local cold_oceans = {
"RoofedForest_ocean",
@ -72,18 +72,15 @@ local warm_oceans = {
local cold = {
place_on = {"group:sand","mcl_core:gravel","mcl_core:dirt","mcl_core:clay","group:material_stone"},
spawn_by = {"mcl_core:water_source"},
spawn_by = {"group:water"},
num_spawn_by = 2,
fill_ratio = 0.01,
flags = "place_center_x, place_center_z, force_placement",
solid_ground = true,
make_foundation = true,
y_offset = -1,
y_min = mcl_vars.mg_overworld_min,
y_max = -2,
chunk_probability = 10, -- todo: 15?
biomes = cold_oceans,
chunk_probability = 400,
sidelen = 20,
y_min = mcl_vars.mg_overworld_min,
y_max = water_level - 6,
y_offset = -1,
flags = "place_center_x, place_center_z, force_placement",
prepare = { foundation = -2, clear = false, mode="water" },
filenames = {
modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts",
modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts",
@ -128,5 +125,5 @@ warm.filenames = {
modpath.."/schematics/mcl_structures_ocean_ruins_warm_4.mts",
}
mcl_structures.register_structure("cold_ocean_ruins",cold)
mcl_structures.register_structure("warm_ocean_ruins",warm)
vl_structures.register_structure("cold_ocean_ruins",cold)
vl_structures.register_structure("warm_ocean_ruins",warm)

View File

@ -0,0 +1,157 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local water_level = minetest.get_mapgen_setting("water_level")
local spawnon = { "mcl_stairs:slab_prismarine_dark" }
local ocean_biomes = {
"RoofedForest_ocean",
"JungleEdgeM_ocean",
"BirchForestM_ocean",
"BirchForest_ocean",
"IcePlains_deep_ocean",
"Jungle_deep_ocean",
"Savanna_ocean",
"MesaPlateauF_ocean",
"ExtremeHillsM_deep_ocean",
"Savanna_deep_ocean",
"SunflowerPlains_ocean",
"Swampland_deep_ocean",
"Swampland_ocean",
"MegaSpruceTaiga_deep_ocean",
"ExtremeHillsM_ocean",
"JungleEdgeM_deep_ocean",
"SunflowerPlains_deep_ocean",
"BirchForest_deep_ocean",
"IcePlainsSpikes_ocean",
"Mesa_ocean",
"StoneBeach_ocean",
"Plains_deep_ocean",
"JungleEdge_deep_ocean",
"SavannaM_deep_ocean",
"Desert_deep_ocean",
"Mesa_deep_ocean",
"ColdTaiga_deep_ocean",
"Plains_ocean",
"MesaPlateauFM_ocean",
"Forest_deep_ocean",
"JungleM_deep_ocean",
"FlowerForest_deep_ocean",
"MushroomIsland_ocean",
"MegaTaiga_ocean",
"StoneBeach_deep_ocean",
"IcePlainsSpikes_deep_ocean",
"ColdTaiga_ocean",
"SavannaM_ocean",
"MesaPlateauF_deep_ocean",
"MesaBryce_deep_ocean",
"ExtremeHills+_deep_ocean",
"ExtremeHills_ocean",
"MushroomIsland_deep_ocean",
"Forest_ocean",
"MegaTaiga_deep_ocean",
"JungleEdge_ocean",
"MesaBryce_ocean",
"MegaSpruceTaiga_ocean",
"ExtremeHills+_ocean",
"Jungle_ocean",
"RoofedForest_deep_ocean",
"IcePlains_ocean",
"FlowerForest_ocean",
"ExtremeHills_deep_ocean",
"MesaPlateauFM_deep_ocean",
"Desert_ocean",
"Taiga_ocean",
"BirchForestM_deep_ocean",
"Taiga_deep_ocean",
"JungleM_ocean"
}
vl_structures.register_structure("ocean_temple",{
place_on = {"group:sand","mcl_core:gravel"},
spawn_by = {"group:water"},
num_spawn_by = 4,
noise_params = {
offset = 0,
scale = 0.0000122,
spread = {x = 250, y = 250, z = 250},
seed = 32345,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "force_placement",
force_placement = true,
prepare = { tolerance = 8, clear = false, foundation = 3, mode="water" },
biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min,
filenames = {
modpath .. "/schematics/mcl_structures_ocean_temple.mts",
modpath .. "/schematics/mcl_structures_ocean_temple_2.mts",
},
y_offset = -1, --function(pr) return pr:next(-2,-1) end, -- fewer mobs if buried in sand
after_place = function(p, _, pr, p1, p2)
vl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true)
vl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true)
vl_structures.construct_nodes(p1,p2,{"group:wall"})
end,
loot = {
["mcl_chests:chest_small"] = {
{
stacks_min = 3,
stacks_max = 10,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
},
{
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
}
},
}
}
})
vl_structures.register_structure_spawn({
name = "mobs_mc:guardian",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,
chance = 10,
interval = 60,
limit = 9,
spawnon = spawnon,
})
vl_structures.register_structure_spawn({
name = "mobs_mc:guardian_elder",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,
chance = 100,
interval = 60,
limit = 4,
spawnon = spawnon,
})

View File

@ -1,19 +1,14 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local spawnon = {"mcl_core:stripped_oak","mcl_stairs:slab_birchwood_top"}
mcl_structures.register_structure("pillager_outpost",{
vl_structures.register_structure("pillager_outpost",{
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
solid_ground = true,
make_foundation = true,
sidelen = 32,
prepare = { padding = 3, corners = 4, foundation = -8, clear = true },
y_offset = 0,
chunk_probability = 600,
chunk_probability = 15,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
biomes = { "Desert", "Plains", "Savanna", "IcePlains", "Taiga" },
@ -62,22 +57,15 @@ mcl_structures.register_structure("pillager_outpost",{
}
}}
},
after_place = function(p,def,pr)
local p1 = vector.offset(p,-9,0,-9)
local p2 = vector.offset(p,9,32,9)
mcl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5)
mcl_structures.spawn_mobs("mobs_mc:parrot",{"mesecons_pressureplates:pressure_plate_stone_off"},p1,p2,pr,3)
mcl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1)
for _,n in pairs(minetest.find_nodes_in_area(p1,p2,{"group:wall"})) do
local def = minetest.registered_nodes[minetest.get_node(n).name:gsub("_%d+$","")]
if def and def.on_construct then
def.on_construct(n)
end
end
after_place = function(p,_,pr) -- fixme: use p1, p2 from the callback?
local p1, p2 = vector.offset(p,-9,0,-9), vector.offset(p,9,32,9)
vl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5)
vl_structures.spawn_mobs("mobs_mc:parrot",{"mesecons_pressureplates:pressure_plate_stone_off"},p1,p2,pr,3)
vl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1)
end
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure_spawn({
name = "mobs_mc:pillager",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,

View File

@ -4,7 +4,7 @@ local modpath = minetest.get_modpath(modname)
local function get_replacements(b,c,pr)
local r = {}
if not b then return r end
for k,v in pairs(b) do
for _, v in pairs(b) do
if pr:next(1,100) < c then table.insert(r,v) end
end
return r
@ -12,14 +12,11 @@ end
local def = {
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:grass_block","group:sand","group:grass_block_snow","mcl_core:snow"},
fill_ratio = 0.006,
flags = "place_center_x, place_center_z, all_floors",
solid_ground = true,
make_foundation = true,
chunk_probability = 800,
prepare = { padding = 0, corners = 3, tolerance = 15, foundation = true, clear = true, clear_top = 0 },
chunk_probability = 20,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
sidelen = 10,
y_offset = -5,
filenames = {
modpath.."/schematics/mcl_structures_ruined_portal_1.mts",
@ -27,25 +24,23 @@ local def = {
modpath.."/schematics/mcl_structures_ruined_portal_3.mts",
modpath.."/schematics/mcl_structures_ruined_portal_4.mts",
modpath.."/schematics/mcl_structures_ruined_portal_5.mts",
modpath.."/schematics/mcl_structures_ruined_portal_6.mts",
modpath.."/schematics/mcl_structures_ruined_portal_99.mts",
},
after_place = function(pos,def,pr)
local hl = def.sidelen / 2
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
after_place = function(pos,def,pr,p1,p2)
local gold = minetest.find_nodes_in_area(p1,p2,{"mcl_core:goldblock"})
local lava = minetest.find_nodes_in_area(p1,p2,{"mcl_core:lava_source"})
local rack = minetest.find_nodes_in_area(p1,p2,{"mcl_nether:netherrack"})
local brick = minetest.find_nodes_in_area(p1,p2,{"mcl_core:stonebrick"})
local obby = minetest.find_nodes_in_area(p1,p2,{"mcl_core:obsidian"})
minetest.bulk_set_node(get_replacements(gold,30,pr),{name="air"})
minetest.bulk_set_node(get_replacements(lava,20,pr),{name="mcl_nether:magma"})
minetest.bulk_set_node(get_replacements(rack,7,pr),{name="mcl_nether:magma"})
minetest.bulk_set_node(get_replacements(obby,30,pr),{name="mcl_core:crying_obsidian"})
minetest.bulk_set_node(get_replacements(obby,10,pr),{name="air"})
minetest.bulk_set_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickcracked"})
minetest.bulk_swap_node(get_replacements(gold,30,pr),{name="air"})
minetest.bulk_swap_node(get_replacements(lava,20,pr),{name="mcl_nether:magma"})
minetest.bulk_swap_node(get_replacements(rack,7,pr),{name="mcl_nether:magma"})
minetest.bulk_swap_node(get_replacements(obby,30,pr),{name="mcl_core:crying_obsidian"})
minetest.bulk_swap_node(get_replacements(obby,10,pr),{name="air"})
minetest.bulk_swap_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickcracked"})
brick = minetest.find_nodes_in_area(p1,p2,{"mcl_core:stonebrick"})
minetest.bulk_set_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickmossy"})
minetest.bulk_swap_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickmossy"})
end,
loot = {
["mcl_chests:chest_small" ] ={{
@ -102,9 +97,10 @@ local def = {
}}
}
}
mcl_structures.register_structure("ruined_portal_overworld",def)
vl_structures.register_structure("ruined_portal_overworld",def)
local ndef = table.copy(def)
ndef.y_min=mcl_vars.mg_lava_nether_max +10
ndef.y_max=mcl_vars.mg_nether_max - 15
ndef.place_on = {"mcl_nether:netherrack","group:soul_block","mcl_blackstone:basalt,mcl_blackstone:blackstone","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium"},
mcl_structures.register_structure("ruined_portal_nether",ndef)
vl_structures.register_structure("ruined_portal_nether",ndef)

Binary file not shown.

Binary file not shown.

View File

@ -1,19 +1,6 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
--local S = minetest.get_translator(modname)
local seed = minetest.get_mapgen_setting("seed")
local water_level = minetest.get_mapgen_setting("water_level")
local pr = PseudoRandom(seed)
--schematics by chmodsayshello
local schems = {
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_front.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_back.mts",
}
local ocean_biomes = {
"RoofedForest_ocean",
@ -78,42 +65,27 @@ local ocean_biomes = {
"JungleM_ocean"
}
local beach_biomes = {
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore"
}
-- FIXME: integrate treasure maps from MCLA
mcl_structures.register_structure("shipwreck",{
vl_structures.register_structure("shipwreck",{
place_on = {"group:sand","mcl_core:gravel"},
spawn_by = {"group:water"},
num_spawn_by = 4,
noise_params = {
offset = 0,
scale = 0.000022,
spread = {x = 250, y = 250, z = 250},
seed = 3,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
sidelen = 16,
flags = "force_placement",
chunk_probability = 15,
biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min,
filenames = schems,
y_offset = function(pr) return pr:next(-4,-2) end,
y_max = water_level-4,
y_offset = function(pr) return pr:next(-3,-1) end,
flags = "place_center_x, place_center_z, force_placement",
prepare = { tolerance = -1, clear = false, foundation = false, mode = "water" },
filenames = {
--schematics by chmodsayshello
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_front.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_back.mts",
},
loot = {
["mcl_chests:chest_small"] = {
{
@ -130,13 +102,13 @@ mcl_structures.register_structure("shipwreck",{
{ itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, pr)
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, pr)
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, pr)
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, pr)
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_bamboo:bamboo", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 },
@ -174,93 +146,3 @@ mcl_structures.register_structure("shipwreck",{
}
})
local spawnon = { "mcl_stairs:slab_prismarine_dark"}
mcl_structures.register_structure("ocean_temple",{
place_on = {"group:sand","mcl_core:gravel"},
spawn_by = {"group:water"},
num_spawn_by = 4,
noise_params = {
offset = 0,
scale = 0.0000122,
spread = {x = 250, y = 250, z = 250},
seed = 32345,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
sidelen = 32,
flags = "force_placement",
biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min,
filenames = {
modpath .. "/schematics/mcl_structures_ocean_temple.mts",
modpath .. "/schematics/mcl_structures_ocean_temple_2.mts",
},
y_offset = function(pr) return pr:next(-2,0) end,
after_place = function(p,def,pr)
local p1 = vector.offset(p,-9,0,-9)
local p2 = vector.offset(p,9,32,9)
mcl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true)
mcl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true)
mcl_structures.construct_nodes(p1,p2,{"group:wall"})
end,
loot = {
["mcl_chests:chest_small"] = {
{
stacks_min = 3,
stacks_max = 10,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
},
{
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
}
},
}
}
})
mcl_structures.register_structure_spawn({
name = "mobs_mc:guardian",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,
chance = 10,
interval = 60,
limit = 9,
spawnon = spawnon,
})
mcl_structures.register_structure_spawn({
name = "mobs_mc:guardian_elder",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,
chance = 100,
interval = 60,
limit = 4,
spawnon = spawnon,
})

View File

@ -0,0 +1,94 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("cocoon",{
place_on = {"group:material_stone"},
flags = "place_center_x, place_center_z, all_floors",
chunk_probability = 20,
y_max = -10,
y_min = mcl_vars.mg_overworld_min,
y_offset = 2,
spawn_by = "air",
check_offset = 1,
num_spawn_by = 6,
force_placement = false,
prepare = { foundation = false, clear = false, clear_top = 0, padding = -1, corners = 1 }, -- TODO: make clear/foundation not use grass
filenames = {
modpath.."/schematics/cocoon_1.mts"
},
after_place = function(p,def,pr,p1,p2)
if mcl_mobspawners then
local spawner = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
if #spawner > 0 then
mcl_mobspawners.setup_spawner(spawner[1], "mobs_mc:cave_spider", 0, 7, 4, 15, -3)
end
end
-- p2.y is the top slice only, not a typo, we look for the rope
local cs = minetest.find_nodes_in_area(vector.new(p1.x,p2.y,p1.z), p2, "mcl_wool:white")
local rope = {}
-- TODO: port to VoxelManip?
for _,c in pairs(cs) do
while true do
c = vector.offset(c,0,1,0)
local name = minetest.get_node(c).name
if name == "ignore" then break end
if name ~= "air" then break end
table.insert(rope,c)
end
end
minetest.bulk_swap_node(rope, {name = "mcl_wool:white", param2 = 2})
-- remove some of the spiderwebs to add variation
local ws = minetest.find_nodes_in_area(p1, p2, "mcl_core:cobweb")
local clear = {}
for i = 1,math.floor(#ws/4) do
if #ws == 0 then break end
local idx = pr:next(1,#ws)
table.insert(clear, ws[idx])
table.remove(ws, idx)
end
minetest.bulk_swap_node(clear, {name = "air"})
end,
loot = {
["mcl_chests:chest_small"] = {
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 2, amount_max = 4 },
{ itemstring = "mcl_farming:potato_item_poison", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
},
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 90, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_core:iron_nugget", weight = 50, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:emerald", weight = 40, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:lapis", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:gold_ingot", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
}
},{
stacks_min = 1,
stacks_max = 1,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:paper", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 2, amount_max = 4 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 3, amount_max = 8 },
{ itemstring = "mcl_books:book", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
}
},
}
}
})

View File

@ -14,39 +14,46 @@ local function spawn_witch(p1,p2)
witch._home = c
witch.can_despawn = false
end
local cat = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat"):get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
local catobject = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
if catobject and catobject:get_pos() then
local cat=catobject:get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
end
return
end
end
local function hut_placement_callback(pos,def,pr)
local hl = def.sidelen / 2
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
local legs = minetest.find_nodes_in_area(vector.offset(pos,-hl,0,-hl),vector.offset(pos,hl,0,hl), "mcl_core:tree")
local function hut_placement_callback(pos,def,pr,p1,p2)
-- p1.y is the bottom slice only, not a typo, we look for the hut legs
local legs = minetest.find_nodes_in_area(p1,vector.new(p2.x,p1.y,p2.z), "mcl_core:tree")
local tree = {}
-- TODO: port leg generation to VoxelManip?
for _,leg in pairs(legs) do
while minetest.get_item_group(mcl_vars.get_node(vector.offset(leg,0,-1,0), true, 333333).name, "water") ~= 0 do
while true do
local name = minetest.get_node(vector.offset(leg,0,-1,0)).name
if name == "ignore" then break end
if name ~= "air" and minetest.get_item_group(name, "water") == 0 then break end
leg = vector.offset(leg,0,-1,0)
table.insert(tree,leg)
end
end
minetest.bulk_set_node(tree, {name = "mcl_core:tree", param2 = 2})
minetest.bulk_swap_node(tree, {name = "mcl_core:tree", param2 = 2})
spawn_witch(p1,p2)
end
mcl_structures.register_structure("witch_hut",{
place_on = {"mcl_core:water_source","mclx_core:river_water_source"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z, liquid_surface, force_placement",
sidelen = 8,
chunk_probability = 300,
vl_structures.register_structure("witch_hut",{
place_on = {"mcl_core:water_source","group:sand","group:grass_block","group:dirt","mclx_core:river_water_source"},
spawn_by = {"mcl_core:water_source","mclx_core:river_water_source"},
check_offset = -1,
num_spawn_by = 3,
flags = "place_center_x, place_center_z, all_surfaces",
chunk_probability = 8,
prepare = { mode="under_air", tolerance=4, clear_bottom=3, padding=0, corners=1, foundation=false },
y_max = mcl_vars.mg_overworld_max,
y_min = -4,
y_min = -5,
y_offset = 0,
biomes = { "Swampland", "Swampland_ocean", "Swampland_shore" },
filenames = { modpath.."/schematics/mcl_structures_witch_hut.mts" },

View File

@ -1,32 +1,26 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local spawnon = {"mcl_deepslate:deepslate","mcl_core:birchwood","mcl_wool:red_carpet","mcl_wool:brown_carpet"}
mcl_structures.register_structure("woodland_cabin",{
vl_structures.register_structure("woodland_cabin",{
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
solid_ground = true,
make_foundation = true,
chunk_probability = 800,
prepare = { padding = 2, corners = 5, foundation = true, clear = true, clear_top = 2 },
force_placement = false,
chunk_probability = 20,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
biomes = { "RoofedForest" },
sidelen = 32,
filenames = {
modpath.."/schematics/mcl_structures_woodland_cabin.mts",
modpath.."/schematics/mcl_structures_woodland_outpost.mts",
},
construct_nodes = {"mcl_barrels:barrel_closed","mcl_books:bookshelf"},
after_place = function(p,def,pr)
local p1=vector.offset(p,-def.sidelen,-1,-def.sidelen)
local p2=vector.offset(p,def.sidelen,def.sidelen,def.sidelen)
mcl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5)
mcl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1)
mcl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1)
after_place = function(p,def,pr,p1,p2)
vl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5)
vl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1)
vl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1)
end,
loot = {
["mcl_chests:chest_small" ] ={{
@ -69,7 +63,7 @@ mcl_structures.register_structure("woodland_cabin",{
}
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure_spawn({
name = "mobs_mc:vindicator",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,
@ -79,7 +73,7 @@ mcl_structures.register_structure_spawn({
spawnon = spawnon,
})
mcl_structures.register_structure_spawn({
vl_structures.register_structure_spawn({
name = "mobs_mc:evoker",
y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max,

View File

@ -10,19 +10,6 @@ local adjacents = {
vector.new(0,-1,0)
}
local plane_adjacents = {
vector.new(1,0,0),
vector.new(-1,0,0),
vector.new(0,0,1),
vector.new(0,0,-1),
}
local function set_node_no_bedrock(pos,node)
local n = minetest.get_node(pos)
if n.name == "mcl_core:bedrock" then return end
return minetest.set_node(pos,node)
end
local function airtower(pos,tbl,h)
for i=1,h do
table.insert(tbl,vector.offset(pos,0,i,0))
@ -32,7 +19,7 @@ end
local function makelake(pos,size,liquid,placein,border,pr,noair)
local p1, p2 = vector.offset(pos,-size,-1,-size), vector.offset(pos,size,-1,size)
local e1, e2 = vector.offset(pos,-size,-2,-size), vector.offset(pos,size,15,size)
minetest.emerge_area(e1, e2, function(blockpos, action, calls_remaining, param)
minetest.emerge_area(e1, e2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end
local nn = minetest.find_nodes_in_area(p1,p2,placein)
if not nn[1] then return end
@ -46,8 +33,8 @@ local function makelake(pos,size,liquid,placein,border,pr,noair)
airtower(nn[i],air,20)
table.insert(lq,nn[i])
end
minetest.bulk_set_node(lq,{name=liquid})
minetest.bulk_set_node(air,{name="air"})
minetest.bulk_swap_node(lq,{name=liquid})
minetest.bulk_swap_node(air,{name="air"})
air = {}
local br = {}
for k,v in pairs(lq) do
@ -74,8 +61,8 @@ local function makelake(pos,size,liquid,placein,border,pr,noair)
end
end
end
minetest.bulk_set_node(br,{name=border})
minetest.bulk_set_node(air,{name="air"})
minetest.bulk_swap_node(br,{name=border})
minetest.bulk_swap_node(air,{name="air"})
return true
end)
return true
@ -83,57 +70,83 @@ end
local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"}
local function get_fallen_tree_schematic(pos,pr)
local function place_fallen_tree(pos,def,pr)
local tree = minetest.find_node_near(pos,15,{"group:tree"})
if not tree then return end
tree = minetest.get_node(tree).name
local maxlen = 8
local minlen = 2
local vprob = 120
local mprob = 160
local minlen, maxlen = 3, 9
local vrate, mrate = 120, 160
local len = pr:next(minlen,maxlen)
local schem = {
size = {x = len + 2, y = 2, z = 3},
data = {
{name = "air", prob=0},
{name = "air", prob=0},
}
}
for i = 1,len do
table.insert(schem.data,{name = "mcl_core:vine",param2=4, prob=vprob})
local dir = pr:next(0,3)
local dx, dy, dz, param2, w1, w2
if dir == 0 then
dx, dy, dz, param2, w1, w2 = 1, 0, 0, 12, 5, 4
elseif dir == 1 then
dx, dy, dz, param2, w1, w2 = -1, 0, 0, 12, 4, 5
elseif dir == 2 then
dx, dy, dz, param2, w1, w2 = 0, 0, 1, 6, 3, 2
else -- if dir == 3 then
dx, dy, dz, param2, w1, w2 = 0, 0, -1, 6, 2, 3
end
table.insert(schem.data,{name = "air", prob=0})
table.insert(schem.data,{name = "air", prob=0})
-- TODO: port this to voxel manipulators
-- ensure we have room for the tree
local minsupport, maxsupport = 99, 1
for i = 1,len do
table.insert(schem.data,{name = "air", prob=0})
-- check below
local n = minetest.get_node(vector.offset(pos, dx * i, -1, dz * i)).name
local nd = minetest.registered_nodes[n]
if n ~= "air" and nd.groups and nd.groups.solid and i > 2 then
if i < minsupport then minsupport = i end
maxsupport = i
end
-- check space
local n = minetest.get_node(vector.offset(pos, dx * i, 0, dz * i)).name
local nd = minetest.registered_nodes[n]
if n ~= "air" and nd.groups and not nd.groups.plant then
if i < minlen or pr:next(1,maxsupport) == 1 then return end
len = i
break
end
end
table.insert(schem.data,{name = tree, param2 = 0})
table.insert(schem.data,{name = "air", prob=0})
for i = 1,len do
table.insert(schem.data,{name = tree, param2 = 12})
if maxsupport - minsupport < minlen then return end
len = math.min(len, maxsupport - 1)
if len < minlen then return end
-- place the tree
minetest.swap_node(pos, {name = tree, param2 = 0})
-- some are hollow:
if vl_hollow_logs.logs and pr:next(1,20) == 1 then
local nam = string.sub(tree, string.find(tree, ":") + 1)
nam = "vl_hollow_logs:"..nam.."_hollow"
if minetest.registered_nodes[nam] then tree = nam end
end
table.insert(schem.data,{name = "air", prob=0})
table.insert(schem.data,{name = "air", prob=0})
for i = 1,len do
table.insert(schem.data,{name = mushrooms[pr:next(1,#mushrooms)], param2 = 12, prob=mprob})
for i = 2,len do
minetest.swap_node(vector.offset(pos, dx * i, 0, dz * i), {name = tree, param2 = param2})
if pr:next(0,255) < vrate then
local side = vector.offset(pos, dx * i + dz, 0, dz * i + dx)
local n = minetest.get_node(side).name
if n == "air" then
minetest.swap_node(side, {name="mcl_core:vine", param2=w1})
end
end
if pr:next(0,255) < vrate then
local side = vector.offset(pos, dx * i - dz, 0, dz * i - dx)
local n = minetest.get_node(side).name
if n == "air" then
minetest.swap_node(side, {name="mcl_core:vine", param2=w2})
end
end
if pr:next(0,255) < mrate then
local top = vector.offset(pos, dx * i, 1, dz * i)
local n = minetest.get_node(top).name
if n == "air" then
minetest.swap_node(top, {name = mushrooms[pr:next(1,#mushrooms)], param2 = 12})
end
end
end
table.insert(schem.data,{name = "air", prob=0})
table.insert(schem.data,{name = "air", prob=0})
for i = 1,len do
table.insert(schem.data,{name = "mcl_core:vine",param2=5, prob=vprob})
end
table.insert(schem.data,{name = "air", prob=0})
table.insert(schem.data,{name = "air", prob=0})
for i = 1,len do
table.insert(schem.data,{name = "air", prob=0})
end
return schem
end
mcl_structures.register_structure("fallen_tree",{
vl_structures.register_structure("fallen_tree",{
priority = 1100, -- after regular trees
place_on = {"group:grass_block"},
terrain_feature = true,
noise_params = {
@ -145,27 +158,13 @@ mcl_structures.register_structure("fallen_tree",{
persist = 0.66
},
flags = "place_center_x, place_center_z",
sidelen = 18,
solid_ground = true,
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
on_place = function(pos,def,pr)
local air_p1 = vector.offset(pos,-def.sidelen/2,1,-def.sidelen/2)
local air_p2 = vector.offset(pos,def.sidelen/2,1,def.sidelen/2)
local air = minetest.find_nodes_in_area(air_p1,air_p2,{"air"})
if #air < ( def.sidelen * def.sidelen ) / 2 then
return false
end
return true
end,
place_func = function(pos,def,pr)
local schem=get_fallen_tree_schematic(pos,pr)
if not schem then return end
return minetest.place_schematic(pos,schem,"random")
end
place_func = place_fallen_tree
})
mcl_structures.register_structure("lavapool",{
vl_structures.register_structure("lavapool",{
place_on = {"group:sand", "group:dirt", "group:stone"},
terrain_feature = true,
noise_params = {
@ -180,12 +179,12 @@ mcl_structures.register_structure("lavapool",{
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr)
place_func = function(pos, _, pr)
return makelake(pos,5,"mcl_core:lava_source",{"group:material_stone", "group:sand", "group:dirt"},"mcl_core:stone",pr)
end
})
mcl_structures.register_structure("water_lake",{
vl_structures.register_structure("water_lake",{
place_on = {"group:dirt","group:stone"},
terrain_feature = true,
noise_params = {
@ -200,12 +199,12 @@ mcl_structures.register_structure("water_lake",{
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr)
place_func = function(pos, _, pr)
return makelake(pos,5,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block"},"mcl_core:dirt_with_grass",pr)
end
})
mcl_structures.register_structure("water_lake_mangrove_swamp",{
vl_structures.register_structure("water_lake_mangrove_swamp",{
place_on = {"mcl_mud:mud"},
biomes = { "MangroveSwamp" },
terrain_feature = true,
@ -221,12 +220,12 @@ mcl_structures.register_structure("water_lake_mangrove_swamp",{
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr)
place_func = function(pos, _, pr)
return makelake(pos,3,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block","mcl_mud:mud"},"mcl_mud:mud",pr,true)
end
})
mcl_structures.register_structure("basalt_column",{
vl_structures.register_structure("basalt_column",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
terrain_feature = true,
spawn_by = {"air"},
@ -244,7 +243,7 @@ mcl_structures.register_structure("basalt_column",{
y_max = mcl_vars.mg_nether_max - 20,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos,def,pr)
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-5,-1,-5),vector.offset(pos,5,-1,5),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"})
table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
@ -265,12 +264,12 @@ mcl_structures.register_structure("basalt_column",{
end
end
end
minetest.bulk_set_node(magma,{name="mcl_nether:magma"})
minetest.bulk_set_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
return true
end
})
mcl_structures.register_structure("basalt_pillar",{
vl_structures.register_structure("basalt_pillar",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
terrain_feature = true,
noise_params = {
@ -286,7 +285,7 @@ mcl_structures.register_structure("basalt_pillar",{
y_max = mcl_vars.mg_nether_max-40,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos,def,pr)
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-2,-1,-2),vector.offset(pos,2,-1,2),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"})
table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
@ -306,13 +305,13 @@ mcl_structures.register_structure("basalt_pillar",{
end
end
end
minetest.bulk_set_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_set_node(magma,{name="mcl_nether:magma"})
minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
return true
end
})
mcl_structures.register_structure("lavadelta",{
vl_structures.register_structure("lavadelta",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
spawn_by = {"mcl_blackstone:basalt","mcl_blackstone:blackstone"},
num_spawn_by = 2,
@ -330,7 +329,7 @@ mcl_structures.register_structure("lavadelta",{
y_max = mcl_vars.mg_nether_max,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos,def,pr)
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos,-10,-1,-10),vector.offset(pos,10,-2,10),{"mcl_blackstone:basalt","mcl_blackstone:blackstone","mcl_nether:netherrack"})
table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
@ -340,7 +339,7 @@ mcl_structures.register_structure("lavadelta",{
for i=1,pr:next(1,#nn) do
table.insert(lava,nn[i])
end
minetest.bulk_set_node(lava,{name="mcl_nether:nether_lava_source"})
minetest.bulk_swap_node(lava,{name="mcl_nether:nether_lava_source"})
local basalt = {}
local magma = {}
for _,v in pairs(lava) do
@ -355,8 +354,8 @@ mcl_structures.register_structure("lavadelta",{
table.insert(magma,v)
end
end
minetest.bulk_set_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_set_node(magma,{name="mcl_nether:magma"})
minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
return true
end
})

View File

@ -0,0 +1,301 @@
# mcl_villages
When creating buildings or farms for use with this mod, you can prevent paths
from crossing areas by using the `mcl_villages:no_paths` block. You may need to
stack them 2 high to prevent all paths. After the village paths have be laid
this block will be replaced by air.
## Building Interfaces
### Parameter
All of the following functions take a table with the following keys.
#### Mandatory
name
: The name to use for the object.
mts
: The path to the mts format schema file.
#### Optional
yadjust
: Y axis adjustment when placing the schema. This can be positive to raise the
placement, or negative to lower it.
If your schema does not contain a ground layer then set this to 1.
no_ground_turnip
: If you don't want the foundation under the building modified, you can disable
the ground turnip by setting this to true.
Mainly useful for small thing such as lamps, planters, etc.
no_clearance
: If you don't want the area around and above the building modified, you can
disable the overground clearance by setting this to true.
Mainly useful for small thing such as lamps, planters, etc.
### mcl_villages.register_lamp(table)
Register a structure to use as a lamp. These will be added to the table used when
adding lamps to paths during village creation.
### mcl_villages.register_bell(table)
Register a structure to use as a bell. These will be added to the table used when
adding the bell during village creation.
There is 1 bell per village.
### mcl_villages.register_well(table)
Register a structure to use as a well. These will be added to the table used when
adding the wells during village creation.
The number of wells is calculated randomly based on the number of beds in the
village. Every 10 beds add 1 to the maximum number.
e.g. 8 beds == 1 well, 15 beds == 1 or 2 wells, 22 beds == 1 to 3 wells, etc.
### mcl_villages.register_building(table)
Register a building used for jobs, houses, or other uses.
The schema is parsed to work out how many jobs and beds are in it.
If you are adding a job site for a custom profession then ensure you call
```mobs_mc.register_villager_profession``` before you register a building using it.
If a building doesn't have any job sites or beds then it may get added during
the house placement phase. This will simply add another building to
the village and will not affect the number of jobs or beds.
#### Additional options
The ```mcl_villages.register_building``` call accepts the following optional
parameters in the table.
min_jobs
: A village will need at least this many jobs to have one of these buildings.
This is used to restrict buildings to bigger villages.
max_jobs
: A village will need less that or equal to (<=) this many jobs to have one of
these buildings.
This is used to restrict buildings to smaller villages.
num_others
: A village will need this many other job sites before you can have another of
these jobs sites.
This is used to influence the ratio of buildings in a village.
is_mandatory
: This ensures that each village will have at least one of these buildings.
### mobs_mc.register_villager_profession(title, table)
**TODO** this should be somewhere else.
This API call allows you to register professions for villagers.
It takes 2 arguments.
1. title - The title to use for the profession.
This mus be unique; the profession will be rejected if this title is already
used.
1. Record - a table containing the details of the profession, it contains the
following fields.
1. name: The name displayed for the profession in the UI.
1. texture: The texture to use for the profession
1. jobsite: the node or group name sued to flag blocks as job sites for this
profession
1. trades: a table containing trades with 1 entry for each trade level.
You can access the current profession and job site data in
```mobs_mc.professions``` and ```mobs_mc.jobsites```.
### mcl_villages.register_on_village_placed(func)
This function allows registering functions to be called after a village is
laid out.
Note that the village may not be completed as the building post processing is
non-deterministic to avoid overloading the server.
`settlement_info` is a table containing data for all the buildings in the
village. The bell is always the first entry in the table.
`blockseed` is the block seed for the chunk the village was generated for.
Villages can extend outside of this chunk.
```lua
local function my_village_hook(settlement_info, blockseed)
minetest.log("The village has " .. #settlement_info .. " buildings in it!")
end
mcl_villages.register_on_village_placed(my_village_hook)
```
### mcl_villages.register_on_villager_spawned(func)
This function allows registering functions to be called after a villager is
placed as part of village generation.
`villager_ent` is the entity created by `minetest.add_entity`.
`blockseed` is the block seed for the chunk the village was generated for.
Villages can extend outside of this chunk.
```lua
local function my_villager_hook(villager_ent, blockseed)
local l = villager_ent:get_luaentity()
minetest.log("The villager's id is " .. l._id)
end
mcl_villages.register_on_villager_spawned(my_villager_hook)
```
## Farm Interface
These functions aid creating crops for use use in farms placed during village
generation.
### mcl_villages.get_crop_types()
This allows checking what crop types are supported.
Currently they are: grain, root, gourd, flower, bush, tree.
Placement of gourds should take in to consideration the way they fruit.
### mcl_villages.get_crops()
Returns a table containing all registered crops.
### mcl_villages.get_weighted_crop(biome, crop_type, pr)
Gets a random crop for the biome and crop type.
### mcl_villages.register_crop(crop_def)
Registers a crop for use on farms.
crop_def is a table with the following fields:
* `node` the name of the crop node to place. e.g. `mcl_farming:wheat_1`.
* `crop_type` the type crop. e.g. `grain`
* `biomes` a table containing the weighting to give the crop.
* Supported biome values are:
* acacia
* bamboo
* desert
* jungle
* plains
* savanna
* spruce
* If you leave a biome out ot he definition then the crop will not be available in that biome.
e.g.
```lua
mcl_villages.register_crop({
type = "grain",
node = "mcl_farming:wheat_1",
biomes = {
acacia = 10,
bamboo = 10,
desert = 10,
jungle = 10,
plains = 10,
savanna = 10,
spruce = 10,
},
})
```
### Creating farms with replaceable crops
To create a farm that will utilize registered crops you follow the basic process
for creating a farm, but you leave out the crops.
Once you have your farm constructed then instead of placing crops you place blocks named `mcl_villages:crop_*` over the dirt in the farm.
Each crop type has 8 blocks that can be used for it. This allows, but does not
guarantee, variety of crops in a farm.
Each of the crop tiles has an image of a entity that it represents. This image
is representative, not explicit.
i.e. The root crop tiles have an image of a carrot on them, but they will be
swapped for a random root crop, not always carrots.
Each specific node will be replaced by a single item.
e.g. if you use `mcl_villages:crop_root_1` and `mcl_villages:crop_root_2` in your farm then all there will be at most 2 types of root crops on the farm.
It is random, so both types may get replaced by the same crop.
Remember that gourds affect 5 nodes when they crop; a good farmer won't plant
anything on the 4 nodes a fruit wil form and your farm should not do that
either.
Once you have saved the schema for your farm you register it with the building interface.
e.g.
```lua
mcl_villages.register_building({
name = "my_farm",
mts = schem_path .. "/my_farm.mts",
num_others = 3,
})
```
When a village is generated there will be a chance your farm will be placed, any
crop blocks will be replaced by biome appropriate crops.
If a crop cannot be found for a crop type in a biome, then a default will be
used. This ensure all farming blocks are full, ven if it's al the same crop.
The default is wheat.
## Village Layout
There are two methods for layout out villages, circle layout is more likely to be
used for small villages and grid for large villages.
The circle layout uses circles (surprise) to calculate if buildings overlap. It
creates fairly widely spaced layouts.
The grid layout uses a predetermined grid layout to positions buildings and uses
AreaStore to adjust building position if there are collisions.
The predetermined grid is below, position 0 is the bell, the other numbers are the order of placement.
||||||||
| -- | -- | -- | -- | -- | -- | -- |
|48|41|33|25|29|37|45|
|40|17|13| 9|11|15|43|
|32|19| 5| 1| 3|22|35|
|28|23| 7| 0| 8|24|27|
|36|21| 4| 2| 6|20|31|
|44|16|12|10|14|18|39|
|46|38|30|26|34|42|47|

View File

@ -21,7 +21,6 @@ Basic conversion of Settlements mod for compatibility with VoxeLibre, plus new s
Seed-based Village Generation, multi-threading, bugfixes: kay27
=========================
version: 0.1 alpha
@ -43,3 +42,5 @@ This mod is based on "ruins" by BlockMen
Completely new schematics for VoxeLibre:
MysticTempest - CC-BY-SA 4.0
New schematics and improvements in mineclonia by codiac.

View File

@ -0,0 +1,154 @@
mcl_villages.schematic_houses = {}
mcl_villages.schematic_jobs = {}
mcl_villages.schematic_lamps = {}
mcl_villages.schematic_bells = {}
mcl_villages.schematic_wells = {}
mcl_villages.on_village_placed = {}
mcl_villages.on_villager_placed = {}
mcl_villages.mandatory_buildings = {}
local S = minetest.get_translator(minetest.get_current_modname())
local function job_count(schem_lua)
local count = 0
for _, n in pairs(mobs_mc.jobsites) do
if string.find(n, "^group:") then
if n == "group:cauldron" then
count = count + select(2, string.gsub(schem_lua, '"mcl_cauldrons:cauldron', ""))
else
local name = string.sub(n, 6, -1)
local num = select(2, string.gsub(schem_lua, name, ""))
if num then
minetest.log("info", string.format("[mcl_villages] Guessing how to handle %s counting it as %d job sites", name, num))
count = count + num
else
minetest.log("warning", string.format("[mcl_villages] Don't know how to handle group %s counting it as 1 job site", n))
count = count + 1
end
end
else
count = count + select(2, string.gsub(schem_lua, '{name="' .. n .. '"', ""))
end
end
return count
end
local function load_schema(name, mts)
local schem_lua = minetest.serialize_schematic(mts, "lua", { lua_use_comments = false, lua_num_indent_spaces = 0 }) .. " return schematic"
-- MCLA node names to VL for import
if string.find(mts, "new_villages/") then
for _, sub in pairs(mcl_villages.mcla_to_vl) do
schem_lua = schem_lua:gsub(sub[1], sub[2])
end
end
local schematic = loadstring(schem_lua)()
return { name = name, size = schematic.size, schem_lua = schem_lua }
end
local all_optional = { "yadjust", "no_ground_turnip", "no_clearance", "rotation_offset" }
local function set_all_optional(record, data)
for _, field in ipairs(all_optional) do
if record[field] then data[field] = record[field] end
end
end
local function set_mandatory(record, type)
if record['is_mandatory'] then
if not mcl_villages.mandatory_buildings[type] then mcl_villages.mandatory_buildings[type] = {} end
table.insert(mcl_villages.mandatory_buildings[type], record["name"])
end
end
function mcl_villages.register_lamp(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
table.insert(mcl_villages.schematic_lamps, data)
set_mandatory(record, 'lamps')
end
function mcl_villages.register_bell(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
table.insert(mcl_villages.schematic_bells, data)
set_mandatory(record, 'bells')
end
function mcl_villages.register_well(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
table.insert(mcl_villages.schematic_wells, data)
set_mandatory(record, 'wells')
end
local optional_fields = { "min_jobs", "max_jobs", "num_others", "is_mandatory" }
function mcl_villages.register_building(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
for _, field in ipairs(optional_fields) do
if record[field] then data[field] = record[field] end
end
local str = data["schem_lua"]
local num_beds = select(2, string.gsub(str, '"mcl_beds:bed_[^"]+_bottom"', ""))
if num_beds > 0 then data["num_beds"] = num_beds end
local job_count = job_count(data["schem_lua"])
if job_count > 0 then
data["num_jobs"] = job_count
table.insert(mcl_villages.schematic_jobs, data)
set_mandatory(record, 'jobs')
else
table.insert(mcl_villages.schematic_houses, data)
set_mandatory(record, 'houses')
end
end
local crop_list = {}
function mcl_villages.register_crop(crop_def)
local crops = crop_list[crop_def.type] or {}
for biome, weight in pairs(crop_def.biomes) do
if crops[biome] == nil then crops[biome] = {} end
crops[biome][crop_def.node] = weight
end
crop_list[crop_def.type] = crops
end
function mcl_villages.get_crop_types()
local ret = {}
for k, _ in pairs(crop_list) do
table.insert(ret, k)
end
return ret
end
function mcl_villages.get_weighted_crop(biome, crop_type, pr)
local crops = crop_list[crop_type]
if not crops then return end -- unknown crop
local crops = crops[biome] or crops["plains"]
local total = 0
for _, weight in pairs(crops) do total = total + weight end
local rand = pr:next(0, 1e7) * 1e-7 * total
for node, weight in pairs(crops) do
if rand <= weight then
return node
end
rand = rand - weight
end
return
end
function mcl_villages.register_on_village_placed(func)
table.insert(mcl_villages.on_village_placed, func)
end
function mcl_villages.register_on_villager_spawned(func)
table.insert(mcl_villages.on_villager_placed, func)
end

View File

@ -1,344 +1,358 @@
--[[
-------------------------------------------------------------------------------
-- 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)
local min_jobs = tonumber(minetest.settings:get("vl_villages_min_jobs")) or 2
local max_jobs = tonumber(minetest.settings:get("vl_villages_max_jobs")) or 14
local placement_priority = minetest.settings:get("vl_villages_placement_priority") or "houses" -- houses is safer for villagers at night
local max_height_difference = 40 -- at distance 40. In the center, half as much
-- Disable special junglewood for now.
-- special material for spawning npcs
-- schem_lua = schem_lua:gsub("mcl_core:junglewood",
-- "settlements:junglewood")
--
local S = minetest.get_translator(minetest.get_current_modname())
-- 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
minetest.log("action", "Cannot build village at: " .. minetest.pos_to_string(center))
return false
local function add_building(settlement, building, count_buildings)
if placement_priority == "jobs" then
table.insert(settlement, building)
else
minetest.log("action", "Village built.")
--minetest.log("action", "Build village at: " .. minetest.pos_to_string(center) .. " with surface material: " .. surface_material)
table.insert(settlement, 1, building) -- insert "backwards" - todo: add table.reverse
end
count_buildings[building.name] = (count_buildings[building.name] or 0) + 1
count_buildings.num_jobs = count_buildings.num_jobs + (building.num_jobs or 0)
count_buildings.num_beds = count_buildings.num_beds + (building.num_beds or 0)
if building.group then
count_buildings[building.group] = (count_buildings[building.group] or 0) + 1
end
end
-- 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
local function layout_town(minp, maxp, pr, input_settlement)
local center = vector.new(pr:next(minp.x + 24, maxp.x - 24), maxp.y, pr:next(minp.z + 24, maxp.z - 24))
minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(minp).." - "..minetest.pos_to_string(maxp))
local possible_rotations = {"0", "90", "180", "270"}
local center_surface
local settlement = {}
-- 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)
local x, y, z, r, lastr = center.x, center.y, center.z, 0, 99
local mindist = 3
if #input_settlement >= 12 then mindist = 2 end
-- draw j circles around center and increase radius by math.random(2,4)
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)
local steps = math.min(math.floor(math.pi * 2 * r / 2), 30) -- try up to 30 angles
for a = 0, steps - 1 do
if #settlement == #input_settlement then break end -- everything placed
local angle = a * 71 / steps * math.pi * 2 -- prime to increase randomness
local cpos = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5))
local building = table.copy(input_settlement[#settlement + 1])
local size = vector.copy(building.size)
--local rotation = possible_rotations[pr:next(1, #possible_rotations)]
-- instead of random rotations, rotating doors to the center makes the village
-- more defensive and hence safer for the poor villagers, even though less random
-- case distinction is simpler and faster than trigonometry here:
local rotation = building.rotation_offset or 0
if math.abs(cpos.z-center.z) > math.abs(cpos.x-center.x) then
rotation = rotation + (cpos.z <= center.z and 0 or 2) -- zero indexed for modulo below
else
chunks[chunk_number] = true
pos_surface, surface_material = settlements.find_surface(pos1, true)
rotation = rotation + (cpos.x <= center.x and 1 or 3) -- zero indexed for modulo below
end
if not pos_surface then break end
rotation = possible_rotations[rotation % 4 + 1]
--minetest.log("action", building.name.." at "..minetest.pos_to_string(cpos).." rotation: "..rotation.." to "..minetest.pos_to_string(center).." "..minetest.pos_to_string(center-cpos))
if rotation == "90" or rotation == "270" then size.x, size.z = size.z, size.x end
local tlpos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
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
-- ensure we have 3 space for terraforming, and avoid problems with VoxelManip
if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x
and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then
local pos, surface_material = vl_terraforming.find_level(cpos, size, 6)
if pos and pos.y + size.y > maxp.y then pos = nil end
-- check distance to other buildings. Note that we still want to add baseplates etc.
if pos and mcl_villages.surface_mat[surface_material.name] and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then
-- use town bell as new reference point for placement height
if #settlement == 0 then
center_surface, y = cpos, math.min(maxp.y, pos.y + max_height_difference + 1)
end
-- limit height differences to town center, but gradually allow more
if math.abs(pos.y - center_surface.y) <= max_height_difference * (0.5 + r/80) then
local minp = vector.offset(pos, -math.floor((size.x-1)/2), building.yadjust, -math.floor((size.z-1)/2))
building.minp = minp
building.maxp = vector.offset(minp, size.x, size.y, size.z)
building.pos = pos
building.size = size
building.rotation = rotation
building.surface_mat = surface_material
table.insert(settlement, building)
-- minetest.log("verbose", "[mcl_villages] Planning "..schema["name"].." at "..minetest.pos_to_string(pos))
lastr = r
else
minetest.log("verbose", "Too large height difference "..math.abs(pos.y - center_surface.y).." at distance "..r)
end
end
end
if number_of_buildings == number_built then
break
end
end
if number_built >= number_of_buildings then
r = r + pr:next(2,4)
if r > lastr + 20 then -- too disconnected
minetest.log("verbose", "Disconnected village "..r.." > "..lastr)
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)
-- minetest.log("verbose", "Planned "..#input_settlement.." buildings, placed "..#settlement)
if #settlement < #input_settlement and #settlement < 6 then
minetest.log("action", "[mcl_villages] Bad village location, could only place "..#settlement.." buildings at "..minetest.pos_to_string(center))
return
end
minetest.log("warning", "[mcl_villages] Attempt to 'construct' inexistant nodes: " .. name)
minetest.log("action", "[mcl_villages] village plan completed at " .. minetest.pos_to_string(center))
return settlement
end
local function spawn_iron_golem(pos)
--minetest.log("action", "Attempt to spawn iron golem.")
local p = minetest.find_node_near(pos,50,"mcl_core:grass_path")
if p then
local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity()
if l then
l._home = p
function mcl_villages.create_site_plan(minp, maxp, pr)
local settlement = {}
-- initialize all settlement_info table
local count_buildings = { num_jobs = 0, num_beds = 0, target_jobs = pr:next(min_jobs, max_jobs) }
-- first building is townhall in the center
local bindex = pr:next(1, #mcl_villages.schematic_bells)
local bell_info = table.copy(mcl_villages.schematic_bells[bindex])
if mcl_villages.mandatory_buildings['jobs'] then
for _, bld_name in pairs(mcl_villages.mandatory_buildings['jobs']) do
local building_info = info_for_building(bld_name, mcl_villages.schematic_jobs)
add_building(settlement, building_info, count_buildings)
end
end
end
local function spawn_villagers(minp,maxp)
--minetest.log("action", "Attempt to spawn villagers.")
local beds=minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20),{"mcl_beds:bed_red_bottom"})
for _,bed in pairs(beds) do
local m = minetest.get_meta(bed)
if m:get_string("villager") == "" then
local v=minetest.add_entity(bed,"mobs_mc:villager")
if v then
local l=v:get_luaentity()
l._bed = bed
m:set_string("villager",l._id)
end
while count_buildings.num_jobs < count_buildings.target_jobs do
local rindex = pr:next(1, #mcl_villages.schematic_jobs)
local building_info = mcl_villages.schematic_jobs[rindex]
if
(building_info.min_jobs == nil or count_buildings.target_jobs >= building_info.min_jobs)
and (building_info.max_jobs == nil or count_buildings.target_jobs <= building_info.max_jobs)
and (
building_info.num_others == nil
or (count_buildings[building_info.group or building_info.name] or 0) == 0
or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs
)
then
add_building(settlement, building_info, count_buildings)
end
end
end
local function fix_village_water(minp,maxp)
local palettenodes = minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20), "group:water_palette")
for _, palettenodepos in pairs(palettenodes) do
local palettenode = minetest.get_node(palettenodepos)
minetest.set_node(palettenodepos, {name = palettenode.name})
end
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")
construct_node(p1, p2, "mcl_smoker:smoker")
construct_node(p1, p2, "mcl_barrels:barrel_closed")
construct_node(p1, p2, "mcl_blast_furnace:blast_furnace")
construct_node(p1, p2, "mcl_brewing:stand_000")
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)
if mcl_villages.mandatory_buildings['houses'] then
for _, bld_name in pairs(mcl_villages.mandatory_buildings['houses']) do
local building_info = info_for_building(bld_name, mcl_villages.schematic_houses)
add_building(settlement, building_info, count_buildings)
end
end
while count_buildings.num_beds <= count_buildings.num_jobs do
local rindex = pr:next(1, #mcl_villages.schematic_houses)
local building_info = mcl_villages.schematic_houses[rindex]
if
(building_info.min_jobs == nil or count_buildings.target_jobs >= building_info.min_jobs)
and (building_info.max_jobs == nil or count_buildings.target_jobs <= building_info.max_jobs)
and (
building_info.num_others == nil
or (count_buildings[building_info.group or building_info.name] or 0) == 0
or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs
)
then
add_building(settlement, building_info, count_buildings)
end
end
-- Based on number of villagers
local num_wells = pr:next(1, math.ceil(count_buildings.num_beds / 10))
for _ = 1, num_wells do
local windex = pr:next(1, #mcl_villages.schematic_wells)
local cur_schem = table.copy(mcl_villages.schematic_wells[windex])
table.insert(settlement, pr:next(1, #settlement), cur_schem)
end
if placement_priority == "randomg" then
settlement = mcl_villages.shuffle(settlement, pr)
end
table.insert(settlement, 1, bell_info)
return layout_town(minp, maxp, pr, settlement)
end
function settlements.place_schematics(settlement_info, pr)
local building_all_info
local function init_nodes(p1, p2, pr)
vl_structures.construct_nodes(p1, p2, {
"mcl_itemframes:item_frame",
"mcl_itemframes:glow_item_frame",
"mcl_furnaces:furnace",
"mcl_anvils:anvil",
"mcl_books:bookshelf",
"mcl_armor_stand:armor_stand",
-- jobsite: "mcl_smoker:smoker",
-- jobsite: "mcl_barrels:barrel_closed",
-- jobsite: "mcl_blast_furnace:blast_furnace",
-- jobsite: "mcl_brewing:stand_000",
})
for i, built_house in ipairs(settlement_info) do
local is_last = i == #settlement_info
-- Support mods with custom job sites
local job_sites = minetest.find_nodes_in_area(p1, p2, mobs_mc.jobsites)
for _, v in pairs(job_sites) do vl_structures.init_node_construct(v) end
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 nodes = vl_structures.construct_nodes(p1, p2, {"mcl_chests:chest_small", "mcl_chests:chest" })
for _, n in pairs(nodes) do mcl_villages.fill_chest(n, pr) end
end
function mcl_villages.place_schematics(sminp, smaxp, settlement, blockseed, pr)
-- first building is always the bell
local bell_pos = vector.offset(settlement[1].minp, math.floor(settlement[1].size.x/2), 0, math.floor(settlement[1].size.z/2))
for i, building in ipairs(settlement) do
local minp, cpos, maxp, size, rotation = building.minp, building.pos, building.maxp, building.size, building.rotation
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
-- adjust the schema to match location and biome
local surface_material = building.surface_mat or {name = "mcl_core:dirt" }
local platform_material = building.platform_mat or building.surface_mat or {name = "mcl_core:stone" }
local schem_lua = building.schem_lua
schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material.name..'"')
schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material.name..'"') -- also keeping param2 would be nicer, grass color
schem_lua = mcl_villages.substitute_materials(cpos, schem_lua, pr)
local schematic = loadstring(schem_lua)()
local is_belltower = building_all_info["name"] == "belltower"
-- the foundation and air space for the building was already built before
-- minetest.log("action", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material.name)
minetest.place_schematic(minp, schematic, rotation, nil, true, { place_center_x = false, place_center_y = false, place_center_z = false })
mcl_villages.store_path_ends(minp, maxp, cpos, blockseed, bell_pos)
mcl_villages.increase_no_paths(minp, maxp) -- help the path finder
end
-- build foundation for the building an make room above
-- Path planning and placement
mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome), sminp, smaxp)
mcl_villages.clean_no_paths(sminp, smaxp)
-- Clean up paths and initialize nodes
for i, building in ipairs(settlement) do
init_nodes(building.minp, building.maxp, pr)
end
mcl_structures.place_schematic(
pos,
schematic,
rotation,
nil,
true,
nil,
function(p1, p2, size, rotation, pr)
if is_belltower then
spawn_iron_golem(p1)
-- Replace center block with a temporary block, which will be used run delayed actions
local block_name = minetest.get_node(bell_pos).name -- to restore the node afterwards
minetest.swap_node(bell_pos, { name = "mcl_villages:village_block" })
local meta = minetest.get_meta(bell_pos)
meta:set_string("node_type", block_name)
meta:set_string("blockseed", blockseed)
meta:set_string("infotext", S("The timer for this village has not run yet!"))
minetest.get_node_timer(bell_pos):start(1.0)
end
minetest.register_node("mcl_villages:village_block", {
drawtype = "glasslike",
groups = { not_in_creative_inventory = 1 },
light_source = 14, -- This is a light source so that lamps don't get placed near it
-- Somethings don't work reliably when done in the map building
-- so we use a timer to run them later when they work more reliably
-- e.g. spawning mobs, running minetest.find_path
on_timer = function(pos, _)
local meta = minetest.get_meta(pos)
mcl_villages.post_process_village(meta:get_string("blockseed"))
-- not swap_node, to clear metadata afterwards
minetest.set_node(pos, { name = meta:get_string("node_type") })
return false
end,
})
function mcl_villages.post_process_village(blockseed)
local village_info = mcl_villages.get_village(blockseed)
if not village_info then return end
-- minetest.log("Postprocessing village")
local settlement_info = village_info.data
local jobs, beds = {}, {}
local bell_pos = vector.copy(settlement_info[1].pos)
local bell = vector.offset(bell_pos, 0, 2, 0)
local biome_name = minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome)
-- Spawn Golem
local l = minetest.add_entity(bell, "mobs_mc:iron_golem"):get_luaentity()
if l then
l._home = bell
else
minetest.log("info", "Could not create a golem!")
end
-- Spawn cats
local sp = minetest.find_nodes_in_area_under_air(vector.offset(bell, -20, -10, -20),vector.offset(bell, 20, 10, 20), { "group:opaque" })
for _ = 1, math.random(3) do
local v = minetest.add_entity(vector.offset(sp[math.random(#sp)], 0, 1, 0), "mobs_mc:cat")
if v and v:get_luaentity() then
v:get_luaentity()._home = bell_pos -- help them stay local
else
minetest.log("info", "Could not spawn a cat")
break
end
end
-- collect beds and job sites
for _, building in pairs(settlement_info) do
local minp, maxp = building.minp, building.maxp
if building.num_jobs then
local jobsites = minetest.find_nodes_in_area(minp, maxp, mobs_mc.jobsites)
for _, job_pos in pairs(jobsites) do table.insert(jobs, job_pos) end
end
if building.num_beds then
local bld_beds = minetest.find_nodes_in_area(minp, maxp, { "group:bed" })
for _, bed_pos in pairs(bld_beds) do
local bed_group = minetest.get_item_group(minetest.get_node(bed_pos).name, "bed")
-- We only spawn at bed bottoms, 1 is bottom, 2 is top
if bed_group == 1 then table.insert(beds, bed_pos) end
end
end
end
-- TODO: shuffle jobs?
-- minetest.log("beds: "..#beds.." jobsites: "..#jobs)
if beds then
for _, bed_pos in pairs(beds) do
minetest.forceload_block(bed_pos, true)
local m = minetest.get_meta(bed_pos)
m:set_string("bell_pos", minetest.pos_to_string(bell_pos))
if m:get_string("villager") == "" then
local v = minetest.add_entity(vector.offset(bed_pos, 0, 0.06, 0), "mobs_mc:villager")
if v then
local l = v:get_luaentity()
l._bed = bed_pos
l._bell = bell_pos
m:set_string("villager", l._id)
m:set_string("infotext", S("A villager sleeps here"))
local job_pos = table.remove(jobs, 1)
if job_pos then villager_employ(l, job_pos) end -- HACK: merge more MCLA villager job code?
for _, callback in pairs(mcl_villages.on_villager_placed) do callback(v, blockseed) end
else
init_nodes(p1, p2, size, rotation, pr)
spawn_villagers(p1,p2)
fix_village_water(p1,p2)
minetest.log("info", "Could not create a villager!")
end
end,
pr
)
else
minetest.log("info", "bed already owned by " .. m:get_string("villager")) -- should not happen unless villages overlap
end
end
end
end
-- Terraform for an entire village
function mcl_villages.terraform(settlement, pr)
-- TODO: sort top-down, then bottom-up, or opposite?
-- we make the foundations 2 node wider than necessary, to have one node for path laying
for i, building in ipairs(settlement) do
if not building.no_clearance then
local pos, size = building.pos, building.size
pos = vector.offset(pos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
-- TODO: allow different clearance for different buildings?
vl_terraforming.clearance(pos.x-1, pos.y, pos.z-1, size.x+2, size.y, size.z+2, 2, building.surface_mat, building.dust_mat, pr)
end
end
for i, building in ipairs(settlement) do
if not building.no_ground_turnip then
local pos, size = building.pos, building.size
local surface_mat = building.surface_mat
local platform_mat = building.platform_mat or { name = mcl_villages.foundation_materials[surface_mat.name] or "mcl_core:dirt" }
local stone_mat = building.stone_mat or { name = mcl_villages.stone_materials[surface_mat.name] or "mcl_core:stone" }
local dust_mat = building.dust_mat
building.platform_mat = platform_mat -- remember for use in schematic placement
building.stone_mat = stone_mat
pos = vector.offset(pos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
vl_terraforming.foundation(pos.x-2, pos.y, pos.z-2, size.x+4, -5, size.z+4, 2, surface_mat, platform_mat, stone_mat, dust_mat, pr)
end
end
end

View File

@ -1,77 +1,292 @@
-- 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
-- legacy type in old schematics
minetest.register_alias("mcl_villages:stonebrickcarved", "mcl_core:stonebrickcarved")
--[[ 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
--
mcl_villages.surface_mat = {}
mcl_villages.surface_mat["mcl_core:andesite"] = true
mcl_villages.surface_mat["mcl_core:diorite"] = true
mcl_villages.surface_mat["mcl_core:dirt"] = true
mcl_villages.surface_mat["mcl_core:dirt_with_grass"] = true
--mcl_villages.surface_mat["mcl_core:dirt_with_dry_grass"] = true
mcl_villages.surface_mat["mcl_core:dirt_with_grass_snow"] = true
--mcl_villages.surface_mat["mcl_core:dry_dirt_with_grass"] = true
mcl_villages.surface_mat["mcl_core:grass_path"] = true
mcl_villages.surface_mat["mcl_core:granite"] = true
mcl_villages.surface_mat["mcl_core:podzol"] = true
mcl_villages.surface_mat["mcl_core:redsand"] = true
mcl_villages.surface_mat["mcl_core:sand"] = true
mcl_villages.surface_mat["mcl_core:sandstone"] = true
mcl_villages.surface_mat["mcl_core:sandstonesmooth"] = true
mcl_villages.surface_mat["mcl_core:sandstonesmooth2"] = true
--mcl_villages.surface_mat["mcl_core:silver_sand"] = true
--mcl_villages.surface_mat["mcl_core:snow"] = true
mcl_villages.surface_mat["mcl_core:stone"] = true
mcl_villages.surface_mat["mcl_core:stone_with_coal"] = true
mcl_villages.surface_mat["mcl_core:stone_with_iron"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_orange"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_red"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_white"] = true
-- substitute foundation materials
mcl_villages.foundation_materials = {}
mcl_villages.foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
mcl_villages.foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone"
-- substitute stone materials in foundation
mcl_villages.stone_materials = {}
mcl_villages.default_crop = "mcl_farming:wheat_1"
--
-- path to schematics
-- Biome based block substitutions
--
schem_path = settlements.modpath.."/schematics/"
--
-- list of schematics
--
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true)
-- TODO maybe this should be in the biomes?
mcl_villages.biome_map = {
BambooJungle = "bamboo",
BambooJungleEdge = "bamboo",
BambooJungleEdgeM = "bamboo",
BambooJungleM = "bamboo",
settlements.schematic_table = {
{name = "belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 9, hsize = 14, max_num = 0 , rplc = basic_pseudobiome_villages },
{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages },
{name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, 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 = 9, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages },
{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 4, 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 = 9, 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 = 8, hheight = 8, hsize = 13, max_num = 0.7 , rplc = basic_pseudobiome_villages },
{name = "tavern", mts = schem_path.."tavern.mts", hwidth = 12, 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 },
Jungle = "jungle",
JungleEdge = "jungle",
JungleEdgeM = "jungle",
JungleM = "jungle",
Desert = "desert",
Savanna = "acacia",
SavannaM = "acacia",
Mesa = "hardened_clay",
MesaBryce = "hardened_clay ",
MesaPlateauF = "hardened_clay",
MesaPlateauFM = "hardened_clay",
MangroveSwamp = "mangrove",
RoofedForest = "dark_oak",
BirchForest = "birch",
BirchForestM = "birch",
ColdTaiga = "spruce",
ExtremeHills = "spruce",
ExtremeHillsM = "spruce",
IcePlains = "spruce",
IcePlainsSpikes = "spruce",
MegaSpruceTaiga = "spruce",
MegaTaiga = "spruce",
Taiga = "spruce",
["ExtremeHills+"] = "spruce",
CherryGrove = "cherry",
-- no change
--FlowerForest = "oak",
--Forest = "oak",
--MushroomIsland = "",
--Plains = "oak",
--StoneBeach = "",
--SunflowerPlains = "oak",
--Swampland = "oak",
}
--
-- maximum allowed difference in height for building a sttlement
--
max_height_difference = 56
--
--
--
half_map_chunk_size = 40
--quarter_map_chunk_size = 20
mcl_villages.vl_to_mcla = {
{ '"mcl_core:tree"', '"mcl_trees:tree_oak"'},
{ '"mcl_core:darktree"', '"mcl_trees:tree_dark_oak"'},
{ '"mcl_core:wood"', '"mcl_trees:wood_oak"'},
{ '"mcl_core:darkwood"', '"mcl_trees:wood_dark_oak"'},
{ '"mcl_fences:fence', '"mcl_fences:oak_fence'},
{ '"mcl_stairs:stair_wood"', '"mcl_stairs:stair_oak"'},
{ '"mcl_stairs:stair_wood_', '"mcl_stairs:stair_oak_'},
{ '"mcl_stairs:slab_wood"', '"mcl_stairs:slab_oak"'},
{ '"mcl_stairs:slab_wood_', '"mcl_stairs:slab_oak_'},
{ '"mcl_doors:wooden_door_', '"mcl_doors:door_oak_'},
{ '"mcl_doors:trapdoor_', '"mcl_doors:trapdoor_oak_'},
{ '"xpanes:bar', '"mcl_panes:bar' },
{ '"xpanes:pane', '"mcl_panes:pane' },
{ '"mcl_itemframes:item_frame"', '"mcl_itemframes:frame"' },
{ '"mesecons_pressureplates:pressure_plate_wood_', '"mesecons_pressureplates:pressure_plate_oak_'},
-- tree types
{ '"mcl_core:([a-z]*)tree"', '"mcl_trees:tree_%1"'},
{ '"mcl_core:([a-z]*)wood"', '"mcl_trees:wood_%1"'},
{ '"mcl_stairs:stair_darkwood"', '"mcl_stairs:stair_dark_oak"'},
{ '"mcl_stairs:stair_([a-z]*)wood"', '"mcl_stairs:stair_%1"'},
{ '"mcl_bamboo:bamboo_fence', '"mcl_fences:bamboo_fence'},
{ '"mcl_bamboo:bamboo_plank', '"mcl_core:bamboowood'},
{ '"mcl_bamboo:bamboo_block', '"mcl_core:bambootree'},
{ '"mcl_stairs:stair_bamboo_plank', '"mcl_stairs:stair_bamboo'},
{ '"mcl_bamboo:pressure_plate_bamboo_wood_', '"mesecons_pressureplates:pressure_plate_bamboo_'},
{ '"mcl_bamboo:bamboo_trapdoor', '"mcl_doors:trapdoor_bamboo'},
{ '"mcl_bamboo:bamboo_door', '"mcl_doors:door_bamboo'},
}
mcl_villages.mcla_to_vl = {
-- bidirectional
{ '"mcl_trees:tree_oak"', '"mcl_core:tree"'},
{ '"mcl_trees:tree_dark_oak"', '"mcl_core:darktree"'},
{ '"mcl_trees:wood_oak"', '"mcl_core:wood"'},
{ '"mcl_trees:wood_dark_oak"', '"mcl_core:darkwood"'},
{ '"mcl_fences:oak_fence', '"mcl_fences:fence'},
{ '"mcl_stairs:stair_oak"', '"mcl_stairs:stair_wood"'},
{ '"mcl_stairs:stair_oak_bark', '"mcl_stairs:stair_tree_bark'},
{ '"mcl_stairs:stair_oak_', '"mcl_stairs:stair_wood_'},
{ '"mcl_stairs:slab_oak"', '"mcl_stairs:slab_wood"'},
{ '"mcl_stairs:slab_oak_', '"mcl_stairs:slab_wood_'},
{ '"mcl_doors:door_oak_', '"mcl_doors:wooden_door_'},
{ '"mcl_doors:trapdoor_oak_', '"mcl_doors:trapdoor_'},
{ '"mcl_panes:bar', '"xpanes:bar'},
{ '"mcl_panes:pane', '"xpanes:pane'},
{ '"mcl_itemframes:frame"', '"mcl_itemframes:item_frame"'},
{ '"mesecons_pressureplates:pressure_plate_oak_', '"mesecons_pressureplates:pressure_plate_wood_'},
-- tree types
{ '"mcl_trees:tree_([a-z]*)"', '"mcl_core:%1tree"'},
{ '"mcl_trees:wood_([a-z]*)"', '"mcl_core:%1wood"'},
{ '"mcl_stairs:stair_birch(["_])', '"mcl_stairs:stair_birchwood%1'},
{ '"mcl_stairs:stair_spruce(["_])', '"mcl_stairs:stair_sprucewood%1'},
{ '"mcl_stairs:stair_dark_oak(["_])', '"mcl_stairs:stair_darkwood%1'},
{ '"mcl_stairs:stair_jungle(["_])', '"mcl_stairs:stair_junglewood%1'},
{ '"mcl_stairs:stair_acacia(["_])', '"mcl_stairs:stair_acaciawood%1'},
{ '"mcl_fences:bamboo_fence', '"mcl_bamboo:bamboo_fence'},
{ '"mcl_core:bamboowood', '"mcl_bamboo:bamboo_plank'},
{ '"mcl_core:bambootree', '"mcl_bamboo:bamboo_block'},
{ '"mcl_stairs:stair_bamboo', '"mcl_stairs:stair_bamboo_plank'},
{ '"mesecons_pressureplates:pressure_plate_bamboo_', '"mcl_bamboo:pressure_plate_bamboo_wood_'},
{ '"mcl_doors:trapdoor_bamboo', '"mcl_bamboo:bamboo_trapdoor'},
{ '"mcl_doors:door_bamboo', '"mcl_bamboo:bamboo_door'},
}
mcl_villages.material_substitions = {
desert = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1?
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:birch_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:birch_door%1"' },
{ "mcl_core:cobble", "mcl_core:sandstone" },
{ '"mcl_stairs:stair_cobble([^"]*)"', '"mcl_stairs:stair_sandstone%1"' },
{ '"mcl_walls:cobble([^"]*)"', '"mcl_walls:sandstone%1"' },
{ '"mcl_stairs:slab_cobble([^"]*)"', '"mcl_stairs:slab_sandstone%1"' },
{ '"mcl_core:stonebrick"', '"mcl_core:redsandstone"' },
{ '"mcl_core:stonebrick_([^"]+)"', '"mcl_core:redsandstone_%1"' },
{ '"mcl_walls:stonebrick([^"]*)"', '"mcl_walls:redsandstone%1"' },
{ '"mcl_stairs:stair_stonebrick"', '"mcl_stairs:stair_redsandstone"' },
{ '"mcl_stairs:stair_stonebrick_([^"]+)"', '"mcl_stairs:stair_redsandstone_%1"' },
{ '"mcl_stairs:slab_brick_block([^"]*)"', '"mcl_core:redsandstonesmooth2%1"' },
{ '"mcl_core:brick_block"', '"mcl_core:redsandstonesmooth2"' },
{ "mcl_trees:tree_oak", "mcl_core:redsandstonecarved" },
{ "mcl_trees:wood_oak", "mcl_core:redsandstonesmooth" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
{ '"mcl_stairs:stair_oak_bark([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1?
{ '"mcl_core:leaves"', '"air"' }, -- addition to MCLA
},
spruce = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sprucewood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_sprucewood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:spruce_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:spruce_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_spruce" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_spruce" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:spruce_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_spruce%1"' },
{ '"mcl_core:leaves"', '"mcl_core:spruceleaves"' }, -- addition to MCLA
},
birch = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_birchwood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:birch_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:birch_door%1"' },
{ "mcl_trees:tree_oak", "mcl_core:stripped_birch" }, -- divert from MCLA, use stripped birch, what is the name in MCLA?
{ "mcl_trees:wood_oak", "mcl_trees:wood_birch" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_birch%1"' },
{ '"mcl_core:leaves"', '"mcl_core:birchleaves"' }, -- addition to MCLA
},
acacia = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_acaciawood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_acaciawood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:acacia_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:acacia_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_acacia" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_acacia" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:acacia_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_acacia%1"' },
{ '"mcl_core:leaves"', '"mcl_core:acacialeaves"' }, -- addition to MCLA
},
dark_oak = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_darkwood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_darkwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:dark_oak_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:dark_oak_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_dark_oak" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_dark_oak" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:dark_oak_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_dark_oak%1"' },
{ '"mcl_core:leaves"', '"mcl_core:darkleaves"' }, -- addition to MCLA
},
jungle = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_junglewood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_junglewood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:jungle_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:jungle_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_jungle" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_jungle" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:jungle_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_jungle%1"' },
{ '"mcl_core:leaves"', '"mcl_core:jungleleaves"' }, -- addition to MCLA
},
bamboo = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_bamboo_plank%1"' }, -- divert from MCLA
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_bamboo_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:trapdoor_bamboo%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:door_bamboo%1"' },
{ "mcl_core:cobble", "mcl_core:andesite" },
{ '"mcl_stairs:stair_cobble([^"]*)"', '"mcl_stairs:stair_andesite%1"' },
{ '"mcl_walls:cobble([^"]*)"', '"mcl_walls:andesite%1"' },
{ '"mcl_stairs:slab_cobble([^"]*)"', '"mcl_stairs:slab_andesite%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_bamboo" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_bamboo" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:bamboo_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_bamboo%1"' },
{ '"mcl_core:leaves"', '"air"' }, -- addition to MCLA
},
cherry = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_cherry_blossom%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_cherry_blossom_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:trapdoor_cherry_blossom%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:door_cherry_blossom%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_cherry_blossom" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_cherry_blossom" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:cherry_blossom_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_cherry_blossom%1"' },
{ '"mcl_core:leaves"', '"mcl_core:leaves"' }, -- addition to MCLA
},
}

View File

@ -1,90 +0,0 @@
local function mcl_log (message)
mcl_util.mcl_log (message, "[Village - Foundation]")
end
local foundation_materials = {}
foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
--"mcl_core:sandstonecarved"
-------------------------------------------------------------------------------
-- function to fill empty space below baseplate when building on a hill
-------------------------------------------------------------------------------
function settlements.ground(pos, pr, platform_material) -- role model: Wendelsteinkircherl, Brannenburg
local p2 = vector.new(pos)
local cnt = 0
local mat = "mcl_core:dirt"
if not platform_material then
mat = "mcl_core:dirt"
else
mat = platform_material
end
p2.y = p2.y-1
while true do
cnt = cnt+1
if cnt > 20 then break end
if cnt>pr:next(2,4) then
if not platform_material then
mat = "mcl_core:stone"
end
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
local surface_mat = settlement_info[i]["surface_mat"]
mcl_log("Surface material: " .. tostring(surface_mat))
local platform_mat = foundation_materials[surface_mat]
mcl_log("Foundation material: " .. tostring(platform_mat))
--
-- 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}
-- Pass in biome info and make foundations of same material (seed: apple for desert)
settlements.ground(p, pr, platform_mat)
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,79 +1,29 @@
settlements = {}
settlements.modpath = minetest.get_modpath(minetest.get_current_modname())
mcl_villages = {}
mcl_villages.modpath = minetest.get_modpath(minetest.get_current_modname())
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.grundstellungen()
local village_boost = tonumber(minetest.settings:get("vl_villages_boost")) or 1
dofile(mcl_villages.modpath.."/const.lua")
dofile(mcl_villages.modpath.."/utils.lua")
dofile(mcl_villages.modpath.."/buildings.lua")
dofile(mcl_villages.modpath.."/paths.lua")
dofile(mcl_villages.modpath.."/api.lua")
local S = minetest.get_translator(minetest.get_current_modname())
local villagegen={}
--
-- register block for npc spawn
--
minetest.register_node("mcl_villages:stonebrickcarved", {
description = S("Chiseled Stone Village Bricks"),
_doc_items_longdesc = doc.sub.items.temp.build,
tiles = {"mcl_core_stonebrick_carved.png"},
drop = "mcl_core:stonebrickcarved",
groups = {pickaxey=1, stone=1, stonebrick=1, building_block=1, material_stone=1},
sounds = mcl_sounds.node_sound_stone_defaults(),
is_ground_content = false,
_mcl_blast_resistance = 6,
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1},})
--[[ Enable for testing, but use MineClone2's own spawn code if/when merging.
--
-- register inhabitants
--
if minetest.get_modpath("mobs_mc") then
mcl_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)
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)
if mcl_villages.village_exists(param.blockseed) then return end
local pr = PcgRandom(param.blockseed)
local settlement = mcl_villages.create_site_plan(param.minp, param.maxp, pr)
if not settlement then return false, false end
-- all foundations first, then all buildings, to avoid damaging very close buildings
mcl_villages.terraform(settlement, pr)
mcl_villages.place_schematics(param.minp, param.maxp, settlement, param.blockseed, pr)
mcl_villages.add_village(param.blockseed, settlement)
for _, on_village_placed_callback in pairs(mcl_villages.on_village_placed) do
on_village_placed_callback(settlement, param.blockseed)
end
end
-- Disable natural generation in singlenode.
@ -81,61 +31,430 @@ 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)
if maxp.y < 0 then return end
-- randomly try to build settlements
if blockseed % 77 ~= 17 then return end
--minetest.log("Rng good. Generate attempt")
-- needed for manual and automated settlement building
-- don't build settlements on (too) uneven terrain
local n=minetest.get_node_or_nil(minp)
if n and n.name == "mcl_villages:structblock" then return end
--minetest.log("No existing village attempt here")
if villagegen[minetest.pos_to_string(minp)] ~= nil then return end
--minetest.log("Not in village gen. Put down placeholder: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
minetest.set_node(minp,{name="mcl_villages:structblock"})
local height_difference = settlements.evaluate_heightmap()
if not height_difference or height_difference > max_height_difference then
minetest.log("action", "Do not spawn village here as heightmap not good")
return
if village_boost == 0 then return end
local pr = PcgRandom(blockseed)
if pr:next(0,1e9) * 100e-9 >= village_boost then return end
if village_boost < 25 then -- otherwise, this tends to transitively emerge too much
minp, maxp = vector.offset(minp, -16, 0, -16), vector.offset(maxp, 16, 0, 16)
end
--minetest.log("Build me a village: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
villagegen[minetest.pos_to_string(minp)]={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed })
end)
end
-- Handle legacy structblocks that are not fully emerged yet.
minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1}})
minetest.register_lbm({
name = "mcl_villages:structblock",
run_at_every_load = true,
nodenames = {"mcl_villages:structblock"},
action = function(pos, node)
minetest.set_node(pos, {name = "air"})
if not villagegen[minetest.pos_to_string(pos)] then return end
local minp=villagegen[minetest.pos_to_string(pos)].minp
local maxp=villagegen[minetest.pos_to_string(pos)].maxp
minetest.emerge_area(minp, maxp, ecb_village, villagegen[minetest.pos_to_string(minp)])
villagegen[minetest.pos_to_string(minp)]=nil
local minp, maxp = vector.offset(pos, -40, -40, -40), vector.offset(pos, 40, 40, 40)
local blockseed = PcgRandom(minetest.hash_node_position(pos)):next()
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed})
end
})
-- manually place villages
if minetest.is_creative_enabled("") then
minetest.register_craftitem("mcl_villages:tool", {
description = S("mcl_villages build tool"),
inventory_image = "default_tool_woodshovel.png",
-- build ssettlement
-- build settlement
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.under then return end
if not minetest.check_player_privs(placer, "server") then
minetest.chat_send_player(placer:get_player_name(), S("Placement denied. You need the “server” privilege to place villages."))
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 pos = pointed_thing.under
local minp, maxp = vector.offset(pos, -40, -40, -40), vector.offset(pos, 40, 40, 40)
local blockseed = PcgRandom(minetest.hash_node_position(pos)):next()
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed })
end
})
mcl_wip.register_experimental_item("mcl_villages:tool")
end
-- This makes the temporary node invisble unless in creative mode
local drawtype = minetest.is_creative_enabled("") and "glasslike" or "airlike"
-- Special node for schematics editing: no path on this place
minetest.register_node("mcl_villages:no_paths", {
description = S("Prevent paths from being placed during villager generation. Replaced by air after village path generation"),
paramtype = "light",
drawtype = drawtype,
inventory_image = "mcl_core_barrier.png",
wield_image = "mcl_core_barrier.png",
tiles = { "mcl_core_barrier.png" },
is_ground_content = false,
groups = { creative_breakable = 1, not_solid = 1, not_in_creative_inventory = 1 },
})
-- Special node for schematics editing: path endpoint
minetest.register_node("mcl_villages:path_endpoint", {
description = S("Mark the node as a good place for paths to connect to"),
is_ground_content = false,
tiles = { "wool_white.png" },
wield_image = "wool_white.png",
wield_scale = { x = 1, y = 1, z = 0.5 },
groups = { handy = 1, supported_node = 1, not_in_creative_inventory = 1 },
sounds = mcl_sounds.node_sound_wool_defaults(),
paramtype = "light",
sunlight_propagates = true,
drawtype = "nodebox",
node_box = { type = "fixed", fixed = { { -0.5, -0.5, -0.5, 0.5, -0.45, 0.5 } } },
_mcl_hardness = 0.1,
_mcl_blast_resistance = 0.1,
})
local schem_path = mcl_villages.modpath .. "/schematics/"
mcl_villages.register_bell({ name = "belltower", mts = schem_path .. "new_villages/belltower.mts", yadjust = 1 })
mcl_villages.register_well({
name = "well",
mts = schem_path .. "new_villages/well.mts",
yadjust = -1,
rotation_offset = 3, -- lamp is east
})
for i = 1, 6 do
mcl_villages.register_lamp({
name = "lamp",
mts = schem_path .. "new_villages/lamp_" .. i .. ".mts",
yadjust = 1,
no_ground_turnip = true,
no_clearance = true,
})
end
mcl_villages.register_building({
name = "house_big",
mts = schem_path .. "new_villages/house_4_bed.mts",
min_jobs = 6,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "house_large",
mts = schem_path .. "new_villages/house_3_bed.mts",
min_jobs = 4,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "house_medium",
mts = schem_path .. "new_villages/house_2_bed.mts",
min_jobs = 2,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "house_chimney",
mts = schem_path .. "haeuschen2.mts",
min_jobs = 2,
max_jobs = 99,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "house_small",
mts = schem_path .. "new_villages/house_1_bed.mts",
min_jobs = 1,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "blacksmith",
mts = schem_path .. "new_villages/blacksmith.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "butcher",
mts = schem_path .. "new_villages/butcher.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 0, -- entrance is north
})
mcl_villages.register_building({
name = "farm",
mts = schem_path .. "new_villages/farm.mts",
num_others = 3,
yadjust = 1,
rotation_offset = 3, -- composters are east
})
mcl_villages.register_building({
name = "fish_farm",
mts = schem_path .. "new_villages/fishery.mts",
num_others = 8,
yadjust = -2,
rotation_offset = 2, -- entrances are east and west
})
mcl_villages.register_building({
name = "fletcher_tiny",
group = "g:fletcher",
mts = schem_path .. "bogner.mts",
num_others = 8,
max_jobs = 6,
yadjust = 0,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "fletcher",
group = "g:fletcher",
mts = schem_path .. "new_villages/fletcher.mts",
num_others = 8,
min_jobs = 7,
yadjust = 1,
rotation_offset = 3, -- entrance is east
})
mcl_villages.register_building({
name = "library",
group = "g:library",
mts = schem_path .. "new_villages/library.mts",
min_jobs = 12,
max_jobs = 99,
num_others = 15,
yadjust = 1,
})
mcl_villages.register_building({
name = "librarian",
group = "g:library",
mts = schem_path .. "schreiber.mts",
min_jobs = 1,
max_jobs = 11,
yadjust = 0,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "map_shop",
mts = schem_path .. "new_villages/cartographer.mts",
num_others = 15,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "mason",
mts = schem_path .. "new_villages/mason.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "mill",
mts = schem_path .. "new_villages/mill.mts",
num_others = 8,
yadjust = 1,
})
mcl_villages.register_building({
name = "tannery",
mts = schem_path .. "new_villages/leather_worker.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "tool_smith",
mts = schem_path .. "new_villages/toolsmith.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "weapon_smith",
mts = schem_path .. "new_villages/weaponsmith.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 0, -- entrance is north
})
mcl_villages.register_building({
name = "chapel",
group = "g:church",
mts = schem_path .. "new_villages/chapel.mts",
num_others = 8,
min_jobs = 1,
max_jobs = 9,
yadjust = 1,
})
mcl_villages.register_building({
name = "church_european",
group = "g:church",
mts = schem_path .. "kirche.mts",
num_others = 20,
min_jobs = 8,
max_jobs = 99,
yadjust = 0,
rotation_offset = 2, -- entrance is west, but tower is south
})
mcl_villages.register_building({
name = "church",
group = "g:church",
mts = schem_path .. "new_villages/church.mts",
num_others = 20,
min_jobs = 8,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "farm_small",
mts = schem_path .. "new_villages/farm_small_1.mts",
num_others = 3,
yadjust = 1,
rotation_offset = 3, -- composters are south west?
})
mcl_villages.register_building({
name = "farm_small2",
mts = schem_path .. "new_villages/farm_small_2.mts",
num_others = 3,
yadjust = 1,
rotation_offset = 3, -- composters are south west?
})
mcl_villages.register_building({
name = "farm_large",
mts = schem_path .. "new_villages/farm_large_1.mts",
num_others = 6,
yadjust = 1,
rotation_offset = 3, -- composters are east
})
mcl_villages.register_crop({
type = "grain",
node = "mcl_farming:wheat_1",
biomes = {
acacia = 10,
bamboo = 10,
desert = 10,
jungle = 10,
plains = 10,
savanna = 10,
spruce = 10,
},
})
mcl_villages.register_crop({
type = "root",
node = "mcl_farming:carrot_1",
biomes = {
acacia = 10,
bamboo = 6,
desert = 10,
jungle = 6,
plains = 6,
spruce = 10,
},
})
mcl_villages.register_crop({
type = "root",
node = "mcl_farming:potato_1",
biomes = {
acacia = 6,
bamboo = 10,
desert = 6,
jungle = 10,
plains = 10,
spruce = 6,
},
})
mcl_villages.register_crop({
type = "root",
node = "mcl_farming:beetroot_0",
biomes = {
acacia = 3,
bamboo = 3,
desert = 3,
jungle = 3,
plains = 3,
spruce = 3,
},
})
mcl_villages.register_crop({
type = "gourd",
node = "mcl_farming:melontige_1",
biomes = {
bamboo = 10,
jungle = 10,
},
})
mcl_villages.register_crop({
type = "gourd",
node = "mcl_farming:pumpkin_1",
biomes = {
acacia = 10,
bamboo = 5,
desert = 10,
jungle = 5,
plains = 10,
spruce = 10,
},
})
-- TODO: make flowers biome-specific
for name, def in pairs(minetest.registered_nodes) do
if def.groups.flower and not def.groups.double_plant and name ~= "mcl_flowers:wither_rose" then
mcl_villages.register_crop({
type = "flower",
node = name,
biomes = {
acacia = 10,
bamboo = 6,
desert = 10,
jungle = 6,
plains = 6,
spruce = 10,
},
})
end
end
-- Crop placeholder nodes at different growth stages, for designing schematics
for _, crop_type in ipairs(mcl_villages.get_crop_types()) do
for count = 1, 8 do
local tile = crop_type .. "_" .. count .. ".png"
minetest.register_node("mcl_villages:crop_" .. crop_type .. "_" .. count, {
description = S("A place to plant @1 crops", crop_type),
is_ground_content = false,
tiles = { tile },
wield_image = tile,
wield_scale = { x = 1, y = 1, z = 0.5 },
groups = { handy = 1, supported_node = 1, not_in_creative_inventory = 1 },
paramtype = "light",
sunlight_propagates = true,
drawtype = "nodebox",
node_box = { type = "fixed", fixed = { { -0.5, -0.5, -0.5, 0.5, -0.45, 0.5 } } },
_mcl_hardness = 0.1,
_mcl_blast_resistance = 0.1,
})
end
end

View File

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

View File

@ -1,91 +1,374 @@
-------------------------------------------------------------------------------
-- 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
local light_threshold = tonumber(minetest.settings:get("mcl_villages_light_threshold")) or 5
end_point = settlement_info[o]["pos"]
if starting_point ~= end_point
then
-- loop until end_point is reched (distance == 0)
while true do
local get_node = core.get_node
local swap_node = core.swap_node
-- 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
-- This ends up being a nested table.
-- 1st level is the blockseed which is the village
-- 2nd is the distance of the building from the bell
-- 3rd is the pos of the end points
local path_ends = {}
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
-- note: not using LVM here, as this runs after the pathfinder
-- simple function to increase "no_paths" walls
function mcl_villages.clean_no_paths(minp, maxp)
local no_paths_nodes = minetest.find_nodes_in_area(minp, maxp, { "mcl_villages:no_paths" })
if #no_paths_nodes > 0 then
minetest.bulk_swap_node(no_paths_nodes, { name = "air" })
end
end
-- this can still run in LVM
-- simple function to increase "no_paths" walls
function mcl_villages.increase_no_paths(minp, maxp)
local p = vector.zero()
for z = minp.z, maxp.z do
p.z = z
for x = minp.x, maxp.x do
p.x = x
for y = minp.y, maxp.y - 1 do
p.y = y
local n = get_node(p)
if n and n.name == "mcl_villages:no_paths" then
p.y = y + 1
n = get_node(p)
if n and n.name == "air" then
swap_node(p, {name = "mcl_villages:no_paths" })
end
end
end
end
end
end
-- Insert end points in to the nested tables
function mcl_villages.store_path_ends(minp, maxp, pos, blockseed, bell_pos)
-- We store by distance because we create paths far away from the bell first
local dist = vector.distance(bell_pos, pos)
local id = "block_" .. blockseed -- cannot use integers as keys
-- TODO: benchmark best way
local tab = {}
local v = vector.zero()
for zi = minp.z, maxp.z do
v.z = zi
for yi = minp.y, maxp.y do
v.y = yi
for xi = minp.x, maxp.x do
v.x = xi
local n = get_node(v)
if n and n.name == "mcl_villages:path_endpoint" then
table.insert(tab, vector.copy(v))
swap_node(v, { name = "air" })
end
end
end
end
if not path_ends[id] then path_ends[id] = {} end
table.insert(path_ends[id], {dist, minetest.pos_to_string(pos), tab})
end
local function place_lamp(pos, pr)
local lamp_index = pr:next(1, #mcl_villages.schematic_lamps)
local schema = mcl_villages.schematic_lamps[lamp_index]
local schem_lua = mcl_villages.substitute_materials(pos, schema.schem_lua, pr)
local schematic = loadstring(schem_lua)()
minetest.place_schematic(vector.offset(pos, 0, schema.yadjust or 0, 0), schematic, "0",
{["air"] = "ignore"}, -- avoid destroying stairs etc.
true,
{ place_center_x = true, place_center_y = false, place_center_z = true }
)
end
local function smooth_path(path, passes, minp, maxp)
-- bridge over water/laver
for i = 2, #path - 1 do
while true do
local cur = path[i]
local node = get_node(cur).name
if node == "air" and vector.in_area(cur, minp, maxp) then
local under = get_node(vector.offset(path[i], 0, -1, 0)).name
local udef = minetest.registered_nodes[under]
-- do not build paths over leaves
if udef and (udef.groups.leaves or 0) > 0 then
swap_node(path[i], {name="mcl_villages:no_paths"})
return -- bad path
end
break
else
local ndef = minetest.registered_nodes[node]
if not ndef then break end -- ignore
if (ndef.groups.water or 0) > 0 or (ndef.groups.lava or 0) > 0 then
cur.y = cur.y + 1
else
break
end
end
end
end
-- Smooth out bumps in path to reduce weird stairs
local any_changed = false
for pass = 1, passes do
local changed = false
for i = 2, #path - 1 do
local prev_y = path[i - 1].y
local y = path[i].y
local next_y = path[i + 1].y
local bump = get_node(path[i]).name
local bdef = minetest.registered_nodes[bump]
-- TODO: also replace bamboo underneath with dirt here?
if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then
-- ignore in this pass
elseif (y > next_y + 1 and y <= prev_y) -- large step
or (y > prev_y + 1 and y <= next_y) -- large step
or (y > prev_y and y > next_y) then
-- Remove peaks to flatten path
path[i].y = math.max(prev_y, next_y)
swap_node(path[i], { name = "air" })
changed = true
elseif (y < next_y - 1 and y >= prev_y) -- large step
or (y < prev_y - 1 and y >= next_y) -- large step
or (y < prev_y and y < next_y) then
-- Fill in dips to flatten path
path[i].y = math.min(prev_y, next_y) - 1 -- to replace below first
swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs?
path[i].y = path[i].y + 1 -- above dirt
changed = true
end
end
if changed then any_changed = true else break end
end
-- by delaying this, we allow making bridges over deep dips:
--[[
if any_changed then
-- we may not yet have filled a gap
for i = 2, #path - 1 do
local below = vector.offset(path[y], 0, -1, 0)
local bdef = minetest.registered_nodes[get_node(path[i]).name]
if bdef and not bdef.walkable then
swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs?
end
end
end]]
return path
end
local function place_path(path, pr, stair, slab)
-- find water/lava below
for i = 2, #path - 1 do
local prev_y = path[i - 1].y
local y = path[i].y
local next_y = path[i + 1].y
local bump = get_node(path[i]).name
local bdef = minetest.registered_nodes[bump]
if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then
-- Find air
local up_pos = vector.copy(path[i])
while true do
up_pos.y = up_pos.y + 1
local up_node = get_node(up_pos).name
local udef = minetest.registered_nodes[up_node]
if udef and (udef.groups.water or 0) == 0 and (udef.groups.lava or 0) == 0 then
swap_node(up_pos, { name = "air" })
path[i] = up_pos
break
elseif not udef then break end -- ignore node encountered
end
end
end
for i, pos in ipairs(path) do
local n0 = get_node(pos).name
if n0 ~= "air" then swap_node(pos, { name = "air" }) end
local under_pos = vector.offset(pos, 0, -1, 0)
local n = get_node(under_pos).name
local ndef = minetest.registered_nodes[n]
local groups = ndef and ndef.groups or {}
local done = false
if i > 1 and pos.y > path[i - 1].y then
-- stairs up
if (groups.stair or 0) == 0 then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1]))
swap_node(under_pos, { name = stair, param2 = param2 })
end
elseif i < #path-1 and pos.y > path[i + 1].y then
-- stairs down
if (groups.stair or 0) == 0 then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1]))
swap_node(under_pos, { name = stair, param2 = param2 })
end
elseif (groups.stair or 0) == 0 and i > 1 and pos.y < path[i - 1].y then
-- stairs down
local n2 = get_node(vector.offset(path[i - 1], 0, -1, 0)).name
if not minetest.get_item_group(n2, "stair") then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(path[i - 1], pos))
if i < #path - 1 then -- uglier, but easier to walk up?
param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1]))
end
minetest.add_node(pos, { name = stair, param2 = param2 })
pos.y = pos.y + 1
end
elseif (groups.stair or 0) == 0 and i < #path-1 and pos.y < path[i + 1].y then
-- stairs up
local n2 = get_node(vector.offset(path[i + 1], 0, -1, 0)).name
if not minetest.get_item_group(n2, "stair") then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(path[i + 1], pos))
if i > 1 then -- uglier, but easier to walk up?
param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1]))
end
swap_node(pos, { name = stair, param2 = param2 })
pos.y = pos.y + 1
end
end
-- flat
if not done then
if (groups.water or 0) > 0 then
swap_node(under_pos, { name = slab })
elseif (groups.lava or 0) > 0 then
swap_node(under_pos, { name = "mcl_stairs:slab_stone" })
elseif (groups.sand or 0) > 0 then
swap_node(under_pos, { name = "mcl_core:sandstonesmooth2" })
elseif (groups.soil or 0) > 0 and (groups.dirtifies_below_solid or 0) == 0 then
swap_node(under_pos, { name = "mcl_core:grass_path" })
end
end
-- Clear space for villagers to walk
for j = 1, 2 do
local over_pos = vector.offset(pos, 0, j, 0)
if get_node(over_pos).name ~= "air" then
swap_node(over_pos, { name = "air" })
end
end
end
-- Do lamps afterwards so we don't put them where a path will be laid
for _, pos in ipairs(path) do
if minetest.get_node_light(pos, 0) < light_threshold then
local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos, -1, -1, -1), vector.offset(pos, 1, 1, 1),
{ "group:material_sand", "group:material_stone", "group:grass_block", "group:wood_slab" }
)
-- todo: shuffle nn?
for _, npos in ipairs(nn) do
local node = get_node(npos).name
if node ~= "mcl_core:grass_path" and minetest.get_item_group(node, "stair") == 0 then
if minetest.get_item_group(node, "wood_slab") ~= 0 then
minetest.add_node(vector.offset(npos, 0, 1, 0), { name = "mcl_torches:torch", param2 = 1 })
else
place_lamp(npos, pr)
end
break
end
end
end
end
end
-- FIXME: ugly
function get_biome_stair_slab(biome_name)
-- Use the same stair and slab throughout the entire village
-- The quotes are necessary to be matched as JSON strings
local stair, slab = '"mcl_stairs:stair_oak"', '"mcl_stairs:slab_oak_top"'
-- Change stair and slab for biome
if mcl_villages.biome_map[biome_name] and mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]] then
for _, sub in pairs(mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]]) do
stair = stair:gsub(sub[1], sub[2])
slab = slab:gsub(sub[1], sub[2])
end
end
-- translate MCLA values to VL
for _, sub in pairs(mcl_villages.mcla_to_vl) do
stair = stair:gsub(sub[1], sub[2])
slab = slab:gsub(sub[1], sub[2])
end
-- The quotes are to match what is in JSON schemas, but we don't want them now
return stair:gsub('"', ""), slab:gsub('"', "")
end
-- Work out which end points should be connected
-- works from the outside of the village in
function mcl_villages.paths(blockseed, biome_name, minp, maxp)
local pr = PcgRandom(blockseed)
local pathends = path_ends["block_" .. blockseed]
if pathends == nil then
minetest.log("warning", string.format("[mcl_villages] Tried to set paths for block seed that doesn't exist %d", blockseed))
return
end
-- Stair and slab style of the village
local stair, slab = get_biome_stair_slab(biome_name)
table.sort(pathends, function(a, b) return a[1] > b[1] end)
--minetest.log("action", "path ends: "..dump(pathends,""))
-- find ways to connect
local connected, to_place = {}, {}
for _, tmp in ipairs(pathends) do
local from, from_eps = tmp[2], tmp[3]
-- ep == end_point
for _, from_ep_pos in ipairs(from_eps) do
-- TODO: add back some logic as before that ensures some longer paths, too?
local cand = {}
for _, tmp in ipairs(pathends) do
local to, to_eps = tmp[2], tmp[3]
if from ~= to and not connected[from .. "-" .. to] and not connected[to .. "-" .. from] then
for _, to_ep_pos in ipairs(to_eps) do
local dist = vector.distance(from_ep_pos, to_ep_pos)
table.insert(cand, {dist, from, from_ep_pos, to, to_ep_pos})
end
end
end
table.sort(cand, function(a,b) return a[1] < b[1] end)
--minetest.log("action", "candidates: "..dump(cand,""))
for _, pair in ipairs(cand) do
local dist, from, from_ep_pos, to, to_ep_pos = unpack(pair)
local path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 4, 4)
if path then smooth_path(path, 3, minp, maxp) end
path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 2, 2)
if path then smooth_path(path, 1, minp, maxp) end
path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1)
if path then
--minetest.log("path "..from.." to "..to.." len "..tostring(#path))
path = smooth_path(path, 1, minp, maxp)
if path then
connected[from .. "-" .. to] = 1
table.insert(to_place, pair)
goto continue -- add only one path per building
end
end
end
end
::continue::
end
--minetest.log("action", "to_place: "..dump(to_place,""))
-- now lay the actual paths
for _, cand in ipairs(to_place) do
local dist, from, from_ep_pos, to, to_ep_pos = unpack(cand)
local path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1)
if path then
path = place_path(path, pr, stair, slab)
else
minetest.log("warning",
string.format(
"[mcl_villages] No good path from %s to %s, distance %d",
minetest.pos_to_string(from_ep_pos),
minetest.pos_to_string(to_ep_pos),
dist
)
)
end
end
path_ends["block_" .. blockseed] = nil
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More