This commit is contained in:
HimbeerserverDE 2021-01-27 20:13:16 +01:00
commit a3ccb54376
38 changed files with 1741 additions and 100 deletions

@ -85,3 +85,12 @@ Report all bugs and missing Minecraft features here:
We have an IRC channel! Join us on #mineclone2 in freenode.net.
<ircs://irc.freenode.net:6697/#mineclone2>
## Creating releases
* Launch MineClone2 to make sure it still runs
* Update the version number in README.md
* Use `git tag <version number>` to tag the latest commit with the version number
* Push to repo (don't forget `--tags`!)
* Update ContentDB (https://content.minetest.net/packages/Wuzzy/mineclone2/)
* Update first post in forum thread (https://forum.minetest.net/viewtopic.php?f=50&t=16407)
* Post release announcement and changelog in forums

@ -20,7 +20,6 @@ For these features, no easy Lua workaround could be found.
## Interface
- Inventory: Hold down right mouse button while holding an item stack to drop items into the slots as you move the mouse. Makes crafting MUCH faster
- Sneak+Leftclick on crafting output crafts as many items as possible and immediately puts it into the player inventory ([issue 5211](https://github.com/minetest/minetest/issues/5211))
- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Required for large chests
- Sneak+click puts items in different inventories depending on the item type (maybe group-based)? Required for sneak-clicking to armor slots
## Workaround theoretically possible
@ -35,6 +34,7 @@ For these features, a workaround (or hack ;-)) by using Lua is theoretically pos
- Set frequency in which players lose breath. 2 seconds are hardcoded in Minetest, in Minecraft it's 1 second
- Set damage frequency of `damage_per_second`. In Minecraft many things damage players every half-second rather than every second
- Possible to damage players directly when they are with the head inside. This allows to add Minecraft-like suffocation
- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Useful for large chests
#### Nice-to-haye
- Utility function to rotate pillar-like nodes, requiring only 3 possible orientations (X, Y, Z). Basically this is `minetest.rotate_node` but with less orientations; the purpur pillar would mess up if a mirrored rotation would be possible. This is already implemented in MCL2, See `mcl_util` for more infos

@ -32,6 +32,10 @@ local STEP_LENGTH = 0.3
-- How many rays to compute entity exposure to explosion
local N_EXPOSURE_RAYS = 16
-- Nodes having a blast resistance of this value or higher are treated as
-- indestructible
local INDESTRUCT_BLASTRES = 1000000
minetest.register_on_mods_loaded(function()
-- Store blast resistance values by content ids to improve performance.
for name, def in pairs(minetest.registered_nodes) do
@ -135,14 +139,21 @@ end
-- strength - The strength of each ray
-- raydirs - The directions for each ray
-- radius - The maximum distance each ray will go
-- drop_chance - The chance that destroyed nodes will drop their items
-- fire - If true, 1/3 of destroyed nodes become fire
-- info - Table containing information about explosion
-- puncher - object that punches other objects (optional)
--
-- Values in info:
-- drop_chance - The chance that destroyed nodes will drop their items
-- fire - If true, 1/3 nodes become fire
-- griefing - If true, the explosion will destroy nodes (default: true)
-- max_blast_resistance - The explosion will treat all non-indestructible nodes
-- as having a blast resistance of no more than this
-- value
--
-- Note that this function has been optimized, it contains code which has been
-- inlined to avoid function calls and unnecessary table creation. This was
-- measured to give a significant performance increase.
local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, puncher, creative_enabled)
local function trace_explode(pos, strength, raydirs, radius, info, puncher)
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(vector.subtract(pos, radius),
@ -164,7 +175,12 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
local data = vm:get_data()
local destroy = {}
local drop_chance = info.drop_chance
local fire = info.fire
local max_blast_resistance = info.max_blast_resistance
-- Trace rays for environment destruction
if info.griefing then
for i = 1, #raydirs do
local rpos_x = pos.x
local rpos_y = pos.y
@ -183,6 +199,10 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
local cid = data[idx]
local br = node_blastres[cid]
if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
br = max_blast_resistance
end
local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z})
rpos_x = rpos_x + STEP_LENGTH * rdir_x
@ -200,6 +220,7 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
end
end
end
end
-- Entities in radius of explosion
local punch_radius = 2 * strength
@ -327,7 +348,7 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
-- Remove destroyed blocks and drop items
for hash, idx in pairs(destroy) do
local do_drop = not creative_enabled and math.random() <= drop_chance
local do_drop = math.random() <= drop_chance
local on_blast = node_on_blast[data[idx]]
local remove = true
@ -377,7 +398,6 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
-- Log explosion
minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) ..
' with strength ' .. strength .. ' and radius ' .. radius)
end
-- Create an explosion with strength at pos.
@ -385,16 +405,24 @@ end
-- Parameters:
-- pos - The position where the explosion originates from
-- strength - The blast strength of the explosion (a TNT explosion uses 4)
-- info - Table containing information about explosion.
-- info - Table containing information about explosion
-- puncher - object that is reported as source of punches/damage (optional)
--
-- Values in info:
-- drop_chance - If specified becomes the drop chance of all nodes in the
-- explosion (defaults to 1.0 / strength)
-- no_sound - If true then the explosion will not play a sound
-- no_particle - If true then the explosion will not create particles
-- explosion (default: 1.0 / strength)
-- max_blast_resistance - If specified the explosion will treat all
-- non-indestructible nodes as having a blast resistance
-- of no more than this value
-- sound - If true, the explosion will play a sound (default: true)
-- particles - If true, the explosion will create particles (default: true)
-- fire - If true, 1/3 nodes become fire (default: false)
-- griefing - If true, the explosion will destroy nodes (default: true)
function mcl_explosions.explode(pos, strength, info, puncher)
if info == nil then
info = {}
end
-- The maximum blast radius (in the air)
local radius = math.ceil(1.3 * strength / (0.3 * 0.75) * 0.3)
@ -403,13 +431,31 @@ function mcl_explosions.explode(pos, strength, info, puncher)
end
local shape = sphere_shapes[radius]
local creative_enabled = minetest.is_creative_enabled("")
trace_explode(pos, strength, shape, radius, (info and info.drop_chance) or 1 / strength, info.fire == true, puncher, creative_enabled)
-- Default values
if info.drop_chance == nil then info.drop_chance = 1 / strength end
if info.particles == nil then info.particles = true end
if info.sound == nil then info.sound = true end
if info.fire == nil then info.fire = false end
if info.griefing == nil then info.griefing = true end
if info.max_blast_resistance == nil then
info.max_blast_resistance = INDESTRUCT_BLASTRES
end
if not (info and info.no_particle) then
-- For backwards compatibility
if info.no_particle then info.particles = false end
if info.no_sound then info.sound = false end
-- Dont do drops in creative mode
if minetest.is_creative_enabled("") then
info.drop_chance = 0
end
trace_explode(pos, strength, shape, radius, info, puncher)
if info.particles then
add_particles(pos, radius)
end
if not (info and info.no_sound) then
if info.sound then
minetest.sound_play("tnt_explode", {
pos = pos, gain = 1.0,
max_hear_distance = strength * 16

@ -25,6 +25,7 @@ mcl_vars.inventory_header = ""
local mg_name = minetest.get_mapgen_setting("mg_name")
local minecraft_height_limit = 256
local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true"
local singlenode = mg_name == "singlenode"
-- Calculate mapgen_edge_min/mapgen_edge_max
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
@ -45,7 +46,7 @@ local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes
if not superflat then
if not superflat and not singlenode then
-- Normal mode
--[[ Realm stacking (h is for height)
- Overworld (h>=256)
@ -66,6 +67,14 @@ if not superflat then
mcl_vars.mg_lava = true
mcl_vars.mg_bedrock_is_rough = true
elseif singlenode then
mcl_vars.mg_overworld_min = -66
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min
mcl_vars.mg_lava = false
mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min
mcl_vars.mg_bedrock_is_rough = false
else
-- Classic superflat
local ground = minetest.get_mapgen_setting("mgflat_ground_level")
@ -128,3 +137,4 @@ 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())

@ -395,4 +395,13 @@ function mcl_util.generate_on_place_plant_function(condition)
end
end
-- adjust the y level of an object to the center of its collisionbox
-- used to get the origin position of entity explosions
function mcl_util.get_object_center(obj)
local collisionbox = obj:get_properties().collisionbox
local pos = obj:get_pos()
local ymin = collisionbox[2]
local ymax = collisionbox[5]
pos.y = pos.y + (ymax - ymin) / 2.0
return pos
end

@ -789,18 +789,16 @@ local check_for_death = function(self, cause, cmi_cause)
local puncher = cmi_cause.puncher
if puncher then
wielditem = puncher:get_wielded_item()
if mod_experience and ((not self.child) or self.type ~= "animal") then
mcl_experience.throw_experience(self.object:get_pos(), math.random(self.xp_min, self.xp_max))
end
end
end
local cooked = mcl_burning.is_burning(self.object) or mcl_enchanting.has_enchantment(wielditem, "fire_aspect")
local looting = mcl_enchanting.get_enchantment(wielditem, "looting")
item_drop(self, cooked, looting)
end
local pos = self.object:get_pos()
if mod_experience and ((not self.child) or self.type ~= "animal") then
mcl_experience.throw_experience(pos, math.random(self.xp_min, self.xp_max))
end
end
-- execute custom death function
@ -2258,7 +2256,6 @@ local dogswitch = function(self, dtime)
return self.dogshoot_switch
end
-- execute current state (stand, walk, run, attacks)
-- returns true if mob has died
local do_states = function(self, dtime)
@ -2550,7 +2547,7 @@ local do_states = function(self, dtime)
if mod_explosions then
if mobs_griefing and not minetest.is_protected(pos, "") then
mcl_explosions.explode(self.object:get_pos(), self.explosion_strength, { drop_chance = 1.0 }, self.object)
mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object)
else
minetest.sound_play(self.sounds.explode, {
pos = pos,

@ -71,7 +71,7 @@ mobs:register_mob("mobs_mc:creeper", {
if self._forced_explosion_countdown_timer ~= nil then
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
if self._forced_explosion_countdown_timer <= 0 then
mobs:boom(self, self.object:get_pos(), self.explosion_strength)
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
self.object:remove()
end
end

@ -962,6 +962,7 @@ mobs:register_mob("mobs_mc:villager", {
walk_velocity = 1.2,
run_velocity = 2.4,
drops = {},
can_despawn = false,
-- TODO: sounds
animation = {
stand_speed = 25,

@ -26,7 +26,7 @@ S("An arrow fired from a bow has a regular damage of 1-9. At full charge, there'
S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."),
_doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."),
inventory_image = "mcl_bows_arrow_inv.png",
groups = { ammo=1, ammo_bow=1 },
groups = { ammo=1, ammo_bow=1, ammo_bow_regular=1 },
_on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir)
-- Shoot arrow
local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51))
@ -166,7 +166,7 @@ ARROW_ENTITY.on_step = function(self, dtime)
local objects = minetest.get_objects_inside_radius(pos, 1)
for _,obj in ipairs(objects) do
if obj:is_player() then
if not minetest.is_creative_enabled(obj:get_player_name()) then
if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then
if obj:get_inventory():room_for_item("main", "mcl_bows:arrow") then
obj:get_inventory():add_item("main", "mcl_bows:arrow")
minetest.sound_play("item_drop_pickup", {

@ -33,7 +33,7 @@ local bow_load = {}
-- Another player table, this one stores the wield index of the bow being charged
local bow_index = {}
mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack)
mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack, collectable)
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrow_item.."_entity")
if power == nil then
power = BOW_MAX_SPEED --19
@ -63,6 +63,7 @@ mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damag
le._is_critical = is_critical
le._startpos = pos
le._knockback = knockback
le._collectable = collectable
minetest.sound_play("mcl_bows_bow_shoot", {pos=pos, max_hear_distance=16}, true)
if shooter ~= nil and shooter:is_player() then
if obj:get_luaentity().player == "" then
@ -91,6 +92,7 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica
local arrow_stack, arrow_stack_id = get_arrow(player)
local arrow_itemstring
local has_infinity_enchantment = mcl_enchanting.has_enchantment(player:get_wielded_item(), "infinity")
local infinity_used = false
if minetest.is_creative_enabled(player:get_player_name()) then
if arrow_stack then
@ -103,7 +105,9 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica
return false
end
arrow_itemstring = arrow_stack:get_name()
if not has_infinity_enchantment then
if has_infinity_enchantment and minetest.get_item_group(arrow_itemstring, "ammo_bow_regular") > 0 then
infinity_used = true
else
arrow_stack:take_item()
end
local inv = player:get_inventory()
@ -116,7 +120,7 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica
local dir = player:get_look_dir()
local yaw = player:get_look_horizontal()
mcl_bows.shoot_arrow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item())
mcl_bows.shoot_arrow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item(), not infinity_used)
return true
end

@ -2,25 +2,14 @@ local S = minetest.get_translator("mcl_chests")
local mod_doc = minetest.get_modpath("doc")
-- Chest Entity
local entity_animations = {}
local animate_chests = (minetest.settings:get_bool("animated_chests") ~= false)
local entity_animation_speed = 25
do
local names = {"open", "opened", "close", "closed"}
local following = {["open"] = "opened", ["close"] = "closed"}
local durations = {10, 0, 10, 5}
local anim_start = 0
for index, name in ipairs(names) do
local duration = durations[index]
local anim_end = anim_start + duration
entity_animations[name] = {
bounds = {x = anim_start, y = anim_end},
sched_anim = following[name],
sched_time = duration / entity_animation_speed
local entity_animations = {
["open"] = {x = 0, y = 10},
["open_partly"] = {x = 0, y = 7},
["close"] = {x = 10, y = 20},
["close_partly"] = {x = 13, y = 20},
}
anim_start = anim_end
end
end
minetest.register_entity("mcl_chests:chest", {
initial_properties = {
@ -33,21 +22,19 @@ minetest.register_entity("mcl_chests:chest", {
set_animation = function(self, animname)
local anim = entity_animations[animname]
self.object:set_animation(anim.bounds, entity_animation_speed, 0, false)
if anim.sched_anim then
self.sched_anim = anim.sched_anim
self.sched_time = anim.sched_time
end
if not anim then return end
self.object:set_animation(anim, entity_animation_speed, 0, false)
end,
open = function(self, playername)
open = function(self, playername, partly)
self.players[playername] = true
if not self.is_open then
self.is_open = true
self:set_animation("open")
self:set_animation(partly and "open_partly" or "open")
minetest.sound_play(self.sound_prefix .. "_open", {
pos = self.node_pos,
})
self.is_open = true
self.opened_partly = partly
end
end,
@ -58,11 +45,12 @@ minetest.register_entity("mcl_chests:chest", {
for _ in pairs(playerlist) do
return
end
self.is_open = false
self:set_animation("close")
self:set_animation(self.opened_partly and "close_partly" or "close")
minetest.sound_play(self.sound_prefix .. "_close", {
pos = self.node_pos,
})
self.is_open = false
self.opened_partly = false
end
end,
@ -100,23 +88,12 @@ minetest.register_entity("mcl_chests:chest", {
on_activate = function(self)
self.object:set_armor_groups({immortal = 1})
self:set_animation("closed")
self.players = {}
end,
on_step = function(self, dtime)
local sched_anim, sched_time = self.sched_anim, self.sched_time
if not self:check() then
self.object:remove()
elseif sched_anim and sched_time then
sched_time = sched_time - dtime
if sched_time < 0 then
self:set_animation(sched_anim)
self.sched_time = nil
self.sched_anim = nil
else
self.sched_time = sched_time
end
end
end
})
@ -149,8 +126,8 @@ local function create_entity(pos, node_name, textures, param2, double, sound_pre
return luaentity
end
local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix)
local dir = minetest.facedir_to_dir(param2)
local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, dir)
local dir = dir or minetest.facedir_to_dir(param2)
local entity_pos = get_entity_pos(pos, dir, double)
return find_entity(entity_pos) or create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, dir, entity_pos)
end
@ -175,11 +152,22 @@ Value:
If player is using a chest: { pos = <chest node position> }
Otherwise: nil ]]
local open_chests = {}
local function back_is_blocked(pos, dir)
pos = vector.add(pos, dir)
local def = minetest.registered_nodes[minetest.get_node(pos).name]
pos.y = pos.y + 1
local def2 = minetest.registered_nodes[minetest.get_node(pos).name]
return not def or def.groups.opaque == 1 or not def2 or def2.groups.opaque == 1
end
-- To be called if a player opened a chest
local player_chest_open = function(player, pos, node_name, textures, param2, double, sound, mesh)
local name = player:get_player_name()
open_chests[name] = {pos = pos, node_name = node_name, textures = textures, param2 = param2, double = double, sound = sound, mesh = mesh}
find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh):open(name)
if animate_chests then
local dir = minetest.facedir_to_dir(param2)
find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, dir):open(name, back_is_blocked(pos, dir) or double and back_is_blocked(mcl_util.get_double_container_neighbor_pos(pos, param2, node_name:sub(-4)), dir))
end
end
-- Simple protection checking functions
@ -238,7 +226,9 @@ local player_chest_close = function(player)
if open_chest == nil then
return
end
if animate_chests then
find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, open_chest.double, open_chest.sound, open_chest.mesh):close(name)
end
chest_update_after_close(open_chest.pos)
open_chests[name] = nil

@ -147,6 +147,17 @@ minetest.register_craftitem("mcl_core:apple", {
local gapple_hunger_restore = minetest.item_eat(4)
local function eat_gapple(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local node = minetest.get_node(pointed_thing.under)
if placer and not placer:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
end
end
elseif pointed_thing.type == "object" then
return itemstack
end
local regen_duration, absorbtion_factor = 5, 1
if itemstack:get_name() == "mcl_core:apple_gold_enchanted" then
regen_duration, absorbtion_factor = 20, 4

@ -219,6 +219,16 @@ end
minetest.register_node("mcl_enchanting:table", {
description = S("Enchanting Table"),
_tt_help = S("Spend experience, and lapis to enchant various items."),
_doc_items_longdesc = S("Enchanting Tables will let you enchant armors, tools, weapons, and books with various abilities. But, at the cost of some experience, and lapis lazuli."),
_doc_items_usagehelp =
S("Rightclick the Enchanting Table to open the enchanting menu.").."\n"..
S("Place a tool, armor, weapon or book into the top left slot, and then place 1-3 Lapis Lazuli in the slot to the right.").."\n".."\n"..
S("After placing your items in the slots, the enchanting options will be shown. Hover over the options to read what is available to you.").."\n"..
S("These options are randomized, and dependent on experience level; but the enchantment strength can be increased.").."\n".."\n"..
S("To increase the enchantment strength, place bookshelves around the enchanting table. However, you will need to keep 1 air node between the table, & the bookshelves to empower the enchanting table.").."\n".."\n"..
S("After finally selecting your enchantment; left-click on the selection, and you will see both the lapis lazuli and your experience levels consumed. And, an enchanted item left in its place."),
_doc_items_hidden = false,
drawtype = "nodebox",
tiles = {"mcl_enchanting_table_top.png", "mcl_enchanting_table_bottom.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png"},
node_box = {

@ -448,3 +448,34 @@ minetest.register_node("mcl_flowers:waterlily", {
-- Legacy support
minetest.register_alias("mcl_core:tallgrass", "mcl_flowers:tallgrass")
-- mcimport support: re-adds missing double_plant tops in mcimported worlds.
local mg_name = minetest.get_mapgen_setting("mg_name")
local mod_mcimport = minetest.get_modpath("mcimport") ~= nil
local fix_doubleplants = minetest.settings:get_bool("fix_doubleplants", true)
if mod_mcimport and mg_name == "singlenode" and fix_doubleplants == true then
local flowernames = { "peony", "rose_bush", "lilac", "sunflower", "double_fern", "double_grass" }
for c=1, 6 do
local flowername = flowernames[c]
end
minetest.register_lbm({
label = "Add double plant tops.",
name = "mcl_flowers:double_plant_topper",
run_at_every_load = true,
nodenames = { "mcl_flowers:peony", "mcl_flowers:rose_bush", "mcl_flowers:lilac", "mcl_flowers:sunflower", "mcl_flowers:double_fern", "mcl_flowers:double_grass" },
action = function(pos, node)
for c=1, 6 do
local flowername = flowernames[c]
local bottom = pos
local top = { x = bottom.x, y = bottom.y + 1, z = bottom.z }
if node.name == "mcl_flowers:"..flowername then
minetest.set_node(top, {name = "mcl_flowers:"..flowername.."_top"})
end
end
end,
})
end

@ -8,6 +8,7 @@ if mcl_vars.mg_dungeons == false then
return
end
if mg_name ~= "singlenode" then
-- Get loot for dungeon chests
local get_loot = function()
local loottable = {
@ -396,3 +397,4 @@ minetest.register_on_generated(function(minp, maxp)
end
end)
end

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

@ -0,0 +1,357 @@
--[[
-------------------------------------------------------------------------------
-- 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 = minetest.get_node_or_nil(pos)
if not platform_material then
return
end
platform_material = platform_material.name
-- pick random material
local material = wallmaterial[math.random(1,#wallmaterial)]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return(schematic)"
-- replace material
if replace_wall == "y" then
schem_lua = schem_lua:gsub("mcl_core:cobble", material)
end
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass",
platform_material)
-- Disable special junglewood for now.
-- special material for spawning npcs
-- schem_lua = schem_lua:gsub("mcl_core:junglewood",
-- "settlements:junglewood")
--
-- format schematic string
local schematic = loadstring(schem_lua)()
-- build foundation for the building an make room above
local width = schematic["size"]["x"]
local depth = schematic["size"]["z"]
local height = schematic["size"]["y"]
local possible_rotations = {"0", "90", "180", "270"}
local rotation = possible_rotations[ math.random( #possible_rotations ) ]
settlements.foundation(
pos,
width,
depth,
height,
rotation)
vm:set_data(data)
-- place schematic
minetest.place_schematic_on_vmanip(
vm,
pos,
schematic,
rotation,
nil,
true)
vm:write_to_map(true)
end]]
-------------------------------------------------------------------------------
-- initialize settlement_info
-------------------------------------------------------------------------------
function settlements.initialize_settlement_info(pr)
local count_buildings = {}
-- count_buildings table reset
for k,v in pairs(schematic_table) do
-- local name = schematic_table[v]["name"]
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 with LVM
--------------------------------------------------------------------------------
function settlements.create_site_plan_lvm(maxp, minp, pr)
local settlement_info = {}
local building_all_info
local possible_rotations = {"0", "90", "180", "270"}
-- find center of chunk
local center = {
x=maxp.x-half_map_chunk_size,
y=maxp.y,
z=maxp.z-half_map_chunk_size
}
-- find center_surface of chunk
local center_surface, surface_material = settlements.find_surface_lvm(center, minp)
-- go build settlement around center
if not center_surface then return false end
-- add settlement to list
table.insert(settlements_in_world, center_surface)
-- save list to file
settlements.save()
-- initialize all settlement_info table
local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr)
-- first building is townhall in the center
building_all_info = schematic_table[1]
local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
-- add to settlement info table
local index = 1
settlement_info[index] = {
pos = center_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
-- increase index for following buildings
index = index + 1
-- now some buildings around in a circle, radius = size of town center
local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"]
-- draw j circles around center and increase radius by math.random(2,5)
for j = 1,20 do
if number_built < number_of_buildings then
-- 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 pos_surface, surface_material = settlements.find_surface_lvm(pos1, minp)
if not pos_surface then break end
local randomized_schematic_table = shuffle(schematic_table, pr)
-- pick schematic
local size = #randomized_schematic_table
for i = size, 1, -1 do
-- already enough buildings of that type?
if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then
building_all_info = randomized_schematic_table[i]
-- check distance to other buildings
local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"])
if distance_to_other_buildings_ok then
-- count built houses
count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1
rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
number_built = number_built + 1
settlement_info[index] = {
pos = pos_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
index = index + 1
break
end
end
end
if number_of_buildings == number_built then
break
end
end
r = r + pr:next(2,5)
end
end
settlements.debug("really ".. number_built)
return settlement_info
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=maxp.x-half_map_chunk_size,
y=maxp.y,
z=maxp.z-half_map_chunk_size
}
-- find center_surface of chunk
local center_surface , surface_material = settlements.find_surface(center)
-- go build settlement around center
if not center_surface then return false end
-- add settlement to list
table.insert(settlements_in_world, center_surface)
-- save list to file
settlements.save()
-- initialize all settlement_info table
local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr)
-- first building is townhall in the center
building_all_info = schematic_table[1]
local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
-- add to settlement info table
local index = 1
settlement_info[index] = {
pos = center_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
--increase index for following buildings
index = index + 1
-- now some buildings around in a circle, radius = size of town center
local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"]
-- draw j circles around center and increase radius by math.random(2,5)
for j = 1,20 do
if number_built < number_of_buildings then
-- 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 pos_surface, surface_material = settlements.find_surface(pos1)
if not pos_surface then break end
local randomized_schematic_table = shuffle(schematic_table, pr)
-- pick schematic
local size = #randomized_schematic_table
for i = size, 1, -1 do
-- already enough buildings of that type?
if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then
building_all_info = randomized_schematic_table[i]
-- check distance to other buildings
local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"])
if distance_to_other_buildings_ok then
-- count built houses
count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1
rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
number_built = number_built + 1
settlement_info[index] = {
pos = pos_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
index = index + 1
break
end
end
end
if number_of_buildings == number_built then
break
end
end
r = r + pr:next(2,5)
end
end
settlements.debug("really ".. number_built)
return settlement_info
end
-------------------------------------------------------------------------------
-- evaluate settlement_info and place schematics
-------------------------------------------------------------------------------
function settlements.place_schematics_lvm(settlement_info, pr)
for i, built_house in ipairs(settlement_info) do
for j, schem in ipairs(schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
building_all_info = schem
break
end
end
local pos = settlement_info[i]["pos"]
local rotation = settlement_info[i]["rotat"]
-- get building node material for better integration to surrounding
local platform_material = settlement_info[i]["surface_mat"]
platform_material_name = minetest.get_name_from_content_id(platform_material)
-- pick random material
local material = wallmaterial[pr:next(1,#wallmaterial)]
--
local building = building_all_info["mts"]
local replace_wall = building_all_info["rplc"]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return(schematic)"
-- 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_name)
--[[ Disable special junglewood for now.
-- special material for spawning npcs
schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood")
--]]
-- format schematic string
local schematic = loadstring(schem_lua)()
-- build foundation for the building an make room above
-- place schematic
minetest.place_schematic_on_vmanip(
vm,
pos,
schematic,
rotation,
nil,
true)
end
end
-------------------------------------------------------------------------------
-- evaluate settlement_info and place schematics
-------------------------------------------------------------------------------
function settlements.place_schematics(settlement_info, pr)
local building_all_info
for i, built_house in ipairs(settlement_info) do
for j, schem in ipairs(schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
building_all_info = schem
break
end
end
local pos = settlement_info[i]["pos"]
local rotation = settlement_info[i]["rotat"]
-- get building node material for better integration to surrounding
local platform_material = settlement_info[i]["surface_mat"]
--platform_material_name = minetest.get_name_from_content_id(platform_material)
-- pick random material
local material = wallmaterial[pr:next(1,#wallmaterial)]
--
local building = building_all_info["mts"]
local replace_wall = building_all_info["rplc"]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return(schematic)"
-- replace material
if replace_wall == "y" then
schem_lua = schem_lua:gsub("mcl_core:cobble", material)
end
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material)
--[[ Disable special junglewood for now.
-- special material for spawning npcs
schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood")
--]]
schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood")
schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air")
-- format schematic string
local schematic = loadstring(schem_lua)()
-- build foundation for the building an make room above
-- place schematic
minetest.place_schematic(
pos,
schematic,
rotation,
nil,
true)
end
end

@ -0,0 +1,83 @@
-- switch for debugging
settlements.debug = function(message)
-- minetest.chat_send_all(message)
-- minetest.log("warning", "[mcl_villages] "..message)
minetest.log("verbose", "[mcl_villages] "..message)
end
-- switch for lvm
settlements.lvm = false
settlements.last_settlement = os.time()
-- material to replace cobblestone with
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:snowblock"
}
end
--
-- possible surfaces where buildings can be built
--
--
-- path to schematics
--
schem_path = settlements.modpath.."/schematics/"
--
-- list of schematics
--
schematic_table = {
{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 11, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08, rplc = "n"},
{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.050, rplc = "n"},
{name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04, rplc = "n"},
{name = "farm", mts = schem_path.."farm.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1, rplc = "n"},
{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 3, hheight = 13, hsize = 10, max_num = 0.1, rplc = "n"},
{name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04, rplc = "n"},
{name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 8, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.09, rplc = "n"},
{name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 13, max_num = 0.7, rplc = "n"},
{name = "tavern", mts = schem_path.."tavern.mts", hwidth = 11, hdepth = 10, hheight = 10, hsize = 13, max_num = 0.050, rplc = "n"},
{name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 6, hsize = 10, max_num = 0.045, rplc = "n"},
}
--
-- list of settlements, load on server start up
--
settlements_in_world = {}
--
-- min_distance between settlements
--
settlements.min_dist_settlements = 64
--
-- maximum allowed difference in height for building a sttlement
--
max_height_difference = 56
--
--
--
half_map_chunk_size = 40
quarter_map_chunk_size = 20

@ -0,0 +1,30 @@
--
function settlements.convert_mts_to_lua()
local building = schem_path.."townhall.mts"
local str = minetest.serialize_schematic(building, "lua", {lua_use_comments = true, lua_num_indent_spaces = 0}).." return(schematic)"
local schematic = loadstring(str)()
local file = io.open(schem_path.."church"..".lua", "w")
file:write(dump(schematic))
file:close()
print(dump(schematic))
end
function settlements.mts_save()
local f = assert(io.open(schem_path.."hut.lua", "r"))
local content = f:read("*all").." return(schematic2)"
f:close()
local schematic2 = loadstring("schematic2 = "..content)()
local seb = minetest.serialize_schematic(schematic2, "mts", {})
local filename = schem_path .. "hut2" .. ".mts"
filename = filename:gsub("\"", "\\\""):gsub("\\", "\\\\")
local file, err = io.open(filename, "wb")
if err == nil and seb then
file:write(seb)
file:flush()
file:close()
end
print("Wrote: " .. filename)
end

@ -0,0 +1,3 @@
mcl_core
mcl_farming?
mobs_mc?

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

@ -0,0 +1,235 @@
-- eclipse debugging lines
--require("debugger")(idehost, ideport, idekey)
--zerobrane debugging lines
--package.cpath = package.cpath .. ";/usr/share/lua/5.2/?.so"
--package.path = package.path .. ";/usr/share/zbstudio/lualibs/mobdebug/?.lua"
--require('mobdebug').start()
settlements = {}
settlements.modpath = minetest.get_modpath("mcl_villages");
dofile(settlements.modpath.."/const.lua")
dofile(settlements.modpath.."/utils.lua")
dofile(settlements.modpath.."/foundation.lua")
dofile(settlements.modpath.."/buildings.lua")
dofile(settlements.modpath.."/paths.lua")
dofile(settlements.modpath.."/convert_lua_mts.lua")
--
-- load settlements on server
--
settlements_in_world = settlements.load()
settlements.grundstellungen()
--[[ Disable custom node spawning.
--
-- register block for npc spawn
--
minetest.register_node("settlements:junglewood", {
description = "special junglewood floor",
tiles = {"default_junglewood.png"},
groups = {choppy=3, wood=2},
sounds = default.node_sound_wood_defaults(),
})
--]]
--[[ Enable for testing, but use MineClone2's own spawn code if/when merging.
--
-- register inhabitants
--
if minetest.get_modpath("mobs_mc") ~= nil then
mobs:register_spawn("mobs_mc:villager", --name
{"mcl_core:stonebrickcarved"}, --nodes
15, --max_light
0, --min_light
20, --chance
7, --active_object_count
31000, --max_height
nil) --day_toggle
end
--]]
--
-- on map generation, try to build a settlement
--
local function build_a_settlement_no_delay(minp, maxp, blockseed)
local settlement_info
local pr = PseudoRandom(blockseed)
--
-- fill settlement_info with buildings and their data
--
if settlements.lvm == true then
-- get LVM of current chunk
local vm, data, va, emin, emax = settlements.getlvm(minp, maxp)
settlement_info = settlements.create_site_plan_lvm(maxp, minp, pr)
else
settlement_info = settlements.create_site_plan(maxp, minp, pr)
end
if not settlement_info then return end
-- evaluate settlement_info and prepair terrain
if settlements.lvm == true then
settlements.terraform_lvm(settlement_info, pr)
else
settlements.terraform(settlement_info, pr)
end
-- evaluate settlement_info and build paths between buildings
if settlements.lvm == true then
settlements.paths_lvm(settlement_info, minp)
else
settlements.paths(settlement_info)
end
-- evaluate settlement_info and place schematics
if settlements.lvm == true then
vm:set_data(data)
settlements.place_schematics_lvm(settlement_info, pr)
vm:write_to_map(true)
else
settlements.place_schematics(settlement_info, pr)
end
-- evaluate settlement_info and initialize furnaces and chests
settlements.initialize_nodes(settlement_info, pr)
end
local function ecb_build_a_settlement(blockpos, action, calls_remaining, param)
if calls_remaining <= 0 then
build_a_settlement_no_delay(param.minp, param.maxp, param.blockseed)
end
end
minetest.register_on_generated(function(minp, maxp, blockseed)
-- needed for manual and automated settlement building
local heightmap = minetest.get_mapgen_object("heightmap")
-- randomly try to build settlements
if blockseed % 77 ~= 17 then return end
-- don't build settlement underground
if maxp.y < 0 then return end
-- don't build settlements too close to each other
local center_of_chunk = {
x=maxp.x-half_map_chunk_size,
y=maxp.y-half_map_chunk_size,
z=maxp.z-half_map_chunk_size
}
local dist_ok = settlements.check_distance_other_settlements(center_of_chunk)
if dist_ok == false then return end
-- don't build settlements on (too) uneven terrain
local height_difference = settlements.evaluate_heightmap(minp, maxp)
if height_difference > max_height_difference then return end
minetest.emerge_area(vector.subtract(minp,24), vector.add(maxp,24), ecb_build_a_settlement, {minp = vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed})
-- old way - wait 3 seconds:
-- minetest.after(3, ecb_build_a_settlement, nil, 1, 0, {minp = vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed})
end)
--
-- manually place buildings, for debugging only
--
minetest.register_craftitem("mcl_villages:tool", {
description = "mcl_villages build tool",
inventory_image = "default_tool_woodshovel.png",
--[[ Disable on_use for now.
-- build single house
--
on_use = function(itemstack, placer, pointed_thing)
local center_surface = pointed_thing.under
if center_surface then
local building_all_info = {name = "blacksmith",
mts = schem_path.."blacksmith.mts",
hsize = 13,
max_num = 0.9,
rplc = "n"}
settlements.build_schematic(center_surface,
building_all_info["mts"],
building_all_info["rplc"],
building_all_info["name"])
-- settlements.convert_mts_to_lua()
-- settlements.mts_save()
end
end,
--]]
--
-- build ssettlement
--
on_place = function(itemstack, placer, pointed_thing)
local pr = PseudoRandom(math.rand(0,32767))
-- enable debug routines
local center_surface = pointed_thing.under
if center_surface then
local minp = {
x=center_surface.x-half_map_chunk_size,
y=center_surface.y-half_map_chunk_size,
z=center_surface.z-half_map_chunk_size
}
local maxp = {
x=center_surface.x+half_map_chunk_size,
y=center_surface.y+half_map_chunk_size,
z=center_surface.z+half_map_chunk_size
}
--
-- get LVM of current chunk
--
local vm, data, va, emin, emax = settlements.getlvm(minp, maxp)
--
-- fill settlement_info with buildings and their data
--
local start_time = os.time()
local settlement_info
if settlements.lvm == true then
settlement_info = settlements.create_site_plan_lvm(maxp, minp, pr)
else
settlement_info = settlements.create_site_plan(maxp, minp, pr)
end
if not settlement_info then return end
--
-- evaluate settlement_info and prepair terrain
--
if settlements.lvm == true then
settlements.terraform_lvm(settlement_info, pr)
else
settlements.terraform(settlement_info, pr)
end
--
-- evaluate settlement_info and build paths between buildings
--
if settlements.lvm == true then
settlements.paths_lvm(settlement_info, minp)
else
settlements.paths(settlement_info)
end
--
-- evaluate settlement_info and place schematics
--
if settlements.lvm == true then
vm:set_data(data)
settlements.place_schematics_lvm(pr)
vm:write_to_map(true)
else
settlements.place_schematics()
end
--
-- evaluate settlement_info and initialize furnaces and chests
--
settlements.initialize_nodes(settlement_info, pr)
local end_time = os.time()
minetest.chat_send_all("Time ".. end_time - start_time)
--
--settlements.convert_mts_to_lua()
--settlements.mts_save()
end
end
})

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

@ -0,0 +1,424 @@
local c_dirt_with_grass = minetest.get_content_id("mcl_core:dirt_with_grass")
local c_dirt_with_snow = minetest.get_content_id("mcl_core:dirt_with_grass_snow")
--local c_dirt_with_dry_grass = minetest.get_content_id("mcl_core:dirt_with_dry_grass")
local c_podzol = minetest.get_content_id("mcl_core:podzol")
local c_sand = minetest.get_content_id("mcl_core:sand")
local c_desert_sand = minetest.get_content_id("mcl_core:redsand")
--local c_silver_sand = minetest.get_content_id("mcl_core:silver_sand")
--
local c_air = minetest.get_content_id("air")
local c_snow = minetest.get_content_id("mcl_core:snowblock")
local c_fern_1 = minetest.get_content_id("mcl_flowers:fern")
local c_fern_2 = minetest.get_content_id("mcl_flowers:fern")
local c_fern_3 = minetest.get_content_id("mcl_flowers:fern")
local c_rose = minetest.get_content_id("mcl_flowers:poppy")
local c_viola = minetest.get_content_id("mcl_flowers:blue_orchid")
local c_geranium = minetest.get_content_id("mcl_flowers:allium")
local c_tulip = minetest.get_content_id("mcl_flowers:tulip_orange")
local c_dandelion_y = minetest.get_content_id("mcl_flowers:dandelion")
local c_dandelion_w = minetest.get_content_id("mcl_flowers:oxeye_daisy")
local c_bush_leaves = minetest.get_content_id("mcl_core:leaves")
local c_bush_stem = minetest.get_content_id("mcl_core:tree")
local c_a_bush_leaves = minetest.get_content_id("mcl_core:acacialeaves")
local c_a_bush_stem = minetest.get_content_id("mcl_core:acaciatree")
local c_water_source = minetest.get_content_id("mcl_core:water_source")
local c_water_flowing = minetest.get_content_id("mcl_core:water_flowing")
-------------------------------------------------------------------------------
-- function to copy tables
-------------------------------------------------------------------------------
function settlements.shallowCopy(original)
local copy = {}
for key, value in pairs(original) do
copy[key] = value
end
return copy
end
--
--
--
function settlements.round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
-------------------------------------------------------------------------------
-- function to find surface block y coordinate
-------------------------------------------------------------------------------
function settlements.find_surface_lvm(pos, minp)
--ab hier altes verfahren
local p6 = vector.new(pos)
local surface_mat = {
c_dirt_with_grass,
c_dirt_with_snow,
--c_dirt_with_dry_grass,
c_podzol,
c_sand,
c_desert_sand
}
local cnt = 0
local itter -- count up or down
local cnt_max = 200
-- starting point for looking for surface
local vi = va:index(p6.x, p6.y, p6.z)
if data[vi] == nil then return nil end
local tmp = minetest.get_name_from_content_id(data[vi])
if data[vi] == c_air then
itter = -1
else
itter = 1
end
while cnt < cnt_max do
cnt = cnt+1
local vi = va:index(p6.x, p6.y, p6.z)
-- local tmp = minetest.get_name_from_content_id(data[vi])
-- if vi == nil
-- then
-- return nil
-- end
for i, mats in ipairs(surface_mat) do
local node_check = va:index(p6.x, p6.y+1, p6.z)
if node_check and vi and data[vi] == mats and
(data[node_check] ~= c_water_source and
data[node_check] ~= c_water_flowing
)
then
local tmp = minetest.get_name_from_content_id(data[node_check])
return p6, mats
end
end
p6.y = p6.y + itter
if p6.y < 0 then return nil end
end
return nil --]]
end
-------------------------------------------------------------------------------
-- function to find surface block y coordinate
-- returns surface postion
-------------------------------------------------------------------------------
function settlements.find_surface(pos)
local p6 = vector.new(pos)
local cnt = 0
local itter -- count up or down
local cnt_max = 200
-- check, in which direction to look for surface
local surface_node = minetest.get_node_or_nil(p6)
if surface_node and string.find(surface_node.name,"air") then
itter = -1
else
itter = 1
end
-- go through nodes an find surface
while cnt < cnt_max do
cnt = cnt+1
minetest.forceload_block(p6)
surface_node = minetest.get_node_or_nil(p6)
if not surface_node then
-- Load the map at pos and try again
minetest.get_voxel_manip():read_from_map(p6, p6)
surface_node = minetest.get_node(p6)
if surface_node.name == "ignore" then
settlements.debug("find_surface1: nil or ignore")
return nil
end
end
-- if surface_node == nil or surface_node.name == "ignore" then
-- --return nil
-- local fl = minetest.forceload_block(p6)
-- if not fl then
--
-- return nil
-- end
-- end
--
-- Check Surface_node and Node above
--
if settlements.surface_mat[surface_node.name] then
local surface_node_plus_1 = minetest.get_node_or_nil({ x=p6.x, y=p6.y+1, z=p6.z})
if surface_node_plus_1 and surface_node and
(string.find(surface_node_plus_1.name,"air") or
string.find(surface_node_plus_1.name,"snow") or
string.find(surface_node_plus_1.name,"fern") or
string.find(surface_node_plus_1.name,"flower") or
string.find(surface_node_plus_1.name,"bush") or
string.find(surface_node_plus_1.name,"tree") or
string.find(surface_node_plus_1.name,"grass"))
then
settlements.debug("find_surface7: " ..surface_node.name.. " " .. surface_node_plus_1.name)
return p6, surface_node.name
else
settlements.debug("find_surface2: wrong surface+1")
end
else
settlements.debug("find_surface3: wrong surface "..surface_node.name.." at pos "..minetest.pos_to_string(p6))
end
p6.y = p6.y + itter
if p6.y < 0 then
settlements.debug("find_surface4: y<0")
return nil
end
end
settlements.debug("find_surface5: cnt_max overflow")
return nil
end
-------------------------------------------------------------------------------
-- check distance for new building
-------------------------------------------------------------------------------
function settlements.check_distance(settlement_info, building_pos, building_size)
local distance
for i, built_house in ipairs(settlement_info) do
distance = math.sqrt(
((building_pos.x - built_house["pos"].x)*(building_pos.x - built_house["pos"].x))+
((building_pos.z - built_house["pos"].z)*(building_pos.z - built_house["pos"].z)))
if distance < building_size or
distance < built_house["hsize"]
then
return false
end
end
return true
end
-------------------------------------------------------------------------------
-- save list of generated settlements
-------------------------------------------------------------------------------
function settlements.save()
local file = io.open(minetest.get_worldpath().."/settlements.txt", "w")
if file then
file:write(minetest.serialize(settlements_in_world))
file:close()
end
end
-------------------------------------------------------------------------------
-- load list of generated settlements
-------------------------------------------------------------------------------
function settlements.load()
local file = io.open(minetest.get_worldpath().."/settlements.txt", "r")
if file then
local table = minetest.deserialize(file:read("*all"))
if type(table) == "table" then
return table
end
end
return {}
end
-------------------------------------------------------------------------------
-- check distance to other settlements
-------------------------------------------------------------------------------
function settlements.check_distance_other_settlements(center_new_chunk)
-- local min_dist_settlements = 300
for i, pos in ipairs(settlements_in_world) do
local distance = vector.distance(center_new_chunk, pos)
-- minetest.chat_send_all("dist ".. distance)
if distance < settlements.min_dist_settlements then
return false
end
end
return true
end
-------------------------------------------------------------------------------
-- fill chests
-------------------------------------------------------------------------------
function settlements.fill_chest(pos, pr)
-- find chests within radius
--local chestpos = minetest.find_node_near(pos, 6, {"mcl_core:chest"})
local chestpos = pos
-- initialize chest (mts chests don't have meta)
local meta = minetest.get_meta(chestpos)
if meta:get_string("infotext") ~= "Chest" then
-- For MineClone2 0.70 or before
-- minetest.registered_nodes["mcl_chests:chest"].on_construct(chestpos)
--
-- For MineClone2 after commit 09ab1482b5 (the new entity chests)
minetest.registered_nodes["mcl_chests:chest_small"].on_construct(chestpos)
end
-- fill chest
local inv = minetest.get_inventory( {type="node", pos=chestpos} )
-- always
inv:add_item("main", "mcl_core:apple "..pr:next(1,3))
-- low value items
if pr:next(0,1) < 1 then
inv:add_item("main", "mcl_farming:bread "..pr:next(0,3))
inv:add_item("main", "mcl_core:iron_ingot "..pr:next(0,3))
inv:add_item("main", "mcl_farming:melon_item "..pr:next(0,3))
inv:add_item("main", "mcl_farming:carrot_item "..pr:next(0,3))
--[[
-- additional fillings when farmin mod enabled
if minetest.get_modpath("farming") ~= nil and farming.mod == "redo" then
if pr:next(0,1) < 1 then
inv:add_item("main", "mcl_farming:melon_item "..pr:next(0,3))
inv:add_item("main", "mcl_farming:carrot_item "..pr:next(0,3))
inv:add_item("main", "farming:corn "..pr:next(0,3))
end
end
--]]
end
-- medium value items
if pr:next(0,3) < 1 then
inv:add_item("main", "mcl_tools:pick_iron "..pr:next(0,1))
inv:add_item("main", "mcl_tools:pick_stone "..pr:next(0,1))
inv:add_item("main", "mcl_fire:flint_and_steel "..pr:next(0,1))
inv:add_item("main", "mcl_buckets:bucket_empty "..pr:next(0,1))
inv:add_item("main", "mcl_tools:sword_iron "..pr:next(0,1))
end
end
-------------------------------------------------------------------------------
-- initialize furnace
-------------------------------------------------------------------------------
function settlements.initialize_furnace(pos)
-- find chests within radius
local furnacepos = minetest.find_node_near(pos,
7, --radius
{"mcl_furnaces:furnace"})
-- initialize furnacepos (mts furnacepos don't have meta)
if furnacepos
then
local meta = minetest.get_meta(furnacepos)
if meta:get_string("infotext") ~= "furnace"
then
minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos)
end
end
end
-------------------------------------------------------------------------------
-- initialize anvil
-------------------------------------------------------------------------------
function settlements.initialize_anvil(pos)
-- find chests within radius
local anvilpos = minetest.find_node_near(pos,
7, --radius
{"mcl_anvils:anvil"})
-- initialize anvilpos (mts anvilpos don't have meta)
if anvilpos
then
local meta = minetest.get_meta(anvilpos)
if meta:get_string("infotext") ~= "anvil"
then
minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos)
end
end
end
-------------------------------------------------------------------------------
-- initialize furnace, chests, anvil
-------------------------------------------------------------------------------
local building_all_info
function settlements.initialize_nodes(settlement_info, pr)
for i, built_house in ipairs(settlement_info) do
for j, schem in ipairs(schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
building_all_info = schem
break
end
end
local width = building_all_info["hwidth"]
local depth = building_all_info["hdepth"]
local height = building_all_info["hheight"]
local p = settlement_info[i]["pos"]
for yi = 1,height do
for xi = 0,width do
for zi = 0,depth do
local ptemp = {x=p.x+xi, y=p.y+yi, z=p.z+zi}
local node = minetest.get_node(ptemp)
if node.name == "mcl_furnaces:furnace" or
node.name == "mcl_chests:chest" or
node.name == "mcl_anvils:anvil" then
minetest.registered_nodes[node.name].on_construct(ptemp)
end
-- when chest is found -> fill with stuff
if node.name == "mcl_chests:chest" then
minetest.after(3, settlements.fill_chest, ptemp, pr)
end
end
end
end
end
end
-------------------------------------------------------------------------------
-- randomize table
-------------------------------------------------------------------------------
function shuffle(tbl, pr)
local table = settlements.shallowCopy(tbl)
local size = #table
for i = size, 1, -1 do
local rand = pr:next(1, size)
table[i], table[rand] = table[rand], table[i]
end
return table
end
-------------------------------------------------------------------------------
-- evaluate heightmap
-------------------------------------------------------------------------------
function settlements.evaluate_heightmap()
local heightmap = minetest.get_mapgen_object("heightmap")
-- max height and min height, initialize with impossible values for easier first time setting
local max_y = -50000
local min_y = 50000
-- only evaluate the center square of heightmap 40 x 40
local square_start = 1621
local square_end = 1661
for j = 1 , 40, 1 do
for i = square_start, square_end, 1 do
-- skip buggy heightmaps, return high value
if heightmap[i] == -31000 or
heightmap[i] == 31000
then
return max_height_difference + 1
end
if heightmap[i] < min_y
then
min_y = heightmap[i]
end
if heightmap[i] > max_y
then
max_y = heightmap[i]
end
end
-- set next line
square_start = square_start + 80
square_end = square_end + 80
end
-- return the difference between highest and lowest pos in chunk
local height_diff = max_y - min_y
-- filter buggy heightmaps
if height_diff <= 1
then
return max_height_difference + 1
end
-- debug info
settlements.debug("heightdiff ".. height_diff)
return height_diff
end
-------------------------------------------------------------------------------
-- get LVM of current chunk
-------------------------------------------------------------------------------
function settlements.getlvm(minp, maxp)
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(minp, maxp)
local va = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local data = vm:get_data()
return vm, data, va, emin, emax
end
-------------------------------------------------------------------------------
-- get LVM of current chunk
-------------------------------------------------------------------------------
function settlements.setlvm(vm, data)
-- Write data
vm:set_data(data)
vm:write_to_map(true)
end
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------
function settlements.Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end

@ -15,7 +15,11 @@ end
-- Probability for every newly generated mapchunk to get corridors
local probability_railcaves_in_mapchunk = P(0.33333)
setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_railcaves_in_mapchunk"))
if setting then
-- Extra check to prevent mod griefing in singlenode, mcimported worlds.
local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name == "singlenode" then
probability_railcaves_in_mapchunk = P(0)
elseif setting then
probability_railcaves_in_mapchunk = P(setting)
end

@ -91,6 +91,9 @@ flame_sound (Flame sound) bool true
# Form: Image height / Image width
fire_animation_frames (Fire Animation Frames) int 8
# Whether to animate chests when open / close
animated_chests (Animated chests) bool true
[Experimental]
# Whether ice is translucent. If disabled, ice is fully opaque.
#
@ -126,3 +129,7 @@ mcl_superflat_classic (Classic superflat map generation) bool false
# WARNING: This setting has quite poor performance and can slow down your
# game by a lot.
mcl_node_particles (Block particles detail level) enum none high,medium,low,none
# If enabled, will run an LBM to fix the top 1/2 of double plants in mcimported worlds; defaults to true.
fix_doubleplants (Mcimport double plant fixes) bool true