From d7ac288ba1d5481b85a31a6e54ce0f621beffde6 Mon Sep 17 00:00:00 2001 From: kno10 Date: Thu, 5 Sep 2024 01:03:33 +0200 Subject: [PATCH] cleanups and fixes --- mods/MAPGEN/mcl_structures/fossil.lua | 2 +- mods/MAPGEN/mcl_structures/ocean_ruins.lua | 14 +- mods/MAPGEN/mcl_structures/ocean_temple.lua | 4 +- .../mcl_structures/pillager_outpost.lua | 2 +- mods/MAPGEN/mcl_structures/shipwrecks.lua | 50 ++----- mods/MAPGEN/mcl_villages/buildings.lua | 2 +- mods/MAPGEN/vl_structures/API.md | 124 ++++++++++++++---- mods/MAPGEN/vl_structures/api.lua | 100 +++++--------- mods/MAPGEN/vl_structures/emerge.lua | 42 +++--- mods/MAPGEN/vl_structures/init.lua | 11 +- mods/MAPGEN/vl_structures/mod.conf | 5 +- mods/MAPGEN/vl_structures/spawning.lua | 87 ++++++------ mods/MAPGEN/vl_structures/util.lua | 1 + mods/MAPGEN/vl_terraforming/API.md | 8 +- mods/MAPGEN/vl_terraforming/foundation.lua | 2 +- mods/MAPGEN/vl_terraforming/level.lua | 69 +++++++++- mods/MAPGEN/vl_terraforming/mod.conf | 2 +- settingtypes.txt | 4 +- 18 files changed, 300 insertions(+), 229 deletions(-) diff --git a/mods/MAPGEN/mcl_structures/fossil.lua b/mods/MAPGEN/mcl_structures/fossil.lua index 78bc03e1d..620abe338 100644 --- a/mods/MAPGEN/mcl_structures/fossil.lua +++ b/mods/MAPGEN/mcl_structures/fossil.lua @@ -4,7 +4,7 @@ local modpath = minetest.get_modpath(modname) vl_structures.register_structure("fossil",{ place_on = {"group:material_stone","group:sand"}, flags = "place_center_x, place_center_z", - prepare = { }, + prepare = false, chunk_probability = 15, -- was 25, FIXME: needs rebalancing y_offset = function(pr) return pr:next(-32,-16) end, y_max = 15, diff --git a/mods/MAPGEN/mcl_structures/ocean_ruins.lua b/mods/MAPGEN/mcl_structures/ocean_ruins.lua index 86986c5d6..2212b21ac 100644 --- a/mods/MAPGEN/mcl_structures/ocean_ruins.lua +++ b/mods/MAPGEN/mcl_structures/ocean_ruins.lua @@ -1,5 +1,6 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) +local water_level = minetest.get_mapgen_setting("water_level") local cold_oceans = { "RoofedForest_ocean", @@ -71,14 +72,15 @@ local warm_oceans = { local cold = { place_on = {"group:sand","mcl_core:gravel","mcl_core:dirt","mcl_core:clay","group:material_stone"}, - spawn_by = {"mcl_core:water_source"}, + spawn_by = {"group:water"}, num_spawn_by = 2, - flags = "place_center_x, place_center_z, force_placement", - y_offset = -1, - y_min = mcl_vars.mg_overworld_min, - y_max = -2, + chunk_probability = 10, -- todo: 15? biomes = cold_oceans, - chunk_probability = 10, + y_min = mcl_vars.mg_overworld_min, + y_max = water_level - 6, + y_offset = -1, + flags = "place_center_x, place_center_z, force_placement", + prepare = { foundation = -2, clear = false, mode="water" }, filenames = { modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts", modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts", diff --git a/mods/MAPGEN/mcl_structures/ocean_temple.lua b/mods/MAPGEN/mcl_structures/ocean_temple.lua index 33ec4c56e..73aa5b270 100644 --- a/mods/MAPGEN/mcl_structures/ocean_temple.lua +++ b/mods/MAPGEN/mcl_structures/ocean_temple.lua @@ -82,7 +82,7 @@ vl_structures.register_structure("ocean_temple",{ }, flags = "force_placement", force_place = true, - prepare = { tolerance = -1, clear = false, foundation = false }, + prepare = { tolerance = 8, clear = false, foundation = 3, mode="water" }, biomes = ocean_biomes, y_max = water_level-4, y_min = mcl_vars.mg_overworld_min, @@ -90,7 +90,7 @@ vl_structures.register_structure("ocean_temple",{ modpath .. "/schematics/mcl_structures_ocean_temple.mts", modpath .. "/schematics/mcl_structures_ocean_temple_2.mts", }, - y_offset = function(pr) return pr:next(-2,0) end, + y_offset = -1, --function(pr) return pr:next(-2,-1) end, -- fewer mobs if buried in sand after_place = function(p, _, pr, p1, p2) vl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true) vl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true) diff --git a/mods/MAPGEN/mcl_structures/pillager_outpost.lua b/mods/MAPGEN/mcl_structures/pillager_outpost.lua index 732b80d80..b310b33b4 100644 --- a/mods/MAPGEN/mcl_structures/pillager_outpost.lua +++ b/mods/MAPGEN/mcl_structures/pillager_outpost.lua @@ -6,7 +6,7 @@ local spawnon = {"mcl_core:stripped_oak","mcl_stairs:slab_birchwood_top"} vl_structures.register_structure("pillager_outpost",{ place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:sand"}, flags = "place_center_x, place_center_z", - prepare = { padding = 3, corners = 4, foundation = -6, clear = true }, + prepare = { padding = 3, corners = 4, foundation = -8, clear = true }, y_offset = 0, chunk_probability = 15, y_max = mcl_vars.mg_overworld_max, diff --git a/mods/MAPGEN/mcl_structures/shipwrecks.lua b/mods/MAPGEN/mcl_structures/shipwrecks.lua index fe6755893..8506c509b 100644 --- a/mods/MAPGEN/mcl_structures/shipwrecks.lua +++ b/mods/MAPGEN/mcl_structures/shipwrecks.lua @@ -2,15 +2,6 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) local water_level = minetest.get_mapgen_setting("water_level") ---schematics by chmodsayshello -local schems = { - modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts", - modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts", - modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts", - modpath.."/schematics/mcl_structures_shipwreck_half_front.mts", - modpath.."/schematics/mcl_structures_shipwreck_half_back.mts", -} - local ocean_biomes = { "RoofedForest_ocean", "JungleEdgeM_ocean", @@ -74,44 +65,27 @@ local ocean_biomes = { "JungleM_ocean" } -local beach_biomes = { - "FlowerForest_beach", - "Forest_beach", - "StoneBeach", - "ColdTaiga_beach_water", - "Taiga_beach", - "Savanna_beach", - "Plains_beach", - "ExtremeHills_beach", - "ColdTaiga_beach", - "Swampland_shore", - "MushroomIslandShore", - "JungleM_shore", - "Jungle_shore" -} - -- FIXME: integrate treasure maps from MCLA vl_structures.register_structure("shipwreck",{ place_on = {"group:sand","mcl_core:gravel"}, spawn_by = {"group:water"}, num_spawn_by = 4, - noise_params = { - offset = 0, - scale = 0.000022, - spread = {x = 250, y = 250, z = 250}, - seed = 3, - octaves = 3, - persist = 0.001, - flags = "absvalue", - }, - flags = "force_placement", + chunk_probability = 10, -- todo: 15? biomes = ocean_biomes, - y_max = water_level-4, y_min = mcl_vars.mg_overworld_min, - prepare = { tolerance = -1, clear = false, foundation = false }, - filenames = schems, + y_max = water_level-4, y_offset = function(pr) return pr:next(-4,-2) end, + flags = "place_center_x, place_center_z, force_placement", + prepare = { tolerance = -1, clear = false, foundation = -2, mode = "water" }, + filenames = { + --schematics by chmodsayshello + modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts", + modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts", + modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts", + modpath.."/schematics/mcl_structures_shipwreck_half_front.mts", + modpath.."/schematics/mcl_structures_shipwreck_half_back.mts", + }, loot = { ["mcl_chests:chest_small"] = { { diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua index c8f3b250f..5c98e2a6e 100644 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -251,7 +251,7 @@ function mcl_villages.post_process_village(blockseed) local jobs, beds = {}, {} local bell_pos = vector.copy(settlement_info[1].pos) - local bell = vector.offset(bell_pos, 0, 1, 0) + local bell = vector.offset(bell_pos, 0, 2, 0) local biome_name = minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome) -- Spawn Golem diff --git a/mods/MAPGEN/vl_structures/API.md b/mods/MAPGEN/vl_structures/API.md index d59993182..5a4b3fb32 100644 --- a/mods/MAPGEN/vl_structures/API.md +++ b/mods/MAPGEN/vl_structures/API.md @@ -1,37 +1,107 @@ # vl_structures -Updated API for structure spawning for VoxeLibre and Mineclonia +Updated API for structure spawning. -## vl_structures.register_structure(name,structure definition,nospawn) -If nospawn is truthy the structure will not be placed by mapgen and the decoration parameters can be omitted. This is intended for secondary structures the placement of which gets triggered by the placement of other structures. It can also be used to register testing structures so they can be used with /spawnstruct. +This module was developed with VoxeLibre and Mineclonia in mind, but means to be portable or at least easy to adapt to other games. -### structure definition +## structure definition + +Structures in this API are defined using the following table: + +``` { - fill_ratio = OR noise = {}, - biomes = {}, - y_min =, - y_max =, - place_on = {}, - spawn_by = {}, - num_spawn_by =, - flags = (default: "place_center_x, place_center_z, force_placement") - (same as decoration def) - y_offset =, --can be a number or a function returning a number - filenames = {} OR place_func = function(pos,def,pr) - -- filenames can be a list of any schematics accepted by mcl_structures.place_schematic / minetest.place_schematic - on_place = function(pos,def,pr) end, - -- called before placement. denies placement when returning falsy. - after_place = function(pos,def,pr) - -- executed after successful placement - prepare = table, -- a foundation is automatically built for the structure - loot = , - --a table of loot tables for mcl_loot indexed by node names - -- e.g. { ["mcl_chests:chest_small"] = {loot},... } + name =, -- structure identifier for logging + priority = 100, -- priority to make placement order more deterministic. Default 100 except for terrain features (900) + chunk_probability =, -- ratio that a block is chosen, 10 means 1-in-10 blocks + fill_ratio = nil, -- OR number of structure spawn attempts per map chunk, default is 1/(80 x 80) when chunk_probability is set + noise = nil, -- OR specify noise parameters, as per minetest.register_decoration + y_min =, -- minimum depth + y_max =, -- maximum depth + biomes = {}, -- biome restriction + place_on = {}, -- if nil, the structure will not be automatically spawned + spawn_by = {}, -- nodes required nearby, as in minetest.register_decoration + num_spawn_by =, -- number of nodes required nearby + prepare =, -- configure foundation and clearing, see vl_terraforming -- ignored for place_func + flags =, -- minetest.register_decoration placement flags, default: "place_center_x, place_center_z" + y_offset =, -- vertical placement offset, can be a number or a function(pr) returning a number + filenames = {}, -- table of schematic filenames + schematics = {}, -- OR table of preloaded schematics + place_func = function(pos,def,pr,blockseed) + -- OR a function to place a structure + after_place = function(pos,def,pr,pmin,pmax,size,rotation) + -- callback executed after successful placement + loot =, -- a table of loot tables for mcl_loot indexed by node names -- ignored for place_func, to be removed + -- e.g. { ["mcl_chests:chest_small"] = {loot},... } + terrain_feature =, -- affects placement priority and disables logging for uninteresting structures + daughters =, -- substructures to spawn, unstable API } +``` + +## vl_structures.register_structure(name,def) + +Register a new structure spawn. + +For extension modules, if you choose a larger (later) placement priority, this +should be less likely to change spawning of original structures and keep the +resulting maps more consistent across seeds. + +## vl_structures.load_schematic(filename, name) + +Load a schematic from a given file name, the name is used for error logging; otherwise it will be derived from the file name. + +## vl_structures.place_structure(pos, def, pr, blockseed, rot) + +Places structure defined by def at position pos, using the pseudorandom pr. + +blockseed is only used by the place_func call, and unused for many simple structures. + +rot is optional, it will then be chosen randomly. + +This is usually called from the mapgen decoration gennotify mechanism, but can be used for substructure spawns. + ## vl_structures.registered_structures + Table of the registered structure defintions indexed by name. -## vl_structures.place_structure(pos, def, pr) -Places a structure using the mapgen placement function +## vl_structures.place_schematic(pos, yoffset, schematic, rotation, def, pr) + +Spawn a structure as defined by "def" at the given position, yoffset, schematic, and rotation. + +This is primarily meant for substructure placement where size (and hence schematic and offsets) need to be fixed before computing the position. + +## vl_structures.parse_rotation(rotation, pr) + +Parse a rotation value (stirngs "0", "90", "180", "270" or "random"), or choosing a random rotation. + +## vl_structures.size_rotated(size, rotation) + +Return the size after rotation, i.e., if rotation is 90 or 270, the x and z sizes are swapped. + +## vl_structures.top_left_from_flags(pos, size, flags) + +Compute the top left corner from the flags, i.e., parse place_center_x, place_center_z etc. + +## vl_structures.get_extends(pos, size, yoffset, rotation, flags) + +Parse rotation and flags, and return the center, minimum corner, maximum corner, and size. + +## vl_structures.init_node_construct(pos) + +Call on_construct callbacks for the node at the given position. + +## vl_structures.construct_nodes(p1,p2,nodes) + +Find all nodes of the listed types in the area and call their on_construct callbacks. + +## vl_structures.fill_chests(p1,p2,loot,pr) + +Fill all loot containers in the area, requires mcl_loot and likely should be moved to the loot API. + +## vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water) + +This function spawns the desired mobs in the given area. The function should move to the mobs API. + +## vl_structures.register_structure_spawn(def) + +This function creates a spawn ABM for the desired mobs. The function should move to the mobs API. -## vl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param) diff --git a/mods/MAPGEN/vl_structures/api.lua b/mods/MAPGEN/vl_structures/api.lua index 7d1a5a8cd..a6632cb58 100644 --- a/mods/MAPGEN/vl_structures/api.lua +++ b/mods/MAPGEN/vl_structures/api.lua @@ -1,21 +1,17 @@ vl_structures.registered_structures = {} local structure_boost = tonumber(minetest.settings:get("vl_structures_boost")) or 1 -local worldseed = minetest.get_mapgen_setting("seed") -local RANDOM_SEED_OFFSET = 959 -- random constant that should be unique across each library - -local vector_offset = vector.offset - --- FIXME: switch to vl_structures_logging? -local logging = true or minetest.settings:get_bool("mcl_logging_structures", true) - --- FIXME: switch to vl_structures_disabled? -local disabled_structures = minetest.settings:get("mcl_disabled_structures") +local logging = minetest.settings:get_bool("vl_structures_logging", false) +local disabled_structures = minetest.settings:get("vl_structures_disabled") disabled_structures = disabled_structures and disabled_structures:split(",") or {} function vl_structures.is_disabled(structname) return table.indexof(disabled_structures,structname) ~= -1 end +local worldseed = minetest.get_mapgen_setting("seed") +local RANDOM_SEED_OFFSET = 959 -- random constant that should be unique across each library +local vector_offset = vector.offset + --- Trim a full path name to its last two parts as short name for logging local function basename(filename) local fn = string.split(filename, "/") @@ -101,30 +97,28 @@ function vl_structures.place_structure(pos, def, pr, blockseed, rot) end -- structure has a custom place function if not def.place_func then - minetest.log("warning","[vl_structures] no schematics and no place_func for schematic "..def.name) + minetest.log("warning", "[vl_structures] no schematics and no place_func for schematic "..def.name) return false end local pp = yoffset ~= 0 and vector_offset(pos, 0, yoffset, 0) or pos if def.place_func and def.prepare then - minetest.log("warning", "[vl_structures] needed prepare for "..def.name.." placed at "..minetest.pos_to_string(pp).." but do not have size information") + minetest.log("warning", "[vl_structures] needed prepare for "..def.name.." placed at "..minetest.pos_to_string(pp).." but do not have size information.") end if def.place_func and def.place_func(pp,def,pr,blockseed) then - if not def.after_place or (def.after_place and def.after_place(pos,def,pr,pmin,pmax,size,param.rotation)) then - if def.sidelen then - local p1, p2 = vector_offset(pos,-def.sidelen,-def.sidelen,-def.sidelen), vector.offset(pos,def.sidelen,def.sidelen,def.sidelen) - if def.loot then vl_structures.fill_chests(p1,p2,def.loot,pr) end - if def.construct_nodes then vl_structures.construct_nodes(p1,p2,def.construct_nodes) end - end - if log_enabled then - minetest.log("action","[vl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp)) - end - return true - else - minetest.log("warning","[vl_structures] after_place failed for schematic "..def.name) + if def.after_place and not def.after_place(pos,def,pr,pmin,pmax,size,param.rotation) then + minetest.log("warning", "[vl_structures] after_place failed for structure "..def.name) return false end + if log_enabled then + minetest.log("action","[vl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp)) + end + return true elseif log_enabled then - minetest.log("warning","[vl_structures] place_func failed for schematic "..def.name) + if def.place_func then + minetest.log("warning","[vl_structures] place_func failed for structure "..def.name) + else + minetest.log("warning","[vl_structures] do not know how to place structure "..def.name) + end end end @@ -139,13 +133,13 @@ function vl_structures.register_structure(name,def) def.name = name vl_structures.registered_structures[name] = def if def.prepare and def.prepare.clear == nil and (def.prepare.clear_bottom or def.prepare.clear_top) then def.prepare.clear = true end - if not def.noise_params and def.chunk_probability and not def.fill_ratio then + if not def.fill_ratio and def.chunk_probability and not def.noise_params then def.fill_ratio = 1.1/80/80 -- 1 per chunk, controlled by chunk probability only end def.flags = def.flags or vl_structures.DEFAULT_FLAGS if def.filenames then for _, filename in ipairs(def.filenames) do - if not mcl_util.file_exists(filename) then + if mcl_util and not mcl_util.file_exists(filename) then minetest.log("warning","[vl_structures] schematic "..(name or "unknown").." is missing file "..basename(filename)) return nil end @@ -153,21 +147,22 @@ function vl_structures.register_structure(name,def) end if def.place_on then minetest.register_on_mods_loaded(function() - def.deco = mcl_mapgen_core.register_decoration({ + local register_decoration = mcl_mapgen_core.register_decoration or minetest.register_decoration -- optional dependency + register_decoration({ name = "vl_structures:"..name, priority = def.priority or (def.terrain_feature and 900) or 100, -- run before regular decorations - deco_type = "schematic", - schematic = EMPTY_SCHEMATIC, -- use gennotify only + fill_ratio = def.fill_ratio, + noise_params = def.noise_params, + y_max = def.y_max, + y_min = def.y_min, + biomes = def.biomes, place_on = def.place_on, spawn_by = def.spawn_by, num_spawn_by = def.num_spawn_by, sidelen = 80, -- no def.sidelen subdivisions for now, this field was used differently before - fill_ratio = def.fill_ratio, - noise_params = def.noise_params, flags = def.flags, - biomes = def.biomes, - y_max = def.y_max, - y_min = def.y_min, + deco_type = "schematic", + schematic = EMPTY_SCHEMATIC, -- use gennotify only gen_callback = function(t,minp,maxp,blockseed) for _, pos in ipairs(t) do local pr = PcgRandom(minetest.hash_node_position(pos) + worldseed + RANDOM_SEED_OFFSET) @@ -183,6 +178,7 @@ function vl_structures.register_structure(name,def) end -- To avoid a cyclic dependency, run this when modules have finished loading +-- Maybe we can eventually remove this - the end portal should likely go into the mapgen itself. minetest.register_on_mods_loaded(function() mcl_mapgen_core.register_generator("static structures", nil, function(minp, maxp, blockseed) for _,struct in pairs(vl_structures.registered_structures) do @@ -200,37 +196,3 @@ mcl_mapgen_core.register_generator("static structures", nil, function(minp, maxp end, 100, true) end) -local structure_spawns = {} -function vl_structures.register_structure_spawn(def) - --name,y_min,y_max,spawnon,biomes,chance,interval,limit - minetest.register_abm({ - label = "Spawn "..def.name, - nodenames = def.spawnon, - min_y = def.y_min or -31000, - max_y = def.y_max or 31000, - interval = def.interval or 60, - chance = def.chance or 5, - action = function(pos, node, active_object_count, active_object_count_wider) - local limit = def.limit or 7 - if active_object_count_wider > limit + mob_cap_animal then return end - if active_object_count_wider > mob_cap_player then return end - local p = vector_offset(pos, 0, 1, 0) - local pname = minetest.get_node(p).name - if def.type_of_spawning == "water" then - if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end - else - if pname ~= "air" then return end - end - if minetest.get_meta(pos):get_string("spawnblock") == "" then return end - if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then - if table.indexof(def.biomes, minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then - return - end - end - local mobdef = minetest.registered_entities[def.name] - if mobdef.can_spawn and not mobdef.can_spawn(p) then return end - minetest.add_entity(p, def.name) - end, - }) -end - diff --git a/mods/MAPGEN/vl_structures/emerge.lua b/mods/MAPGEN/vl_structures/emerge.lua index d81431661..bde17d59c 100644 --- a/mods/MAPGEN/vl_structures/emerge.lua +++ b/mods/MAPGEN/vl_structures/emerge.lua @@ -1,22 +1,14 @@ -local DEFAULT_FLAGS = vl_structures.DEFAULT_FLAGS -local DEFAULT_PREPARE = vl_structures.DEFAULT_PREPARE - local vector_offset = vector.offset local floor = math.floor --- FIXME: switch to vl_structures_logging? -local logging = true or minetest.settings:get_bool("mcl_logging_structures", true) - +local logging = minetest.settings:get_bool("vl_structures_logging", false) local mg_name = minetest.get_mapgen_setting("mg_name") -- parse the prepare parameter local function parse_prepare(prepare) - if prepare == nil or prepare == true then return DEFAULT_PREPARE end + if prepare == nil or prepare == true then return vl_structures.DEFAULT_PREPARE end if prepare == false then return {} end - if prepare.foundation == true then - prepare = table.copy(prepare) - prepare.foundation = DEFAULT_PREPARE.foundation - end + if prepare.foundation == true then prepare.foundation = vl_structures.DEFAULT_PREPARE.foundation end return prepare end @@ -68,17 +60,17 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) local prepare_start = os.clock() -- Get materials from biome (TODO: make this a function + table?): local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)] - local node_top = b and b.node_top and { name = b.node_top } or surface_mat or { name = "mcl_core:dirt_with_grass" } - local node_filler = { name = b and b.node_filler or "mcl_core:dirt" } - local node_stone = { name = b and b.node_stone or "mcl_core:stone" } - local node_dust = b and b.node_dust and { name = b.node_dust } or nil + local node_top = b and b.node_top and { name = b.node_top } or surface_mat or vl_structures.DEFAULT_SURFACE + local node_filler = b and b.node_filler and { name = b.node_filler } or vl_structures.DEFAULT_FILLER + local node_stone = b and b.node_stone and { name = b.node_stone } or vl_structures.DEFAULT_STONE + local node_dust = b and b.node_dust and { name = b.node_dust } or vl_structures.DEFAULT_DUST if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end -- Step 2a: clear overhead area - local corners, padding, depth = prepare.corners or 1, prepare.padding or 1, (type(prepare.foundation) == "number" and prepare.foundation) or -4 + local corners, padding = prepare.corners or 1, prepare.padding or 1 local gp = vector_offset(pmin, -padding, -yoffset, -padding) -- base level if prepare.clear then - local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or DEFAULT_PREPARE.clear_top) + local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or vl_structures.DEFAULT_PREPARE.clear_top) if prepare.clear_bottom == "top" or prepare.clear_bottom == "above" then yoff = size.y + yoffset end --minetest.log("action", "[vl_structures] clearing air "..minetest.pos_to_string(gp)..": ".. (size.x + padding * 2)..","..ymax..","..(size.z + padding * 2)) vl_terraforming.clearance_vm(vm, gp.x, gp.y + yoff, gp.z, @@ -91,7 +83,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) if ddp and ddp.clear then local dsize = vl_structures.size_rotated(ds.size, dr) -- FIXME: rotation of parent local corners, padding, yoffset = ddp.corners or 1, ddp.padding or 1, ddp.yoffset or 0 - local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or DEFAULT_PREPARE.clear_top) + local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or vl_structures.DEFAULT_PREPARE.clear_top) if ddp.clear_bottom == "top" or ddp.clear_bottom == "above" then yoff = dsize.y + yoffset end local gp = vector_offset(pos, dd.pos.x - floor((dsize.x-1)*0.5) - padding, dd.pos.y, @@ -110,7 +102,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) -- Step 2b: baseplate underneath if prepare.foundation then -- minetest.log("action", "[vl_structures] fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name)) - local depth = (type(prepare.foundation) == "number" and prepare.foundation) or DEFAULT_PREPARE.foundation + local depth = (type(prepare.foundation) == "number" and prepare.foundation) or vl_structures.DEFAULT_PREPARE.foundation vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z, size.x + padding * 2, depth, size.z + padding * 2, corners, node_top, node_filler, node_stone, node_dust, pr) @@ -121,7 +113,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) if ddp and ddp.foundation then local dsize = vl_structures.size_rotated(ds.size, dr) -- FIXME: rotation of parent local corners, padding, yoffset = ddp.corners or 1, ddp.padding or 1, ddp.yoffset or 0 - local depth = (type(ddp.foundation) == "number" and ddp.foundation) or DEFAULT_PREPARE.foundation + local depth = (type(ddp.foundation) == "number" and ddp.foundation) or vl_structures.DEFAULT_PREPARE.foundation local gp = vector_offset(pos, dd.pos.x - floor((dsize.x-1)*0.5) - padding, dd.pos.y + (yoffset or 0), dd.pos.z - floor((dsize.z-1)*0.5) - padding) @@ -158,16 +150,16 @@ vl_structures.place_schematic = function(pos, yoffset, schematic, rotation, def, if schematic and not schematic.size then schematic = vl_structures.load_schematic(schematic) end -- legacy local rotation = vl_structures.parse_rotation(rotation, pr) local prepare = parse_prepare(def.prepare) - local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, def.flags or DEFAULT_FLAGS) + local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, def.flags or vl_structures.DEFAULT_FLAGS) -- area to emerge. Add some margin to allow for finding better suitable ground etc. - local tolerance = prepare.tolerance or DEFAULT_PREPARE.tolerance -- may be negative to disable foundations + local tolerance = prepare.tolerance or vl_structures.DEFAULT_PREPARE.tolerance -- may be negative to disable foundations if type(tolerance) ~= "number" then tolerance = 10 end -- extra height for emerge only, min/max/liquid_surface local emin, emax = vector_offset(pmin, 0, -math.max(tolerance, 0), 0), vector.offset(pmax, 0, math.max(tolerance, 0), 0) -- if we need to generate a foundation, we need to emerge a larger area: - if prepare.foundation or prepare.clear then -- these functions need some extra margins. Must match mcl_foundations! + if prepare.foundation or prepare.clear then -- these functions need some extra margins. Must match vl_terraforming! local padding = (prepare.padding or 0) + 3 - local depth = prepare.foundation and ((type(prepare.foundation) == "number" and prepare.foundation or DEFAULT_PREPARE.foundation) - 3) or 0 -- minimum depth - local height = prepare.clear and ((prepare.clear_top or DEFAULT_PREPARE.clear_top)*1.5+0.5*(size.y+yoffset)+2) or 0 -- headroom + local depth = prepare.foundation and ((type(prepare.foundation) == "number" and prepare.foundation or vl_structures.DEFAULT_PREPARE.foundation) - 3) or 0 -- minimum depth + local height = prepare.clear and ((prepare.clear_top or vl_structures.DEFAULT_PREPARE.clear_top)*1.5+0.5*(size.y+yoffset)+2) or 0 -- headroom emin = vector_offset(emin, -padding, depth, -padding) emax = vector_offset(emax, padding, height, padding) end diff --git a/mods/MAPGEN/vl_structures/init.lua b/mods/MAPGEN/vl_structures/init.lua index ec85d3ccb..3b12fb4ab 100644 --- a/mods/MAPGEN/vl_structures/init.lua +++ b/mods/MAPGEN/vl_structures/init.lua @@ -1,13 +1,18 @@ local modname = minetest.get_current_modname() -local S = minetest.get_translator(modname) -local modpath = minetest.get_modpath(modname) - vl_structures = {} +---- Customization parameters for different games -- see vl_terraforming for documentation vl_structures.DEFAULT_PREPARE = { tolerance = 10, foundation = -3, clear = false, clear_bottom = 0, clear_top = 4, padding = 1, corners = 1 } vl_structures.DEFAULT_FLAGS = "place_center_x,place_center_z" +-- fallback types +vl_structures.DEFAULT_SURFACE = { name = "mcl_core:dirt_with_grass" } +vl_structures.DEFAULT_FILLER = { name = "mcl_core:dirt" } +vl_structures.DEFAULT_STONE = { name = "mcl_core:stone" } +vl_structures.DEFAULT_DUST = nil + +local modpath = minetest.get_modpath(modname) dofile(modpath.."/util.lua") dofile(modpath.."/emerge.lua") dofile(modpath.."/api.lua") diff --git a/mods/MAPGEN/vl_structures/mod.conf b/mods/MAPGEN/vl_structures/mod.conf index 7d3b8661a..97fdc1fba 100644 --- a/mods/MAPGEN/vl_structures/mod.conf +++ b/mods/MAPGEN/vl_structures/mod.conf @@ -1,4 +1,5 @@ name = vl_structures author = kno10 -description = Structures API for VoxeLibre and Mineclonia -depends = mcl_init, mcl_util, mcl_loot, vl_terraforming +description = Structure spawning API for VoxeLibre +depends = vl_terraforming +optional_depends = mcl_util, mcl_loot diff --git a/mods/MAPGEN/vl_structures/spawning.lua b/mods/MAPGEN/vl_structures/spawning.lua index 1d40218e8..8ecbdbe0f 100644 --- a/mods/MAPGEN/vl_structures/spawning.lua +++ b/mods/MAPGEN/vl_structures/spawning.lua @@ -1,10 +1,54 @@ --- todo: move this mostly to the mcl_mobs module? +-- TODO: move this to the mcl_mobs module? local mob_cap_player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75 local mob_cap_animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10 local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false) local mg_name = minetest.get_mapgen_setting("mg_name") local vector_offset = vector.offset +-- check if a node is an air node +local function is_air(node) + return node == "air" + -- todo: or: not walkable and not liquid? +end +-- check if a node is a water node +local function is_water(node) + return minetest.get_item_group(node.name, "water") ~= 0 + -- return node.name == "mcl_core:water_source" or node.name ~= "mclx_core:river_water_source" +end + +--- Spawn mobs for a structure +-- @param mob string: mob to spawn +-- @param spawnon string or table: nodes to spawn on +-- @param p1 vector: Lowest coordinates of range +-- @param p2 vector: Highest coordinates of range +-- @param pr PseudoRandom: random generator +-- @param n number: Number of mobs to spawn +-- @param water boolean: Spawn water mobs +function vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water) + n = n or 1 + local sp = {} + if water then + local nn = minetest.find_nodes_in_area(p1,p2,spawnon) + for k,v in pairs(nn) do + if is_water(minetest.get_node(vector_offset(v,0,1,0))) then + table.insert(sp,v) + end + end + else + sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon) + end + table.shuffle(sp) + local count = 0 + local mob_def = minetest.registered_entities[mob] + local enabled = (not peaceful) or (mob_def and mob_spawn_class ~= "hostile") + for _, node in pairs(sp) do + if enabled and count < n and minetest.add_entity(vector_offset(node, 0, 0.5, 0), mob) then + count = count + 1 + end + minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode! + end +end + local structure_spawns = {} --- Structure spawns via ABM -- @param def table: containing @@ -30,12 +74,8 @@ function vl_structures.register_structure_spawn(def) if active_object_count_wider > limit + mob_cap_animal then return end if active_object_count_wider > mob_cap_player then return end local p = vector_offset(pos, 0, 1, 0) - local pname = minetest.get_node(p).name - if def.type_of_spawning == "water" then - if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end - else - if pname ~= "air" then return end -- FIXME: allow everything non-walkable, non-water, non-lava? - end + local pnode = minetest.get_node(p) + if not (def.type_of_spawning == "water" and is_water or is_air)(pnode) then return end if minetest.get_meta(pos):get_string("spawnblock") == "" then return end if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then if table.indexof(def.biomes, minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then @@ -49,36 +89,3 @@ function vl_structures.register_structure_spawn(def) }) end ---- Spawn mobs for a structure --- @param mob string: mob to spawn --- @param spawnon string or table: nodes to spawn on --- @param p1 vector: Lowest coordinates of range --- @param p2 vector: Highest coordinates of range --- @param pr PseudoRandom: random generator --- @param n number: Number of mobs to spawn --- @param water boolean: Spawn water mobs -function vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water) - n = n or 1 - local sp = {} - if water then - local nn = minetest.find_nodes_in_area(p1,p2,spawnon) - for k,v in pairs(nn) do - if minetest.get_item_group(minetest.get_node(vector_offset(v,0,1,0)).name,"water") > 0 then - table.insert(sp,v) - end - end - else - sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon) - end - table.shuffle(sp) - local count = 0 - local mob_def = minetest.registered_entities[mob] - local enabled = (not peaceful) or (mob_def and mob_spawn_class ~= "hostile") - for _, node in pairs(sp) do - if enabled and count < n and minetest.add_entity(vector_offset(node, 0, 0.5, 0), mob) then - count = count + 1 - end - minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode! - end -end - diff --git a/mods/MAPGEN/vl_structures/util.lua b/mods/MAPGEN/vl_structures/util.lua index a4b921946..29d1b69ae 100644 --- a/mods/MAPGEN/vl_structures/util.lua +++ b/mods/MAPGEN/vl_structures/util.lua @@ -83,6 +83,7 @@ end -- @param loot table: Loot table -- @param pr PseudoRandom: random generator function vl_structures.fill_chests(p1,p2,loot,pr) + if not mcl_loot then return end -- optional dependency for it,lt in pairs(loot) do local nodes = minetest.find_nodes_in_area(p1, p2, it) for _,p in pairs(nodes) do diff --git a/mods/MAPGEN/vl_terraforming/API.md b/mods/MAPGEN/vl_terraforming/API.md index 291ae5aa4..ba161e46c 100644 --- a/mods/MAPGEN/vl_terraforming/API.md +++ b/mods/MAPGEN/vl_terraforming/API.md @@ -1,5 +1,6 @@ # vl_terraforming -Terraforming module for VoxeLibre and MineClonia + +Terraforming module built with VoxeLibre and MineClonia in mind, but also useful for other games. This module provides the following key functionalities: @@ -8,7 +9,6 @@ This module provides the following key functionalities: - build a baseplate for a building - clear the area above a building - ## Rounded corners support To get nicer looking baseplates, the code supports rounded corners. @@ -55,7 +55,7 @@ One of these values may be "extreme", and tolerance specifies the maximum height The (rounded) median of these values is used, unless tolerance is set to "min" or "max". -The "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces). +The "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces), "under_water" (solid below water). ## vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) @@ -94,6 +94,6 @@ pr is a PcgRandom random generator - [ ] add an API that works on VM buffers - [ ] add an API version working on the non-VM API -- [ ] benchmark if VM is actually faster than not using VM (5.9 has some optimizations not in VM) +- [ ] benchmark if VM is actually faster than not using VM (5.9 has some optimizations not yet in VM) - [ ] improve tree removal diff --git a/mods/MAPGEN/vl_terraforming/foundation.lua b/mods/MAPGEN/vl_terraforming/foundation.lua index 3769ac147..74e313c03 100644 --- a/mods/MAPGEN/vl_terraforming/foundation.lua +++ b/mods/MAPGEN/vl_terraforming/foundation.lua @@ -102,7 +102,7 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf end -- construct additional baseplate below, also try to make it interesting for yi = py-2,py-20,-1 do - local dy2 = max(0,py-2-yi)^2*0.05 + local dy2 = max(0,py-2-yi)^2*0.10 local active = false for xi = px-1,px+sx do local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 diff --git a/mods/MAPGEN/vl_terraforming/level.lua b/mods/MAPGEN/vl_terraforming/level.lua index aa9d66cb9..074f91294 100644 --- a/mods/MAPGEN/vl_terraforming/level.lua +++ b/mods/MAPGEN/vl_terraforming/level.lua @@ -15,7 +15,7 @@ function vl_terraforming.find_ground_vm(vm, pos) local cur = vm:get_node_at(pos) if cur.name == "ignore" then local e1, e2 = vm:get_emerged_area() - minetest.log("warning","find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + minetest.log("warning", "find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) return nil end @@ -64,7 +64,7 @@ function vl_terraforming.find_under_air_vm(vm, pos) local cur = vm:get_node_at(pos) if cur.name == "ignore" then local e1, e2 = vm:get_emerged_area() - minetest.log("warning","find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + minetest.log("warning", "find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) return nil end @@ -112,7 +112,7 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos) local cur = vm:get_node_at(pos) if cur.name == "ignore" then local e1, e2 = vm:get_emerged_area() - minetest.log("warning","find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + minetest.log("warning", "find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) return nil end @@ -154,6 +154,59 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos) end local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm +--- Find under water surface for a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_under_water_surface_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning", "find_under_water_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_solid_not_tree(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_liquid(cur) then + pos.y = pos.y - 1 + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name)) + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_solid_not_tree(cur) then + if is_liquid(prev) then + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name)) + return pos, cur + else + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil, nil + end + end + end + end +end +local find_under_water_surface_vm = vl_terraforming.find_under_water_surface_vm + --- find suitable height for a structure of this size -- @param vm VoxelManip: to read data -- @param cpos vector: center @@ -164,10 +217,14 @@ local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) local find_ground = find_ground_vm if mode == "liquid_surface" or mode == "liquid" then find_ground = find_liquid_surface_vm end + if mode == "under_water" or mode == "water" then find_ground = find_under_water_surface_vm end if mode == "under_air" then find_ground = find_under_air_vm end -- begin at center, then top-left and clockwise local pos, surface_material = find_ground(vm, cpos) - if not pos then return nil, nil end + if not pos then + -- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default")) + return nil, nil + end local ys = { pos.y } pos.y = pos.y + 1 -- position above surface if size.x == 1 and size.z == 1 then return pos end @@ -200,8 +257,8 @@ function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) end -- well supported base, not too uneven? if #ys < 4 or min(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > tolerance then - --minetest.log("action", "[vl_terraforming] ground too uneven: "..#ys.." positions: "..({dump(ys):gsub("[\n\t ]+", " ")})[1] - -- .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance) + -- minetest.log("action", "[vl_terraforming] ground too uneven: "..#ys.." positions: "..({dump(ys):gsub("[\n\t ]+", " ")})[1] + -- .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance) return nil, nil end cpos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)]) + 1) -- median except for largest, rounded, over surface diff --git a/mods/MAPGEN/vl_terraforming/mod.conf b/mods/MAPGEN/vl_terraforming/mod.conf index fdec41d98..b44840e62 100644 --- a/mods/MAPGEN/vl_terraforming/mod.conf +++ b/mods/MAPGEN/vl_terraforming/mod.conf @@ -1,3 +1,3 @@ name = vl_terraforming author = kno10 -description = Terraforming API for VoxeLibre and Mineclonia +description = Terraforming API diff --git a/settingtypes.txt b/settingtypes.txt index c7df6bff2..1ecb88c3b 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -40,7 +40,7 @@ mcl_doTileDrops (Blocks have drops) bool true mcl_explosions_griefing (Explosions destroy blocks) bool true # Comma separated list of disabled structure names -mcl_disabled_structures (Disabled structures) string +vl_structures_disabled (Disabled structures) string # Comma separated list of disabled event names mcl_disabled_events (Disabled events) string @@ -344,7 +344,7 @@ mcl_logging_mobs_spawn (Log Mob Spawning) bool false mcl_logging_mapgen (Chunk generation logging) bool false # If enabled generated structures will be logged -mcl_logging_structures (Structure generation logging) bool false +vl_structures_logging (Structure generation logging) bool false #Complete debug logging for mcl_signs events. Use this if you have issues with signs. mcl_logging_mcl_signs (Complete debug logging for mcl_signs) bool false