diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb74c5a35..faa7cd75a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,3 +85,12 @@ Report all bugs and missing Minecraft features here: We have an IRC channel! Join us on #mineclone2 in freenode.net. + +## Creating releases +* Launch MineClone2 to make sure it still runs +* Update the version number in README.md +* Use `git tag ` 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 diff --git a/MISSING_ENGINE_FEATURES.md b/MISSING_ENGINE_FEATURES.md index 16f7e1572..fddb89f6c 100644 --- a/MISSING_ENGINE_FEATURES.md +++ b/MISSING_ENGINE_FEATURES.md @@ -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 diff --git a/mods/CORE/mcl_explosions/init.lua b/mods/CORE/mcl_explosions/init.lua index 862a2f9da..b23489861 100644 --- a/mods/CORE/mcl_explosions/init.lua +++ b/mods/CORE/mcl_explosions/init.lua @@ -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,39 +175,49 @@ 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 - for i = 1, #raydirs do - local rpos_x = pos.x - local rpos_y = pos.y - local rpos_z = pos.z - local rdir_x = raydirs[i].x - local rdir_y = raydirs[i].y - local rdir_z = raydirs[i].z - local rstr = (0.7 + math.random() * 0.6) * strength + if info.griefing then + for i = 1, #raydirs do + local rpos_x = pos.x + local rpos_y = pos.y + local rpos_z = pos.z + local rdir_x = raydirs[i].x + local rdir_y = raydirs[i].y + local rdir_z = raydirs[i].z + local rstr = (0.7 + math.random() * 0.6) * strength - for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do - local npos_x = math.floor(rpos_x + 0.5) - local npos_y = math.floor(rpos_y + 0.5) - local npos_z = math.floor(rpos_z + 0.5) - local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride + - npos_x - emin_x + 1 + for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do + local npos_x = math.floor(rpos_x + 0.5) + local npos_y = math.floor(rpos_y + 0.5) + local npos_z = math.floor(rpos_z + 0.5) + local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride + + npos_x - emin_x + 1 - local cid = data[idx] - local br = node_blastres[cid] - local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z}) + local cid = data[idx] + local br = node_blastres[cid] + if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then + br = max_blast_resistance + end - rpos_x = rpos_x + STEP_LENGTH * rdir_x - rpos_y = rpos_y + STEP_LENGTH * rdir_y - rpos_z = rpos_z + STEP_LENGTH * rdir_z + local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z}) - rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH + rpos_x = rpos_x + STEP_LENGTH * rdir_x + rpos_y = rpos_y + STEP_LENGTH * rdir_y + rpos_z = rpos_z + STEP_LENGTH * rdir_z - if rstr <= 0 then - break - end + rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH - if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then - destroy[hash] = idx + if rstr <= 0 then + break + end + + if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then + destroy[hash] = idx + end end end end @@ -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 diff --git a/mods/CORE/mcl_init/init.lua b/mods/CORE/mcl_init/init.lua index a1346f50b..ebbfd5591 100644 --- a/mods/CORE/mcl_init/init.lua +++ b/mods/CORE/mcl_init/init.lua @@ -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()) + diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 7e8513365..6c63c21ab 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -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 diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 821dbc0e9..430c97166 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -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, diff --git a/mods/ENTITIES/mobs_mc/creeper.lua b/mods/ENTITIES/mobs_mc/creeper.lua index 2beffcf83..f1648525a 100644 --- a/mods/ENTITIES/mobs_mc/creeper.lua +++ b/mods/ENTITIES/mobs_mc/creeper.lua @@ -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 diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 5078fa4a8..dc3fbd9ca 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -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, diff --git a/mods/ITEMS/mcl_bows/arrow.lua b/mods/ITEMS/mcl_bows/arrow.lua index c89a22c1a..9f5b3da2c 100644 --- a/mods/ITEMS/mcl_bows/arrow.lua +++ b/mods/ITEMS/mcl_bows/arrow.lua @@ -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", { diff --git a/mods/ITEMS/mcl_bows/bow.lua b/mods/ITEMS/mcl_bows/bow.lua index 573af298f..87820071d 100644 --- a/mods/ITEMS/mcl_bows/bow.lua +++ b/mods/ITEMS/mcl_bows/bow.lua @@ -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 diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index a695eb7c7..6c37cff35 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -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 - } - anim_start = anim_end - end -end +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}, +} 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 = } 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 - 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) + 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 diff --git a/mods/ITEMS/mcl_core/craftitems.lua b/mods/ITEMS/mcl_core/craftitems.lua index b27a6ca6a..7cffcb785 100644 --- a/mods/ITEMS/mcl_core/craftitems.lua +++ b/mods/ITEMS/mcl_core/craftitems.lua @@ -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 diff --git a/mods/ITEMS/mcl_enchanting/init.lua b/mods/ITEMS/mcl_enchanting/init.lua index 5735bfb3e..ab3dddbd3 100644 --- a/mods/ITEMS/mcl_enchanting/init.lua +++ b/mods/ITEMS/mcl_enchanting/init.lua @@ -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 = { diff --git a/mods/ITEMS/mcl_flowers/init.lua b/mods/ITEMS/mcl_flowers/init.lua index 42deede2f..af892bfa1 100644 --- a/mods/ITEMS/mcl_flowers/init.lua +++ b/mods/ITEMS/mcl_flowers/init.lua @@ -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 + diff --git a/mods/MAPGEN/mcl_dungeons/init.lua b/mods/MAPGEN/mcl_dungeons/init.lua index e1218c0bc..1ce1556b2 100644 --- a/mods/MAPGEN/mcl_dungeons/init.lua +++ b/mods/MAPGEN/mcl_dungeons/init.lua @@ -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 diff --git a/mods/MAPGEN/mcl_villages/README.txt b/mods/MAPGEN/mcl_villages/README.txt new file mode 100644 index 000000000..b266a131a --- /dev/null +++ b/mods/MAPGEN/mcl_villages/README.txt @@ -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 + diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua new file mode 100644 index 000000000..1dc9d0c25 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -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 diff --git a/mods/MAPGEN/mcl_villages/const.lua b/mods/MAPGEN/mcl_villages/const.lua new file mode 100644 index 000000000..5aa87078f --- /dev/null +++ b/mods/MAPGEN/mcl_villages/const.lua @@ -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 diff --git a/mods/MAPGEN/mcl_villages/convert_lua_mts.lua b/mods/MAPGEN/mcl_villages/convert_lua_mts.lua new file mode 100644 index 000000000..e66905ba0 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/convert_lua_mts.lua @@ -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 \ No newline at end of file diff --git a/mods/MAPGEN/mcl_villages/depends.txt b/mods/MAPGEN/mcl_villages/depends.txt new file mode 100644 index 000000000..3c04a572b --- /dev/null +++ b/mods/MAPGEN/mcl_villages/depends.txt @@ -0,0 +1,3 @@ +mcl_core +mcl_farming? +mobs_mc? diff --git a/mods/MAPGEN/mcl_villages/foundation.lua b/mods/MAPGEN/mcl_villages/foundation.lua new file mode 100644 index 000000000..677b4efdb --- /dev/null +++ b/mods/MAPGEN/mcl_villages/foundation.lua @@ -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 diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua new file mode 100644 index 000000000..6b795542b --- /dev/null +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -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 + }) + diff --git a/mods/MAPGEN/mcl_villages/paths.lua b/mods/MAPGEN/mcl_villages/paths.lua new file mode 100644 index 000000000..6601fc762 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/paths.lua @@ -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 diff --git a/mods/MAPGEN/mcl_villages/schematics/blacksmith.mts b/mods/MAPGEN/mcl_villages/schematics/blacksmith.mts new file mode 100644 index 000000000..cfdaf99ad Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/blacksmith.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/church.mts b/mods/MAPGEN/mcl_villages/schematics/church.mts new file mode 100644 index 000000000..dbf022cb4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/church.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/farm.mts b/mods/MAPGEN/mcl_villages/schematics/farm.mts new file mode 100644 index 000000000..9094c8681 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/farm.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/lamp.mts b/mods/MAPGEN/mcl_villages/schematics/lamp.mts new file mode 100644 index 000000000..c8d907eba Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/lamp.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/large_house.mts b/mods/MAPGEN/mcl_villages/schematics/large_house.mts new file mode 100644 index 000000000..7b749bf84 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/large_house.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/library.mts b/mods/MAPGEN/mcl_villages/schematics/library.mts new file mode 100644 index 000000000..b47e0b413 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/library.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/medium_house.mts b/mods/MAPGEN/mcl_villages/schematics/medium_house.mts new file mode 100644 index 000000000..43ce2391b Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/medium_house.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/small_house.mts b/mods/MAPGEN/mcl_villages/schematics/small_house.mts new file mode 100644 index 000000000..d7b62529c Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/small_house.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/tavern.mts b/mods/MAPGEN/mcl_villages/schematics/tavern.mts new file mode 100644 index 000000000..5eae8ae23 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/tavern.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/well.mts b/mods/MAPGEN/mcl_villages/schematics/well.mts new file mode 100644 index 000000000..6ea47fea4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/well.mts differ diff --git a/mods/MAPGEN/mcl_villages/screenshot.png b/mods/MAPGEN/mcl_villages/screenshot.png new file mode 100644 index 000000000..64185d0b9 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/screenshot.png differ diff --git a/mods/MAPGEN/mcl_villages/screenshot_2.png b/mods/MAPGEN/mcl_villages/screenshot_2.png new file mode 100644 index 000000000..ba299c851 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/screenshot_2.png differ diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua new file mode 100644 index 000000000..66a3fd6b5 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/utils.lua @@ -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 diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 3cc0d75da..f2e02d997 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -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 diff --git a/settingtypes.txt b/settingtypes.txt index 49274244a..5c9fc1778 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -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