diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 5ea20c707..cce0db664 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -2328,6 +2328,16 @@ mcl_mobs.register_mob("mobs_mc:villager", { end, }) +-- HACK: for compatibility with the new mcl_villages code, but will not allow easy modding yet +mobs_mc.jobsites = {} +for _,p in pairs(professions) do + if p.jobsite then + table.insert(mobs_mc.jobsites, p.jobsite) + end +end +function villager_employ(v, jobsite_pos) + if jobsite_pos then employ(v, jobsite_pos) end +end --[[ Villager spawning in mcl_villages diff --git a/mods/MAPGEN/mcl_villages/API.md b/mods/MAPGEN/mcl_villages/API.md new file mode 100644 index 000000000..c7ac545b8 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/API.md @@ -0,0 +1,301 @@ +# mcl_villages + +When creating buildings or farms for use with this mod, you can prevent paths +from crossing areas by using the `mcl_villages:no_paths` block. You may need to +stack them 2 high to prevent all paths. After the village paths have be laid +this block will be replaced by air. + +## Building Interfaces + +### Parameter + +All of the following functions take a table with the following keys. + +#### Mandatory + +name + +: The name to use for the object. + +mts + +: The path to the mts format schema file. + +#### Optional + +yadjust + +: Y axis adjustment when placing the schema. This can be positive to raise the +placement, or negative to lower it. + + If your schema does not contain a ground layer then set this to 1. + +no_ground_turnip + +: If you don't want the foundation under the building modified, you can disable +the ground turnip by setting this to true. + + Mainly useful for small thing such as lamps, planters, etc. + +no_clearance + +: If you don't want the area around and above the building modified, you can +disable the overground clearance by setting this to true. + + Mainly useful for small thing such as lamps, planters, etc. + +### mcl_villages.register_lamp(table) + +Register a structure to use as a lamp. These will be added to the table used when +adding lamps to paths during village creation. + +### mcl_villages.register_bell(table) + +Register a structure to use as a bell. These will be added to the table used when +adding the bell during village creation. + +There is 1 bell per village. + +### mcl_villages.register_well(table) + +Register a structure to use as a well. These will be added to the table used when +adding the wells during village creation. + +The number of wells is calculated randomly based on the number of beds in the +village. Every 10 beds add 1 to the maximum number. + +e.g. 8 beds == 1 well, 15 beds == 1 or 2 wells, 22 beds == 1 to 3 wells, etc. + +### mcl_villages.register_building(table) + +Register a building used for jobs, houses, or other uses. + +The schema is parsed to work out how many jobs and beds are in it. + +If you are adding a job site for a custom profession then ensure you call +```mobs_mc.register_villager_profession``` before you register a building using it. + +If a building doesn't have any job sites or beds then it may get added during +the house placement phase. This will simply add another building to +the village and will not affect the number of jobs or beds. + +#### Additional options + +The ```mcl_villages.register_building``` call accepts the following optional +parameters in the table. + +min_jobs + +: A village will need at least this many jobs to have one of these buildings. + + This is used to restrict buildings to bigger villages. + +max_jobs + +: A village will need less that or equal to (<=) this many jobs to have one of +these buildings. + + This is used to restrict buildings to smaller villages. + +num_others + +: A village will need this many other job sites before you can have another of +these jobs sites. + + This is used to influence the ratio of buildings in a village. + +is_mandatory + +: This ensures that each village will have at least one of these buildings. + +### mobs_mc.register_villager_profession(title, table) + +**TODO** this should be somewhere else. + +This API call allows you to register professions for villagers. + +It takes 2 arguments. + +1. title - The title to use for the profession. + + This mus be unique; the profession will be rejected if this title is already + used. + +1. Record - a table containing the details of the profession, it contains the + following fields. + + 1. name: The name displayed for the profession in the UI. + 1. texture: The texture to use for the profession + 1. jobsite: the node or group name sued to flag blocks as job sites for this + profession + 1. trades: a table containing trades with 1 entry for each trade level. + +You can access the current profession and job site data in +```mobs_mc.professions``` and ```mobs_mc.jobsites```. + +### mcl_villages.register_on_village_placed(func) + +This function allows registering functions to be called after a village is +laid out. + +Note that the village may not be completed as the building post processing is +non-deterministic to avoid overloading the server. + +`settlement_info` is a table containing data for all the buildings in the +village. The bell is always the first entry in the table. + +`blockseed` is the block seed for the chunk the village was generated for. +Villages can extend outside of this chunk. + +```lua +local function my_village_hook(settlement_info, blockseed) + minetest.log("The village has " .. #settlement_info .. " buildings in it!") +end + +mcl_villages.register_on_village_placed(my_village_hook) +``` + +### mcl_villages.register_on_villager_spawned(func) + +This function allows registering functions to be called after a villager is +placed as part of village generation. + +`villager_ent` is the entity created by `minetest.add_entity`. + +`blockseed` is the block seed for the chunk the village was generated for. +Villages can extend outside of this chunk. + +```lua +local function my_villager_hook(villager_ent, blockseed) + local l = villager_ent:get_luaentity() + minetest.log("The villager's id is " .. l._id) +end + +mcl_villages.register_on_villager_spawned(my_villager_hook) +``` + +## Farm Interface + +These functions aid creating crops for use use in farms placed during village +generation. + +### mcl_villages.get_crop_types() + +This allows checking what crop types are supported. + +Currently they are: grain, root, gourd, flower, bush, tree. + +Placement of gourds should take in to consideration the way they fruit. + +### mcl_villages.get_crops() + +Returns a table containing all registered crops. + +### mcl_villages.get_weighted_crop(biome, crop_type, pr) + +Gets a random crop for the biome and crop type. + +### mcl_villages.register_crop(crop_def) + +Registers a crop for use on farms. + +crop_def is a table with the following fields: + +* `node` the name of the crop node to place. e.g. `mcl_farming:wheat_1`. +* `crop_type` the type crop. e.g. `grain` +* `biomes` a table containing the weighting to give the crop. + * Supported biome values are: + * acacia + * bamboo + * desert + * jungle + * plains + * savanna + * spruce + * If you leave a biome out ot he definition then the crop will not be available in that biome. +e.g. + +```lua +mcl_villages.register_crop({ + type = "grain", + node = "mcl_farming:wheat_1", + biomes = { + acacia = 10, + bamboo = 10, + desert = 10, + jungle = 10, + plains = 10, + savanna = 10, + spruce = 10, + }, +}) +``` + +### Creating farms with replaceable crops + +To create a farm that will utilize registered crops you follow the basic process +for creating a farm, but you leave out the crops. + +Once you have your farm constructed then instead of placing crops you place blocks named `mcl_villages:crop_*` over the dirt in the farm. + +Each crop type has 8 blocks that can be used for it. This allows, but does not +guarantee, variety of crops in a farm. + +Each of the crop tiles has an image of a entity that it represents. This image +is representative, not explicit. + +i.e. The root crop tiles have an image of a carrot on them, but they will be +swapped for a random root crop, not always carrots. + +Each specific node will be replaced by a single item. + +e.g. if you use `mcl_villages:crop_root_1` and `mcl_villages:crop_root_2` in your farm then all there will be at most 2 types of root crops on the farm. + +It is random, so both types may get replaced by the same crop. + +Remember that gourds affect 5 nodes when they crop; a good farmer won't plant +anything on the 4 nodes a fruit wil form and your farm should not do that +either. + +Once you have saved the schema for your farm you register it with the building interface. + +e.g. + +```lua +mcl_villages.register_building({ + name = "my_farm", + mts = schem_path .. "/my_farm.mts", + num_others = 3, +}) +``` + +When a village is generated there will be a chance your farm will be placed, any +crop blocks will be replaced by biome appropriate crops. + +If a crop cannot be found for a crop type in a biome, then a default will be +used. This ensure all farming blocks are full, ven if it's al the same crop. + +The default is wheat. + +## Village Layout + +There are two methods for layout out villages, circle layout is more likely to be +used for small villages and grid for large villages. + +The circle layout uses circles (surprise) to calculate if buildings overlap. It +creates fairly widely spaced layouts. + +The grid layout uses a predetermined grid layout to positions buildings and uses +AreaStore to adjust building position if there are collisions. + +The predetermined grid is below, position 0 is the bell, the other numbers are the order of placement. + +|||||||| +| -- | -- | -- | -- | -- | -- | -- | +|48|41|33|25|29|37|45| +|40|17|13| 9|11|15|43| +|32|19| 5| 1| 3|22|35| +|28|23| 7| 0| 8|24|27| +|36|21| 4| 2| 6|20|31| +|44|16|12|10|14|18|39| +|46|38|30|26|34|42|47| diff --git a/mods/MAPGEN/mcl_villages/README.txt b/mods/MAPGEN/mcl_villages/README.txt index 4a4927051..194d7f73f 100644 --- a/mods/MAPGEN/mcl_villages/README.txt +++ b/mods/MAPGEN/mcl_villages/README.txt @@ -21,7 +21,6 @@ Basic conversion of Settlements mod for compatibility with VoxeLibre, plus new s Seed-based Village Generation, multi-threading, bugfixes: kay27 - ========================= version: 0.1 alpha @@ -43,3 +42,5 @@ This mod is based on "ruins" by BlockMen Completely new schematics for VoxeLibre: MysticTempest - CC-BY-SA 4.0 + +New schematics and improvements in mineclonia by codiac. diff --git a/mods/MAPGEN/mcl_villages/api.lua b/mods/MAPGEN/mcl_villages/api.lua new file mode 100644 index 000000000..3648c1269 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/api.lua @@ -0,0 +1,240 @@ +mcl_villages.schematic_houses = {} +mcl_villages.schematic_jobs = {} +mcl_villages.schematic_lamps = {} +mcl_villages.schematic_bells = {} +mcl_villages.schematic_wells = {} +mcl_villages.on_village_placed = {} +mcl_villages.on_villager_placed = {} +mcl_villages.mandatory_buildings = {} +mcl_villages.forced_blocks = {} + +local S = minetest.get_translator(minetest.get_current_modname()) + +local function job_count(schem_lua) + -- Local copy so we don't trash the schema for other uses, because apparently + -- there isn't a non-destructive way to count occurrences of a string :( + local str = schem_lua + local count = 0 + + for _, n in pairs(mobs_mc.jobsites) do + if string.find(n, "^group:") then + if n == "group:cauldron" then + count = count + select(2, string.gsub(str, '"mcl_cauldrons:cauldron', "")) + else + local name = string.sub(n, 6, -1) + local num = select(2, string.gsub(str, name, "")) + if num then + minetest.log( + "info", + string.format("[mcl_villages] Guessing how to handle %s counting it as %d job sites", name, num) + ) + count = count + num + else + minetest.log( + "warning", + string.format("[mcl_villages] Don't know how to handle group %s counting it as 1 job site", n) + ) + count = count + 1 + end + end + else + count = count + select(2, string.gsub(str, '{name="' .. n .. '"', "")) + end + end + + return count +end + +local function load_schema(name, mts) + local schem_lua = minetest.serialize_schematic(mts, "lua", { lua_use_comments = false, lua_num_indent_spaces = 0 }) + .. " return schematic" + -- MCLA node names to VL for import + for _, sub in pairs(mcl_villages.mcla_to_vl) do + schem_lua = schem_lua:gsub(sub[1], sub[2]) + end + + local schematic = loadstring(schem_lua)() + return { + name = name, + size = schematic.size, + schem_lua = schem_lua, + } +end + +local all_optional = { "yadjust", "no_ground_turnip", "no_clearance" } + +local function set_all_optional(record, data) + for _, field in ipairs(all_optional) do + if record[field] then + data[field] = record[field] + end + end +end + +local function set_mandatory(record, type) + if record['is_mandatory'] then + if not mcl_villages.mandatory_buildings[type] then + mcl_villages.mandatory_buildings[type] = {} + end + + table.insert(mcl_villages.mandatory_buildings[type], record["name"]) + end +end + +function mcl_villages.register_lamp(record) + local data = load_schema(record["name"], record["mts"]) + set_all_optional(record, data) + table.insert(mcl_villages.schematic_lamps, data) + set_mandatory(record, 'lamps') +end + +function mcl_villages.register_bell(record) + local data = load_schema(record["name"], record["mts"]) + set_all_optional(record, data) + table.insert(mcl_villages.schematic_bells, data) + set_mandatory(record, 'bells') +end + +function mcl_villages.register_well(record) + local data = load_schema(record["name"], record["mts"]) + set_all_optional(record, data) + table.insert(mcl_villages.schematic_wells, data) + set_mandatory(record, 'wells') +end + +local optional_fields = { "min_jobs", "max_jobs", "num_others", "is_mandatory" } + +function mcl_villages.register_building(record) + local data = load_schema(record["name"], record["mts"]) + + set_all_optional(record, data) + + for _, field in ipairs(optional_fields) do + if record[field] then + data[field] = record[field] + end + end + + -- Local copy so we don't trash the schema for other uses + local str = data["schem_lua"] + local num_beds = select(2, string.gsub(str, '"mcl_beds:bed_[^"]+_bottom"', "")) + + if num_beds > 0 then + data["num_beds"] = num_beds + end + + local job_count = job_count(data["schem_lua"]) + + if job_count > 0 then + data["num_jobs"] = job_count + table.insert(mcl_villages.schematic_jobs, data) + set_mandatory(record, 'jobs') + else + table.insert(mcl_villages.schematic_houses, data) + set_mandatory(record, 'houses') + end +end + +local supported_crop_types = { + "grain", + "root", + "gourd", + "bush", + "tree", + "flower", +} + +local crop_list = {} + +function mcl_villages.default_crop() + return "mcl_farming:wheat_1" +end + +local weighted_crops = {} + +local function adjust_weights(biome, crop_type) + if weighted_crops[biome] == nil then + weighted_crops[biome] = {} + end + + weighted_crops[biome][crop_type] = {} + + local factor = 100 / crop_list[biome][crop_type]["total_weight"] + local total = 0 + + for node, weight in pairs(crop_list[biome][crop_type]) do + if node ~= "total_weight" then + total = total + (math.round(weight * factor)) + table.insert(weighted_crops[biome][crop_type], { total = total, node = node }) + end + end + + table.sort(weighted_crops[biome][crop_type], function(a, b) + return a.total < b.total + end) +end + +function mcl_villages.get_crop_types() + return table.copy(supported_crop_types) +end + +function mcl_villages.get_crops() + return table.copy(crop_list) +end + +function mcl_villages.get_weighted_crop(biome, crop_type, pr) + if weighted_crops[biome] == nil then + biome = "plains" + end + + if weighted_crops[biome][crop_type] == nil then + return + end + + local rand = pr:next(1, 99) + + for i, rec in ipairs(weighted_crops[biome][crop_type]) do + local weight = rec.total + local node = rec.node + + if rand <= weight then + return node + end + end + + return +end + +function mcl_villages.register_crop(crop_def) + + local node = crop_def.node + local crop_type = crop_def.type + + if table.indexof(supported_crop_types, crop_type) == -1 then + minetest.log("warning", S("Crop type @1 is not supported", crop_type)) + return + end + + for biome, weight in pairs(crop_def.biomes) do + + if crop_list[biome] == nil then + crop_list[biome] = {} + end + + if crop_list[biome][crop_type] == nil then + crop_list[biome][crop_type] = { total_weight = 0 } + end + + crop_list[biome][crop_type][node] = weight + crop_list[biome][crop_type]["total_weight"] = crop_list[biome][crop_type]["total_weight"] + weight + adjust_weights(biome, crop_type) + end +end + +function mcl_villages.register_on_village_placed(func) + table.insert(mcl_villages.on_village_placed, func) +end + +function mcl_villages.register_on_villager_spawned(func) + table.insert(mcl_villages.on_villager_placed, func) +end diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua index 3ee54d4c0..030c3a7c9 100644 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -1,128 +1,29 @@ +local min_jobs = tonumber(minetest.settings:get("mcl_villages_min_jobs")) or 1 +local max_jobs = tonumber(minetest.settings:get("mcl_villages_max_jobs")) or 12 +local placement_priority = minetest.settings:get("mcl_villages_placement_priority") or "random" + +local S = minetest.get_translator(minetest.get_current_modname()) + ------------------------------------------------------------------------------- -- initialize settlement_info ------------------------------------------------------------------------------- function mcl_villages.initialize_settlement_info(pr) - local count_buildings = {} + local count_buildings = { + number_of_jobs = pr:next(min_jobs, max_jobs), + num_jobs = 0, + num_beds = 0, + } - -- count_buildings table reset - for k,v in pairs(mcl_villages.schematic_table) do + for k, v in pairs(mcl_villages.schematic_houses) do + count_buildings[v["name"]] = 0 + end + for k, v in pairs(mcl_villages.schematic_jobs) do count_buildings[v["name"]] = 0 end - -- randomize number of buildings - local number_of_buildings = pr:next(10, 25) - local number_built = 0 - mcl_villages.debug("Village ".. number_of_buildings) - - return count_buildings, number_of_buildings, number_built + return count_buildings end -------------------------------------------------------------------------------- --- check ground for a single building -------------------------------------------------------------------------------- -local function try_place_building(minp, maxp, pos_surface, building_all_info, rotation, settlement_info, pr) - local fwidth, fdepth = building_all_info["hwidth"] or 5, building_all_info["hdepth"] or 5 - if rotation == "90" or rotation == "270" then fwidth, fdepth = fdepth, fwidth end - local fheight = building_all_info["hheight"] or 5 - -- use building centers for better placement - pos_surface.x = pos_surface.x - math.ceil(fwidth / 2) - pos_surface.z = pos_surface.z - math.ceil(fdepth / 2) - -- ensure we have 3 space for terraforming - if pos_surface.x - 3 < minp.x or pos_surface.z + 3 < minp.z or pos_surface.x + fwidth + 3 > maxp.x or pos_surface.z + fheight + 3 > maxp.z then return nil end - -- to find the y position, also check the corners - local ys = {pos_surface.y} - local pos_c - pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x, pos_surface.y+fheight, pos_surface.z)) - if pos_c then table.insert(ys, pos_c.y) end - pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x+fwidth-1, pos_surface.y+fheight, pos_surface.z)) - if pos_c then table.insert(ys, pos_c.y) end - pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x, pos_surface.y+fheight, pos_surface.z+fdepth-1)) - if pos_c then table.insert(ys, pos_c.y) end - pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x+fwidth-1, pos_surface.y+fheight, pos_surface.z+fdepth-1)) - if pos_c then table.insert(ys, pos_c.y) end - table.sort(ys) - -- well supported base, not too uneven? - if #ys < 5 or ys[#ys]-ys[1] > fheight + 3 then return nil end - pos_surface.y = 0.5 * (ys[math.floor(#ys/2)] + ys[math.ceil(#ys/2)]) -- median - -- check distance to other buildings - if not mcl_villages.check_distance(settlement_info, pos_surface, math.max(fheight, fdepth)) then return nil end - return pos_surface -end -------------------------------------------------------------------------------- --- fill settlement_info --------------------------------------------------------------------------------- -function mcl_villages.create_site_plan(minp, maxp, pr) - local center = vector.new(math.floor((minp.x+maxp.x)/2),maxp.y,math.floor((minp.z+maxp.z)/2)) - minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(center)) - local possible_rotations = {"0", "90", "180", "270"} - local center_surface - - local count_buildings, number_of_buildings, number_built = mcl_villages.initialize_settlement_info(pr) - local settlement_info = {} - -- now some buildings around in a circle, radius = size of town center - local x, y, z, r = center.x, maxp.y, center.z, 0 - -- draw j circles around center and increase radius by math.random(2,5) - for j = 1,15 do - for a = 0, 23, 1 do - local angle = a * 71 / 24 * math.pi * 2 -- prime to increase randomness - local pos1 = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5)) - local pos_surface, surface_material = mcl_villages.find_surface(pos1, false) - if pos_surface then - local randomized_schematic_table = mcl_villages.shuffle(mcl_villages.schematic_table, pr) - if #settlement_info == 0 then randomized_schematic_table = { mcl_villages.schematic_table[1] } end -- place town bell first - -- pick schematic - local size = #randomized_schematic_table - for i = 1, #randomized_schematic_table do - local building_all_info = randomized_schematic_table[i] - -- already enough buildings of that type? - if count_buildings[building_all_info["name"]] < building_all_info["max_num"]*number_of_buildings - and (r >= 25 or not string.find(building_all_info["name"], "lamp")) then -- no lamps in the center - local rotation = possible_rotations[pr:next(1, #possible_rotations)] - local pos = try_place_building(minp, maxp, pos_surface, building_all_info, rotation, settlement_info, pr) - if pos then - if #settlement_info == 0 then -- town bell - center_surface, y = pos, pos.y + max_height_difference * 0.5 + 1 - end - -- limit height differences to town center - if math.abs(pos.y - center_surface.y) > max_height_difference * 0.5 then - break -- other buildings likely will not fit either - end - count_buildings[building_all_info["name"]] = (count_buildings[building_all_info["name"]] or 0) + 1 - number_built = number_built + 1 - - pos.y = pos.y + (building_all_info["yadjust"] or 0) - table.insert(settlement_info, { - pos = pos, - name = building_all_info["name"], - hsize = math.max(building_all_info["hwidth"], building_all_info["hdepth"]), -- ,building_all_info["hsize"], - rotat = rotation, - surface_mat = surface_material - }) - -- minetest.log("action", "[mcl_villages] Placing "..building_all_info["name"].." at "..minetest.pos_to_string(pos)) - break - end - end - end - if number_of_buildings == number_built then - break - end - end - if r == 0 then break end -- no angles in the very center - end - if number_built >= number_of_buildings then - break - end - r = r + pr:next(2,5) - end - mcl_villages.debug("really ".. number_built) - if number_built < 7 then - minetest.log("action", "[mcl_villages] Bad village location, could only place "..number_built.." buildings.") - return - end - minetest.log("action", "[mcl_villages] village plan completed at " .. minetest.pos_to_string(center)) - --minetest.log("[mcl_villages] village plan completed at " .. minetest.pos_to_string(center)) -- for debugging only - return settlement_info -end ------------------------------------------------------------------------------- -- evaluate settlement_info and place schematics ------------------------------------------------------------------------------- @@ -139,130 +40,401 @@ local function construct_node(p1, p2, name) return nodes end -local function spawn_iron_golem(pos) - --minetest.log("action", "[mcl_villages] Attempt to spawn iron golem.") - local p = minetest.find_node_near(pos,50,"mcl_core:grass_path") - if p then - p.y = p.y + 1 - local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity() - if l then - l._home = p +local function spawn_cats(pos) + local sp=minetest.find_nodes_in_area_under_air(vector.offset(pos,-20,-20,-20),vector.offset(pos,20,20,20),{"group:opaque"}) + for i=1,math.random(3) do + local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat"):get_luaentity() + if v then + v._home = pos end end end -local function spawn_villagers(minp,maxp) - --minetest.log("action", "[mcl_villages] Attempt to spawn villagers.") - local beds=minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20),{"mcl_beds:bed_red_bottom"}) - for _,bed in pairs(beds) do - local m = minetest.get_meta(bed) - if m:get_string("villager") == "" then - local v=minetest.add_entity(bed,"mobs_mc:villager") - if v then - local l=v:get_luaentity() - l._bed = bed - m:set_string("villager",l._id) - end - end - end -end +local function init_nodes(p1, p2, pr) + --[[for _, n in pairs(minetest.find_nodes_in_area(p1, p2, { "group:wall" })) do + mcl_walls.update_wall(n) + end]]-- -local function fix_village_water(minp,maxp) - local palettenodes = minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20), "group:water_palette") - for _, palettenodepos in pairs(palettenodes) do - local palettenode = minetest.get_node(palettenodepos) - minetest.set_node(palettenodepos, {name = palettenode.name}) - end -end - -local function init_nodes(p1, p2, size, rotation, pr) construct_node(p1, p2, "mcl_itemframes:item_frame") + construct_node(p1, p2, "mcl_itemframes:glow_item_frame") construct_node(p1, p2, "mcl_furnaces:furnace") construct_node(p1, p2, "mcl_anvils:anvil") - construct_node(p1, p2, "mcl_smoker:smoker") - construct_node(p1, p2, "mcl_barrels:barrel_closed") - construct_node(p1, p2, "mcl_blast_furnace:blast_furnace") - construct_node(p1, p2, "mcl_brewing:stand_000") - local nodes = construct_node(p1, p2, "mcl_chests:chest") - if nodes and #nodes > 0 then - for p=1, #nodes do - local pos = nodes[p] - mcl_villages.fill_chest(pos, pr) - end + construct_node(p1, p2, "mcl_books:bookshelf") + construct_node(p1, p2, "mcl_armor_stand:armor_stand") + --construct_node(p1, p2, "mcl_smoker:smoker") + --construct_node(p1, p2, "mcl_barrels:barrel_closed") + --construct_node(p1, p2, "mcl_blast_furnace:blast_furnace") + --construct_node(p1, p2, "mcl_brewing:stand_000") + + -- Support mods with custom job sites + local job_sites = minetest.find_nodes_in_area(p1, p2, mobs_mc.jobsites) + for _, v in pairs(job_sites) do + mcl_structures.init_node_construct(v) end + + -- Do new chest nodes first local nodes = construct_node(p1, p2, "mcl_chests:chest_small") if nodes and #nodes > 0 then for p=1, #nodes do - local pos = nodes[p] - mcl_villages.fill_chest(pos, pr) + mcl_villages.fill_chest(nodes[p], pr) + end + end + + -- Do old chest nodes after + local nodes = construct_node(p1, p2, "mcl_chests:chest") + if nodes and #nodes > 0 then + for p=1, #nodes do + mcl_villages.fill_chest(nodes[p], pr) end end end -function mcl_villages.place_schematics(settlement_info, pr) - local building_all_info - local lvm = VoxelManip() +-- check ground for a single building, adjust position +local function check_ground(lvm, cpos, size) + local cpos, surface_material = mcl_villages.find_surface(lvm, cpos) + if not cpos then return nil, nil end + local pos = vector.offset(cpos, -math.floor(size.x/2), 0, -math.floor(size.z/2)) + local ys = {pos.y} + local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, 0)) + if pos_c then table.insert(ys, pos_c.y) end + local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, 0)) + if pos_c then table.insert(ys, pos_c.y) end + local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, size.z-1)) + if pos_c then table.insert(ys, pos_c.y) end + local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, size.z-1)) + if pos_c then table.insert(ys, pos_c.y) end + table.sort(ys) + -- well supported base, not too uneven? + if #ys < 5 or ys[#ys]-ys[1] > 6 then return nil, nil end + cpos.y = math.floor(0.5 * (ys[math.floor(#ys/2)] + ys[math.ceil(#ys/2)]) + 0.5) -- median, rounded + return cpos, surface_material +end - for i, built_house in ipairs(settlement_info) do - local is_last = i == #settlement_info +local function add_building(settlement, building, count_buildings) + table.insert(settlement, building) + count_buildings[building["name"]] = count_buildings[building["name"]] + 1 + count_buildings.num_jobs = count_buildings.num_jobs + (building["num_jobs"] or 0) + count_buildings.num_beds = count_buildings.num_beds + (building["num_beds"] or 0) +end - for j, schem in ipairs(mcl_villages.schematic_table) do - if settlement_info[i]["name"] == schem["name"] then - building_all_info = schem - break +local function layout_town(lvm, minp, maxp, pr, input_settlement) + local center = vector.new(pr:next(minp.x + 24, maxp.x - 24), maxp.y, pr:next(minp.z + 24, maxp.z - 24)) + minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(center)) + local possible_rotations = {"0", "90", "180", "270"} + local center_surface + + local settlement = {} + -- now some buildings around in a circle, radius = size of town center + local x, y, z, r, lastr = center.x, maxp.y, center.z, 0, 99 + local mindist = 4 + if #input_settlement >= 12 then mindist = 3 end + -- draw j circles around center and increase radius by math.random(2,4) + for j = 1,20 do + local steps = math.min(math.floor(math.pi * 2 * r / 2), 30) -- try up to 30 angles + for a = 0, steps - 1 do + if #settlement == #input_settlement then break end -- everything placed + local angle = a * 71 / steps * math.pi * 2 -- prime to increase randomness + local cpos = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5)) + local building = table.copy(input_settlement[#settlement + 1]) + local size = vector.copy(building.size) + --local rotation = possible_rotations[pr:next(1, #possible_rotations)] + local rotation = math.floor(math.atan2(center.x-cpos.x, center.z-cpos.z) / math.pi * 2+4.5)%4 + local rotation = possible_rotations[1+rotation] + if rotation == "90" or rotation == "270" then size.x, size.z = size.z, size.x end + local tlpos = vector.offset(cpos, -math.floor(size.x / 2), 0, -math.floor(size.z / 2)) + + -- ensure we have 3 space for terraforming, and avoid problems with VoxelManip + if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x + and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then + local pos, surface_material = check_ground(lvm, cpos, size) + -- check distance to other buildings. Note that we still want to add baseplates etc. + if pos and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then + -- use town bell as new reference point for placement height + if #settlement == 0 then + center_surface, y = cpos, pos.y + mcl_villages.max_height_difference * 0.5 + 1 + end + -- limit height differences to town center, but gradually + if math.abs(pos.y - center_surface.y) <= mcl_villages.max_height_difference * (0.3 + math.min(r/30,0.5)) then + local minp = vector.offset(pos, -math.floor(size.x/2), building.yadjust, -math.floor(size.z/2)) + building.minp = minp + building.maxp = vector.offset(minp, size.x, size.y, size.z) + building.pos = pos + building.size = size + building.rotation = rotation + building.surface_mat = surface_material + table.insert(settlement, building) + -- minetest.log("verbose", "[mcl_villages] Placing "..schema["name"].." at "..minetest.pos_to_string(pos)) + lastr = r + --else + -- minetest.log("Height difference "..math.abs(pos.y - center_surface.y)) + end + end end end - - local pos = settlement_info[i]["pos"] - local rotation = settlement_info[i]["rotat"] - -- get building node material for better integration to surrounding - local surface_material = settlement_info[i]["surface_mat"] or "mcl_core:stone" - local platform_material = surface_material - local schem_lua = building_all_info["schem_lua"] - if not schem_lua then - schem_lua = minetest.serialize_schematic(building_all_info["mts"], "lua", { lua_use_comments = false, lua_num_indent_spaces = 0 }) .. " return schematic" - -- MCLA node names to VL for import - for _, sub in pairs(mcl_villages.mcla_to_vl) do - schem_lua = schem_lua:gsub(sub[1], sub[2]) - end - if not schem_lua then error("schema failed to load "..building_all_info["name"]) end - local schematic = loadstring(schem_lua) - if not schematic then error("schema failed to load "..building_all_info["name"].." "..schem_lua) end - schematic = schematic() -- evaluate - if schematic.size["x"] ~= building_all_info["hwidth"] or schematic.size["y"] ~= building_all_info["hheight"] or schematic.size["z"] ~= building_all_info["hdepth"] then - minetest.log("warning", "[mcl_villages] schematic size differs: "..building_all_info["name"].." width "..schematic.size["x"].." height "..schematic.size["y"].." depth "..schematic.size["z"]) - end - building_all_info["schem_lua"] = schem_lua + r = r + pr:next(2,4) + if r > lastr + 25 then -- too disconnected + break end - schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material..'"') - schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material..'"') - local schematic = loadstring(mcl_villages.substitute_materials(pos, schem_lua, pr))() + end + -- minetest.log("verbose", "Planned "..#input_settlement.." buildings, placed "..#settlement) + if #settlement < #input_settlement and #settlement < 6 then + minetest.log("action", "[mcl_villages] Bad village location, could only place "..#settlement.." buildings.") + return + end + minetest.log("action", "[mcl_villages] village plan completed at " .. minetest.pos_to_string(center)) + return settlement +end - -- already built the foundation for the building and made room above - local sx, sy, sz = schematic.size.x, schematic.size.y, schematic.size.z - if rotation == "90" or rotation == "270" then sx, sz = sz, sx end - local p2 = vector.new(pos.x+sx-1,pos.y+sy-1,pos.z+sz-1) - lvm:read_from_map(vector.new(pos.x-3, pos.y-40, pos.z-3), vector.new(pos.x+sx+3, pos.y+sy+40, pos.z+sz+3)) -- safety margins for foundation - lvm:get_data() - -- TODO: make configurable as in MCLA - mcl_villages.foundation(lvm, pos, sx, sy, sz, surface_material, pr) +function mcl_villages.create_site_plan(lvm, minp, maxp, pr) + local settlement = {} + + -- initialize all settlement_info table + local count_buildings = mcl_villages.initialize_settlement_info(pr) + + -- first building is townhall in the center + local bindex = pr:next(1, #mcl_villages.schematic_bells) + local bell_info = table.copy(mcl_villages.schematic_bells[bindex]) + + if mcl_villages.mandatory_buildings['jobs'] then + for _, bld_name in pairs(mcl_villages.mandatory_buildings['jobs']) do + local building_info = info_for_building(bld_name, mcl_villages.schematic_jobs) + add_building(settlement, building_info, count_buildings) + end + end + + while count_buildings.num_jobs < count_buildings.number_of_jobs do + local rindex = pr:next(1, #mcl_villages.schematic_jobs) + local building_info = mcl_villages.schematic_jobs[rindex] + + if + (building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"]) + and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"]) + and ( + building_info["num_others"] == nil + or count_buildings[building_info["name"]] == 0 + or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs + ) + then + add_building(settlement, building_info, count_buildings) + end + end + + if mcl_villages.mandatory_buildings['houses'] then + for _, bld_name in pairs(mcl_villages.mandatory_buildings['houses']) do + local building_info = info_for_building(bld_name, mcl_villages.schematic_houses) + add_building(settlement, building_info, count_buildings) + end + end + + while count_buildings.num_beds <= count_buildings.num_jobs do + local rindex = pr:next(1, #mcl_villages.schematic_houses) + local building_info = mcl_villages.schematic_houses[rindex] + + if + (building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"]) + and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"]) + and ( + building_info["num_others"] == nil + or count_buildings[building_info["name"]] == 0 + or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs + ) + then + add_building(settlement, building_info, count_buildings) + end + end + + -- Based on number of villagers + local num_wells = pr:next(1, math.ceil(count_buildings.num_beds / 10)) + for i = 1, num_wells do + local windex = pr:next(1, #mcl_villages.schematic_wells) + local cur_schem = table.copy(mcl_villages.schematic_wells[windex]) + table.insert(settlement, pr:next(1, #settlement), cur_schem) + end + + if placement_priority == "jobs" then + -- keep ordered as is + elseif placement_priority == "houses" then + table.reverse(settlement) + else + settlement = mcl_villages.shuffle(settlement, pr) + end + + table.insert(settlement, 1, bell_info) + return layout_town(lvm, minp, maxp, pr, settlement) +end + + +function mcl_villages.place_schematics(lvm, settlement, blockseed, pr) + -- local lvm = VoxelManip() + local bell_pos = vector.offset(settlement[1].minp, math.floor(settlement[1].size.x/2), 0, math.floor(settlement[1].size.z/2)) + local bell_center_pos + local bell_center_node_type + + for i, building in ipairs(settlement) do + local minp, cpos, maxp, size, rotation = building.minp, building.pos, building.maxp, building.size, building.rotation + + -- adjust the schema to match location and biome + local surface_material = building.surface_mat or {name = "mcl_core:dirt" } + local platform_material = building.platform_mat or building.surface_mat or {name = "mcl_core:stone" } + local schem_lua = building.schem_lua + schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material.name..'"') -- also keeping param2 would be nicer, grass color + schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material.name..'"') + schem_lua = mcl_villages.substitute_materials(cpos, schem_lua, pr) + local schematic = loadstring(schem_lua)() + + -- the foundation and air space for the building was already built before + -- lvm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z)) + -- lvm:get_data() + -- now added in placement code already, pos has the primary height if (building.yadjust or 0) ~= 0 then minp = vector.offset(minp, 0, building.yadjust, 0) end + -- minetest.log("debug", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material) minetest.place_schematic_on_vmanip( lvm, - pos, + minp, schematic, rotation, nil, true, { place_center_x = false, place_center_y = false, place_center_z = false } ) - lvm:write_to_map(true) -- FIXME: postpone - init_nodes(pos, p2, schematic.size, rotation, pr) - - if building_all_info["name"] == "belltower" then - spawn_iron_golem(pos) + -- to help pathing, increase the height of no_path areas + local p = vector.zero() + for z = minp.z,maxp.z do + p.z = z + for x = minp.x,maxp.x do + p.x = x + for y = minp.y,maxp.y-1 do + p.y = y + local n = lvm:get_node_at(p) + if n and n.name == "mcl_villages:no_paths" then + p.y = y+1 + n = lvm:get_node_at(p) + if n and n.name == "air" then + lvm:set_node_at(p, {name="mcl_villages:no_paths"}) + end + end + end + end + end + mcl_villages.store_path_ends(lvm, minp, maxp, cpos, blockseed, bell_pos) + + if building.name == "belltower" then -- TODO: allow multiple types? + bell_center_pos = cpos + local center_node = lvm:get_node_at(cpos) + bell_center_node_type = center_node.name + end + end + + lvm:write_to_map(true) -- for path finder and light + + local biome_data = minetest.get_biome_data(bell_pos) + local biome_name = minetest.get_biome_name(biome_data.biome) + mcl_villages.paths(blockseed, biome_name) + + -- this will run delayed actions, such as spawning mobs + minetest.set_node(bell_center_pos, { name = "mcl_villages:village_block" }) + local meta = minetest.get_meta(bell_center_pos) + meta:set_string("blockseed", blockseed) + meta:set_string("node_type", bell_center_node_type) + meta:set_string("infotext", S("The timer for this @1 has not run yet!", bell_center_node_type)) + minetest.get_node_timer(bell_center_pos):start(1.0) + + for i, building in ipairs(settlement) do + init_nodes(vector.offset(building.minp,-2,-2,-2), vector.offset(building.maxp,2,2,2), pr) + end + + -- read back any changes + local emin, emax = lvm:get_emerged_area() + lvm:read_from_map(emin, emax) +end + +function mcl_villages.post_process_village(blockseed) + local village_info = mcl_villages.get_village(blockseed) + if not village_info then + return + end + -- minetest.log("Postprocessing village") + + local settlement_info = village_info.data + local jobs = {} + local beds = {} + + local bell_pos = vector.copy(settlement_info[1]["pos"]) + local bell = vector.offset(bell_pos, 0, 2, 0) + local biome_data = minetest.get_biome_data(bell_pos) + local biome_name = minetest.get_biome_name(biome_data.biome) + --mcl_villages.paths(blockseed, biome_name) + + local l = minetest.add_entity(bell, "mobs_mc:iron_golem"):get_luaentity() + if l then + l._home = bell + else + minetest.log("info", "Could not create a golem!") + end + spawn_cats(bell) + + for _, building in pairs(settlement_info) do + local has_beds = building["num_beds"] and building["num_beds"] ~= nil + local has_jobs = building["num_jobs"] and building["num_jobs"] ~= nil + + local minp, maxp = building["minp"], building["maxp"] + + if has_jobs then + local jobsites = minetest.find_nodes_in_area(minp, maxp, mobs_mc.jobsites) + + for _, job_pos in pairs(jobsites) do + table.insert(jobs, job_pos) + end + end + + if has_beds then + local bld_beds = minetest.find_nodes_in_area(minp, maxp, { "group:bed" }) + + for _, bed_pos in pairs(bld_beds) do + local bed_node = minetest.get_node(bed_pos) + local bed_group = core.get_item_group(bed_node.name, "bed") + + -- We only spawn at bed bottoms + -- 1 is bottom, 2 is top + if bed_group == 1 then + table.insert(beds, bed_pos) + end + end + end + end + + -- minetest.log("beds: "..#beds.." jobsites: "..#jobs) + if beds then + for _, bed_pos in pairs(beds) do + local res = minetest.forceload_block(bed_pos, true) + if res then + mcl_villages.forced_blocks[minetest.pos_to_string(bed_pos)] = minetest.get_us_time() + end + local m = minetest.get_meta(bed_pos) + m:set_string("bell_pos", minetest.pos_to_string(bell_pos)) + if m:get_string("villager") == "" then + local v = minetest.add_entity(bed_pos, "mobs_mc:villager") + if v then + local l = v:get_luaentity() + l._bed = bed_pos + l._bell = bell_pos + m:set_string("villager", l._id) + m:set_string("infotext", S("A villager sleeps here")) + + local job_pos = table.remove(jobs, 1) + if job_pos then + villager_employ(l, job_pos) -- HACK: merge more MCLA villager code + end + + for _, callback in pairs(mcl_villages.on_villager_placed) do + callback(v, blockseed) + end + else + minetest.log("info", "Could not create a villager!") + end + else + minetest.log("info", "bed already owned by " .. m:get_string("villager")) + end end - spawn_villagers(pos,p2) - fix_village_water(pos,p2) end end diff --git a/mods/MAPGEN/mcl_villages/const.lua b/mods/MAPGEN/mcl_villages/const.lua index 8d2804952..77e3911ac 100644 --- a/mods/MAPGEN/mcl_villages/const.lua +++ b/mods/MAPGEN/mcl_villages/const.lua @@ -1,28 +1,9 @@ -- switch for debugging function mcl_villages.debug(message) - -- minetest.chat_send_all(message) - -- minetest.log("warning", "[mcl_villages] "..message) minetest.log("verbose", "[mcl_villages] "..message) end ---[[ Manually set in 'buildings.lua' --- material to replace cobblestone with -local wallmaterial = { - "mcl_core:junglewood", - "mcl_core:sprucewood", - "mcl_core:wood", - "mcl_core:birchwood", - "mcl_core:acaciawood", - "mcl_core:stonebrick", - "mcl_core:cobble", - "mcl_core:sandstonecarved", - "mcl_core:sandstone", - "mcl_core:sandstonesmooth2" -} ---]] --- -- possible surfaces where buildings can be built --- mcl_villages.surface_mat = {} mcl_villages.surface_mat["mcl_core:andesite"] = true mcl_villages.surface_mat["mcl_core:diorite"] = true @@ -40,7 +21,7 @@ mcl_villages.surface_mat["mcl_core:sandstone"] = true mcl_villages.surface_mat["mcl_core:sandstonesmooth"] = true mcl_villages.surface_mat["mcl_core:sandstonesmooth2"] = true --mcl_villages.surface_mat["mcl_core:silver_sand"] = true -mcl_villages.surface_mat["mcl_core:snow"] = true +--mcl_villages.surface_mat["mcl_core:snow"] = true mcl_villages.surface_mat["mcl_core:stone"] = true mcl_villages.surface_mat["mcl_core:stone_with_coal"] = true mcl_villages.surface_mat["mcl_core:stone_with_iron"] = true @@ -49,68 +30,10 @@ mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_orange"] = true mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_red"] = true mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_white"] = true --- --- path to schematics --- -schem_path = mcl_villages.modpath.."/schematics/" --- --- list of schematics --- -local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) - -mcl_villages.schematic_table = { - {name = "belltower", mts = schem_path.."new_villages/belltower.mts", hwidth = 9, hdepth = 9, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1, yadjust = 1 }, - --{name = "old_belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 6, hsize = 8, max_num = 0, rplc = basic_pseudobiome_villages, yadjust = 1 }, - --{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 10, hsize = 18, max_num = 0.08 , rplc = basic_pseudobiome_villages }, - --{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 8, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages }, - {name = "new_blacksmith", mts = schem_path.."new_villages/blacksmith.mts", hwidth = 9, hdepth = 11, hheight = 8, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "weaponsmith", mts = schem_path.."new_villages/weaponsmith.mts", hwidth = 11, hdepth = 9, hheight = 6, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "toolsmith", mts = schem_path.."new_villages/toolsmith.mts", hwidth = 9, hdepth = 11, hheight = 6, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "tannery", mts = schem_path.."new_villages/leather_worker.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - --{name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, hdepth = 8, hheight = 10, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages }, - --{name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 14, hheight = 15, hsize = 20, max_num = 0.01 , rplc = basic_pseudobiome_villages }, - {name = "newchurch", mts = schem_path.."new_villages/church.mts", hwidth = 14, hdepth = 16, hheight = 13, hsize = 22, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "chapel", mts = schem_path.."new_villages/chapel.mts", hwidth = 9, hdepth = 10, hheight = 6, hsize = 14, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - --{name = "farm", mts = schem_path.."farm.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 12, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 0 }, - --{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 4, hheight = 6, hsize = 6, max_num = 0.001 , rplc = false }, - {name = "lamp_1", mts = schem_path.."new_villages/lamp_1.mts", hwidth = 1, hdepth = 1, hheight = 4, hsize = 4, max_num = 0.001 , rplc = false, yadjust = 1 }, - {name = "lamp_2", mts = schem_path.."new_villages/lamp_2.mts", hwidth = 1, hdepth = 2, hheight = 6, hsize = 5, max_num = 0.001 , rplc = false, yadjust = 1 }, - {name = "lamp_3", mts = schem_path.."new_villages/lamp_3.mts", hwidth = 3, hdepth = 3, hheight = 4, hsize = 6, max_num = 0.001 , rplc = false, yadjust = 1 }, - {name = "lamp_4", mts = schem_path.."new_villages/lamp_4.mts", hwidth = 1, hdepth = 2, hheight = 5, hsize = 5, max_num = 0.001 , rplc = false, yadjust = 1 }, - {name = "lamp_5", mts = schem_path.."new_villages/lamp_5.mts", hwidth = 1, hdepth = 1, hheight = 2, hsize = 4, max_num = 0.001 , rplc = false, yadjust = 1 }, - {name = "lamp_6", mts = schem_path.."new_villages/lamp_6.mts", hwidth = 1, hdepth = 1, hheight = 3, hsize = 4, max_num = 0.001 , rplc = false, yadjust = 1 }, - --{name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 18, max_num = 0.01 , rplc = basic_pseudobiome_villages }, - {name = "newlibrary", mts = schem_path.."new_villages/library.mts", hwidth = 14, hdepth = 14, hheight = 7, hsize = 21, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - --{name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 9, hdepth = 12, hheight = 9, hsize = 16, max_num = 0.08 , rplc = basic_pseudobiome_villages }, - --{name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 8, hheight = 9, hsize = 13, max_num = 0.3 , rplc = basic_pseudobiome_villages }, - {name = "house_1_bed", mts = schem_path.."new_villages/house_1_bed.mts", hwidth = 9, hdepth = 8, hheight = 7, hsize = 13, max_num = 0.3 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "house_2_bed", mts = schem_path.."new_villages/house_2_bed.mts", hwidth = 11, hdepth = 8, hheight = 7, hsize = 15, max_num = 0.2 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "house_3_bed", mts = schem_path.."new_villages/house_3_bed.mts", hwidth = 11, hdepth = 13, hheight = 9, hsize = 18, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "house_4_bed", mts = schem_path.."new_villages/house_4_bed.mts", hwidth = 11, hdepth = 13, hheight = 10, hsize = 18, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "mason", mts = schem_path.."new_villages/mason.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "mill", mts = schem_path.."new_villages/mill.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "cartographer", mts = schem_path.."new_villages/cartographer.mts", hwidth = 9, hdepth = 12, hheight = 6, hsize = 16, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 2 }, - {name = "fletcher", mts = schem_path.."new_villages/fletcher.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "new_butcher", mts = schem_path.."new_villages/butcher.mts", hwidth = 8, hdepth = 14, hheight = 9, hsize = 17, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "fish_farm", mts = schem_path.."new_villages/fishery.mts", hwidth = 10, hdepth = 7, hheight = 9, hsize = 13, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust=-2 }, - --{name = "tavern", mts = schem_path.."tavern.mts", hwidth = 12, hdepth = 10, hheight = 13, hsize = 17, max_num = 0.050, rplc = basic_pseudobiome_villages }, - --{name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 7, hsize = 11, max_num = 0.01, rplc = basic_pseudobiome_villages }, - {name = "new_well", mts = schem_path.."new_villages/well.mts", hwidth = 6, hdepth = 6, hheight = 8, hsize = 9, max_num = 0.01, rplc = basic_pseudobiome_villages, yadjust=-1 }, - {name = "new_farm", mts = schem_path.."new_villages/farm.mts", hwidth=10, hdepth=9, hheight=6, hsize=14, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "farm_small", mts = schem_path.."new_villages/farm_small_1.mts", hwidth=10, hdepth=9, hheight=6, hsize=14, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "farm_small2", mts = schem_path.."new_villages/farm_small_2.mts", hwidth=9, hdepth=9, hheight=3, hsize=14, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 }, - {name = "farm_large", mts = schem_path.."new_villages/farm_large_1.mts", hwidth=13, hdepth=13, hheight=4, hsize=19, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 }, -} - --- -- maximum allowed difference in height for building a settlement --- -max_height_difference = 56 --- --- --- -half_map_chunk_size = 40 ---quarter_map_chunk_size = 20 +mcl_villages.max_height_difference = 56 + +mcl_villages.half_map_chunk_size = 40 -- -- Biome based block substitutions @@ -156,14 +79,14 @@ mcl_villages.biome_map = { CherryGrove = "cherry", - -- no change, but try to convert MCLA material - -- FlowerForest = "oak", - -- Forest = "oak", - -- MushroomIsland = "oak", - -- Plains = "oak", - -- StoneBeach = "oak", - -- SunflowerPlains = "oak", - -- Swampland = "oak", + -- no change + --FlowerForest = "oak", + --Forest = "oak", + --MushroomIsland = "", + --Plains = "oak", + --StoneBeach = "", + --SunflowerPlains = "oak", + --Swampland = "oak", } mcl_villages.vl_to_mcla = { @@ -190,8 +113,8 @@ mcl_villages.vl_to_mcla = { } mcl_villages.mcla_to_vl = { -- oneway - { '"mcl_villages:no_paths"', '"air"'}, -- TODO: support these - { '"mcl_villages:path_endpoint"', '"air"'}, -- TODO: support these + --{ '"mcl_villages:no_paths"', '"air"'}, -- TODO: support these + --{ '"mcl_villages:path_endpoint"', '"air"'}, -- TODO: support these { '"mcl_villages:crop_root', '"mcl_farming:potato'}, -- TODO: support biome specific farming { '"mcl_villages:crop_grain', '"mcl_farming:wheat'}, -- TODO: support biome specific farming { '"mcl_villages:crop_gourd', '"mcl_farming:pumpkin'}, -- TODO: support biome specific farming @@ -228,7 +151,7 @@ mcl_villages.mcla_to_vl = { } mcl_villages.material_substitions = { desert = { - { '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth%1"' }, + { '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1? { '"mesecons_pressureplates:pressure_plate_oak_([^"]+)"', '"mesecons_pressureplates:pressure_plate_birchwood_%1"', @@ -254,7 +177,7 @@ mcl_villages.material_substitions = { { "mcl_trees:wood_oak", "mcl_core:redsandstonesmooth" }, { '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' }, { '"mcl_stairs:stair_oak_bark([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' }, - { '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth%1"' }, + { '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1? }, spruce = { { '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sprucewood%1"' }, diff --git a/mods/MAPGEN/mcl_villages/foundation.lua b/mods/MAPGEN/mcl_villages/foundation.lua index a01b22b9c..5a26e7469 100644 --- a/mods/MAPGEN/mcl_villages/foundation.lua +++ b/mods/MAPGEN/mcl_villages/foundation.lua @@ -1,12 +1,12 @@ local foundation_materials = {} foundation_materials["mcl_core:sand"] = "mcl_core:sandstone" ---"mcl_core:sandstonecarved" +foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone" local function is_air(node) - return not node or node.name == "air" or node.name == "ignore" + return not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" end local function is_solid(node) - if not node or node.name == "air" or node.name == "ignore" then return false end + if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end --if string.find(node.name,"leaf") then return false end --if string.find(node.name,"tree") then return false end local ndef = minetest.registered_nodes[node.name] @@ -14,8 +14,8 @@ local function is_solid(node) end local function make_solid(lvm, cp, with, except) local cur = lvm:get_node_at(cp) - if not is_solid(cur) or (except and cur.name == except) then - lvm:set_node_at(cp, {name=with}) + if not is_solid(cur) or (except and cur.name == except.name) then + lvm:set_node_at(cp, with) end end local function excavate(lvm,xi,yi,zi,pr) @@ -34,7 +34,7 @@ local function excavate(lvm,xi,yi,zi,pr) -- try to completely remove trees overhead if not string.find(node.name, "leaf") and not string.find(node.name, "tree") then -- stop randomly depending on fill, to narrow down the caves - if pr:next(0,31)^2 > c * 100 then return false end + if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end end lvm:set_node_at(vector.new(xi, yi, zi),{name="air"}) return true -- modified @@ -45,9 +45,9 @@ local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat) pos.y = pos.y+1 local cur = lvm:get_node_at(pos) if not is_solid(cur) then return false end -- above is empty, do not fill below - if cur and cur.name and cur.name ~= surface_mat then platform_mat = cur.name end + if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end if pr:next(1,5) == 5 then -- randomly switch to stone sometimes - platform_mat = "mcl_core:stone" + platform_mat = { name = "mcl_core:stone" } end -- count solid nodes above otherwise for x = xi-1,xi+1 do @@ -57,137 +57,144 @@ local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat) end end -- stop randomly depending on fill, to narrow down the foundation - if pr:next(0,31)^2 > c * 100 then return false end - lvm:set_node_at(vector.new(xi, yi, zi),{name=platform_mat}) + if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end + lvm:set_node_at(vector.new(xi, yi, zi), platform_mat) return true -- modified end ------------------------------------------------------------------------------- -- function clear space above baseplate ------------------------------------------------------------------------------- -function mcl_villages.terraform(settlement_info, pr) +function mcl_villages.terraform(lvm, settlement, pr) + -- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft. --local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip") - local lvm = VoxelManip() + --local lvm = VoxelManip() - for i, built_house in ipairs(settlement_info) do - -- pick right schematic_info to current built_house - for j, schem in ipairs(mcl_villages.schematic_table) do - if settlement_info[i]["name"] == schem["name"] then - schematic_data = schem - break - end + -- we make the foundations 1 node wider than requested, to have one node for path laying + for i, building in ipairs(settlement) do + --lvm:read_from_map(vector.new(pos.x-2, pos.y-20, pos.z-2), vector.new(pos.x+sx+2, pos.y+sy+20, pos.z+sz+2)) + --lvm:get_data() + if not building.no_clearance then + local pos, size = building.pos, building.size + pos = vector.offset(pos, -math.floor(size.x/2)-1, 0, -math.floor(size.z/2)-1) + mcl_villages.clearance(lvm, pos.x, pos.y, pos.z, size.x+2, size.y, size.z+2, pr) end - local pos = settlement_info[i]["pos"] - local fwidth, fheight, fdepth = schematic_data["hwidth"], schematic_data["hheight"], schematic_data["hdepth"] - local surface_mat = settlement_info[i]["surface_mat"] - if settlement_info[i]["rotat"] == "90" or settlement_info[i]["rotat"] == "270" then - fwidth, fdepth = fdepth, fwidth + --lvm:write_to_map(false) + end + for i, building in ipairs(settlement) do + if not building.no_ground_turnip then + local pos, size = building.pos, building.size + local surface_mat = building.surface_mat + local platform_mat = building.platform_mat or { name = foundation_materials[surface_mat.name] or "mcl_core:dirt" } + building.platform_mat = platform_mat -- remember for use in schematic placement + pos = vector.offset(pos, -math.floor(size.x/2)-1, 0, -math.floor(size.z/2)-1) + mcl_villages.foundation(lvm, pos.x, pos.y, pos.z, size.x+2, -4, size.z+2, surface_mat, platform_mat, pr) end - lvm:read_from_map(vector.new(pos.x-2, pos.y-20, pos.z-2), vector.new(pos.x+fwidth+2, pos.y+fheight+20, pos.z+fdepth+2)) - lvm:get_data() - mcl_villages.foundation(lvm, pos, fwidth, fheight, fdepth, surface_mat, pr) - lvm:write_to_map(false) end end -function mcl_villages.foundation(lvm, pos, fwidth, fheight, fdepth, surface_mat, pr) - -- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft. - local platform_mat = foundation_materials[surface_mat] or "mcl_core:dirt" - +local AIR = {name = "air"} +function mcl_villages.clearance(lvm, px, py, pz, sx, sy, sz, pr) -- excavate the needed volume, some headroom, and add a baseplate - local p2 = vector.new(pos) - for xi = pos.x,pos.x+fwidth-1 do - for zi = pos.z,pos.z+fdepth-1 do - lvm:set_node_at(vector.new(xi, pos.y+1, zi),{name="air"}) - -- pos.y+2 to pos.y+5 are filled larger below! - for yi = pos.y+6,pos.y+fheight do - lvm:set_node_at(vector.new(xi, yi, zi),{name="air"}) + for xi = px,px+sx-1 do + for zi = pz,pz+sz-1 do + lvm:set_node_at(vector.new(xi, py+1, zi),AIR) + -- py+2 to py+5 are filled larger below! + for yi = py+6,py+sy do + lvm:set_node_at(vector.new(xi, yi, zi),AIR) end - make_solid(lvm, vector.new(xi, pos.y, zi), surface_mat, platform_mat) - make_solid(lvm, vector.new(xi, pos.y - 1, zi), platform_mat) end end -- slightly widen the cave, to make easier to enter for mobs - for xi = pos.x-1,pos.x+fwidth do - for zi = pos.z-1,pos.z+fdepth do - for yi = pos.y+2,pos.y+5 do - lvm:set_node_at(vector.new(xi, yi, zi),{name="air"}) + for xi = px-1,px+sx do + for zi = pz-1,pz+sz do + for yi = py+2,py+5 do + lvm:set_node_at(vector.new(xi, yi, zi),AIR) end end end -- some extra gaps - for xi = pos.x-2,pos.x+fwidth+1 do - for zi = pos.z-2,pos.z+fdepth+1 do + for xi = px-2,px+sx+1 do + for zi = pz-2,pz+sz+1 do if pr:next(1,4) == 1 then - for yi = pos.y+3,pos.y+5 do - lvm:set_node_at(vector.new(xi, yi, zi),{name="air"}) + for yi = py+3,py+5 do + lvm:set_node_at(vector.new(xi, yi, zi),AIR) end end end end - -- slightly widen the baseplate, to make easier to enter for mobs - for xi = pos.x,pos.x+fwidth-1 do - make_solid(lvm, vector.new(xi, pos.y-1, pos.z-1), surface_mat, platform_mat) - make_solid(lvm, vector.new(xi, pos.y-1, pos.z), platform_mat) - make_solid(lvm, vector.new(xi, pos.y-1, pos.z+fdepth-1), platform_mat) - make_solid(lvm, vector.new(xi, pos.y-1, pos.z+fdepth), surface_mat, platform_mat) + -- cave some additional area overhead, try to make it interesting though + for yi = py+3,py+sy*3 do + local active = false + for xi = px-2,px+sx+1 do + for zi = pz-2,pz+sz+1 do + if excavate(lvm,xi,yi,zi,pr) then active = true end + end + end + if not active and yi > py+sy+5 then break end end - for zi = pos.z,pos.z+fdepth-1 do - make_solid(lvm, vector.new(pos.x-1, pos.y-1, zi), surface_mat, platform_mat) - make_solid(lvm, vector.new(pos.x, pos.y-1, zi), platform_mat) - make_solid(lvm, vector.new(pos.x+fwidth-1, pos.y-1, zi), platform_mat) - make_solid(lvm, vector.new(pos.x+fwidth, pos.y-1, zi), surface_mat, platform_mat) +end +function mcl_villages.foundation(lvm, px, py, pz, sx, sy, sz, surface_mat, platform_mat, pr) + -- generate a baseplate + for xi = px,px+sx-1 do + for zi = pz,pz+sz-1 do + lvm:set_node_at(vector.new(xi, py, zi), surface_mat) + make_solid(lvm, vector.new(xi, py - 1, zi), platform_mat) + end + end + -- slightly widen the baseplate, to make easier to enter for mobs + for xi = px,px+sx-1 do + make_solid(lvm, vector.new(xi, py-1, pz-1), surface_mat, platform_mat) + make_solid(lvm, vector.new(xi, py-1, pz), platform_mat) + make_solid(lvm, vector.new(xi, py-1, pz+sz-1), platform_mat) + make_solid(lvm, vector.new(xi, py-1, pz+sz), surface_mat, platform_mat) + end + for zi = pz,pz+sz-1 do + make_solid(lvm, vector.new(px-1, py-1, zi), surface_mat, platform_mat) + make_solid(lvm, vector.new(px, py-1, zi), platform_mat) + make_solid(lvm, vector.new(px+sx-1, py-1, zi), platform_mat) + make_solid(lvm, vector.new(px+sx, py-1, zi), surface_mat, platform_mat) end -- make some additional steps, along both x sides - for xi = pos.x,pos.x+fwidth-1 do - local cp = vector.new(xi, pos.y-3, pos.z-1) + for xi = px,px+sx-1 do + local cp = vector.new(xi, py-3, pz-1) if is_solid(lvm:get_node_at(cp)) then - cp = vector.new(xi, pos.y-2, pos.z-1) + cp = vector.new(xi, py-2, pz-1) make_solid(lvm, cp, surface_mat, platform_mat) - cp.z = pos.z-2 + cp.z = pz-2 make_solid(lvm, cp, surface_mat, platform_mat) end - local cp = vector.new(xi, pos.y-3, pos.z+fdepth) + local cp = vector.new(xi, py-3, pz+sz) if is_solid(lvm:get_node_at(cp)) then - cp = vector.new(xi, pos.y-2, pos.z+fdepth) + cp = vector.new(xi, py-2, pz+sz) make_solid(lvm, cp, surface_mat, platform_mat) - cp.z = pos.z + fdepth + 1 + cp.z = pz + sz + 1 make_solid(lvm, cp, surface_mat, platform_mat) end end -- make some additional steps, along both z sides - for zi = pos.z,pos.z+fdepth-1 do - local cp = vector.new(pos.x-1, pos.y-3, zi) + for zi = pz,pz+sz-1 do + local cp = vector.new(px-1, py-3, zi) if is_solid(lvm:get_node_at(cp)) then - cp = vector.new(pos.x-1, pos.y-2, zi) + cp = vector.new(px-1, py-2, zi) make_solid(lvm, cp, surface_mat, platform_mat) - cp.x = pos.x-2 + cp.x = px-2 make_solid(lvm, cp, surface_mat, platform_mat) end - local cp = vector.new(pos.x+fwidth, pos.y-3, zi) + local cp = vector.new(px+sx, py-3, zi) if is_solid(lvm:get_node_at(cp)) then - cp = vector.new(pos.x+fwidth, pos.y-2, zi) + cp = vector.new(px+sx, py-2, zi) make_solid(lvm, cp, surface_mat, platform_mat) - cp.x = pos.x+fwidth+1 + cp.x = px+sx+1 make_solid(lvm, cp, surface_mat, platform_mat) end end - -- cave some additional area overhead, try to make it interesting though - for yi = pos.y+3,pos.y+fheight*3 do - local active = false - for xi = pos.x-2,pos.x+fwidth+1 do - for zi = pos.z-2,pos.z+fdepth+1 do - if excavate(lvm,xi,yi,zi,pr) then active = true end - end - end - if not active and yi > pos.y+fheight+5 then break end - end -- construct additional baseplate below, also try to make it interesting - for yi = pos.y-2,pos.y-20,-1 do + for yi = py-2,py-20,-1 do local active = false - for xi = pos.x-1,pos.x+fwidth do - for zi = pos.z-1,pos.z+fdepth do + for xi = px-1,px+sx do + for zi = pz-1,pz+sz do if grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat) then active = true end end end - if not active and yi < pos.y-5 then break end + if not active and yi < py + sy then break end end end diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua index d19cf2153..af8617edd 100644 --- a/mods/MAPGEN/mcl_villages/init.lua +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -1,33 +1,25 @@ mcl_villages = {} mcl_villages.modpath = minetest.get_modpath(minetest.get_current_modname()) -local village_chance = tonumber(minetest.settings:get("mcl_villages_village_chance")) or 5 +local village_chance = tonumber(minetest.settings:get("mcl_villages_village_probability")) or 5 dofile(mcl_villages.modpath.."/const.lua") dofile(mcl_villages.modpath.."/utils.lua") dofile(mcl_villages.modpath.."/foundation.lua") dofile(mcl_villages.modpath.."/buildings.lua") dofile(mcl_villages.modpath.."/paths.lua") +dofile(mcl_villages.modpath.."/api.lua") local S = minetest.get_translator(minetest.get_current_modname()) --- --- register block for npc spawn --- -minetest.register_node("mcl_villages:stonebrickcarved", { - description = S("Chiseled Stone Village Bricks"), - _doc_items_longdesc = doc.sub.items.temp.build, - tiles = {"mcl_core_stonebrick_carved.png"}, - drop = "mcl_core:stonebrickcarved", - groups = {pickaxey=1, stone=1, stonebrick=1, building_block=1, material_stone=1}, - sounds = mcl_sounds.node_sound_stone_defaults(), - is_ground_content = false, - _mcl_blast_resistance = 6, - _mcl_hardness = 1.5, -}) - +minetest.register_alias("mcl_villages:stonebrickcarved", "mcl_core:stonebrickcarved") +--TODO: minetest.register_alias("mcl_villages:structblock", "air") minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1},}) - +-- we currently do not support/use these from MCLA: +--minetest.register_alias("mcl_villages:village_block", "air") +--minetest.register_alias("mcl_villages:no_paths", "air") +--minetest.register_alias("mcl_villages:path_endpoint", "air") +--minetest.register_alias("mcl_villages:building_block", "air") -- -- on map generation, try to build a settlement -- @@ -35,13 +27,22 @@ local function build_a_settlement(minp, maxp, blockseed) if mcl_villages.village_exists(blockseed) then return end local pr = PseudoRandom(blockseed) - local settlement_info = mcl_villages.create_site_plan(minp, maxp, pr) - if not settlement_info then return end + local settlement = mcl_villages.create_site_plan(minp, maxp, pr) + if not settlement then return end - --mcl_villages.terraform(settlement_info, pr) - mcl_villages.place_schematics(settlement_info, pr) - mcl_villages.paths(settlement_info) - mcl_villages.add_village(blockseed, settlement_info) + local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip") + lvm:get_data() + + -- all foundations first, then all buildings, to avoid damaging very close buildings + --mcl_villages.terraform(lvm, settlement, pr) + --mcl_villages.place_schematics(lvm, settlement, pr) + lvm:write_to_map(false) + -- TODO: replace with MCLA code: mcl_villages.paths(settlement) + mcl_villages.add_village(blockseed, settlement) + + for _, on_village_placed_callback in pairs(mcl_villages.on_village_placed) do + on_village_placed_callback(settlement, blockseed) + end end local function ecb_village(blockpos, action, calls_remaining, param) @@ -50,28 +51,73 @@ local function ecb_village(blockpos, action, calls_remaining, param) build_a_settlement(minp, maxp, blockseed) end -local villagegen={} +--local villagegen={} -- Disable natural generation in singlenode. local mg_name = minetest.get_mapgen_setting("mg_name") if mg_name ~= "singlenode" then - mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed) - if maxp.y < 0 or village_chance == 0 then return end - local pr = PseudoRandom(blockseed) - if pr:next(0, 100) > village_chance then return end - local n=minetest.get_node_or_nil(minp) - --if n and n.name == "mcl_villages:structblock" then return end - if n and n.name ~= "air" then return end - minetest.set_node(minp,{name="mcl_villages:structblock"}) + --[[ did not work, because later structure generation would make holes in our schematics + mcl_mapgen_core.register_generator("villages", function(lvm, data, data2, e1, e2, area, minp, maxp, blockseed) + if mcl_villages.village_exists(blockseed) then return false, false end - --[[minetest.emerge_area( - minp, maxp, --vector.offset(minp, -16, -16, -16), vector.offset(maxp, 16, 16, 16), - ecb_village, - { minp = vector.copy(minp), maxp = vector.copy(maxp), blockseed = blockseed } - )]]-- - villagegen[minetest.pos_to_string(minp)]={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed} - end) + lvm:set_data(data) -- FIXME: ugly hack, better directly manipulate the data array + lvm:set_param2_data(data2) + local pr = PcgRandom(blockseed) + if pr:next(0, 100) > village_chance then return end + local settlement = mcl_villages.create_site_plan(lvm, minp, maxp, pr) + if not settlement then return false, false end + + -- all foundations first, then all buildings, to avoid damaging very close buildings + mcl_villages.terraform(lvm, settlement, pr) + mcl_villages.place_schematics(lvm, settlement, pr) + -- TODO: replace with MCLA code: mcl_villages.paths(settlement) + mcl_villages.add_village(blockseed, settlement) + lvm:get_data(data) -- FIXME: ugly hack, better directly manipulate the data array + lvm:get_param2_data(data2) + return true, true + end, function(minp, maxp, blockseed) + for _, on_village_placed_callback in pairs(mcl_villages.on_village_placed) do + on_village_placed_callback(settlement, blockseed) + end + end, 15000) + ]]-- + mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed) + if maxp.y < 0 or mcl_villages.village_exists(blockseed) then return end + local pr = PcgRandom(blockseed) + if pr:next(0, 100) > village_chance then return end + --local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip") -- did not get the lighting fixed? + local lvm = VoxelManip() + lvm:read_from_map(minp, maxp) + local settlement = mcl_villages.create_site_plan(lvm, minp, maxp, pr) + if not settlement then return false, false end + -- all foundations first, then all buildings, to avoid damaging very close buildings + mcl_villages.terraform(lvm, settlement, pr) + mcl_villages.place_schematics(lvm, settlement, blockseed, pr) + mcl_villages.add_village(blockseed, settlement) + --lvm:write_to_map(true) + --mcl_villages.paths(blockseed) -- TODO: biome + end, 15000) end +-- This is a light source so that lamps don't get placed near it +minetest.register_node("mcl_villages:village_block", { + drawtype = "airlike", + groups = { not_in_creative_inventory = 1 }, + light_source = 14, + + -- Somethings don't work reliably when done in the map building + -- so we use a timer to run them later when they work more reliably + -- e.g. spawning mobs, running minetest.find_path + on_timer = function(pos, elapsed) + local meta = minetest.get_meta(pos) + local blockseed = meta:get_string("blockseed") + local node_type = meta:get_string("node_type") + minetest.set_node(pos, { name = node_type }) + mcl_villages.post_process_village(blockseed) + return false + end, +}) + +--[[ minetest.register_lbm({ name = "mcl_villages:structblock", run_at_every_load = true, @@ -85,6 +131,7 @@ minetest.register_lbm({ villagegen[minetest.pos_to_string(minp)]=nil end }) +]]-- -- manually place villages if minetest.is_creative_enabled("") then minetest.register_craftitem("mcl_villages:tool", { @@ -99,8 +146,340 @@ if minetest.is_creative_enabled("") then end local minp = vector.subtract(pointed_thing.under, half_map_chunk_size) local maxp = vector.add(pointed_thing.under, half_map_chunk_size) - build_a_settlement(minp, maxp, math.random(0,32767)) + --build_a_settlement(minp, maxp, math.random(0,32767)) end }) mcl_wip.register_experimental_item("mcl_villages:tool") end + +-- This makes the temporary node invisble unless in creative mode +local drawtype = "airlike" +if minetest.is_creative_enabled("") then + drawtype = "glasslike" +end + +minetest.register_node("mcl_villages:no_paths", { + description = S( + "Prevent paths from being placed during villager generation. Replaced by air after village path generation" + ), + paramtype = "light", + drawtype = drawtype, + inventory_image = "mcl_core_barrier.png", + wield_image = "mcl_core_barrier.png", + tiles = { "mcl_core_barrier.png" }, + is_ground_content = false, + groups = { creative_breakable = 1, not_solid = 1, not_in_creative_inventory = 1 }, +}) + +minetest.register_node("mcl_villages:path_endpoint", { + description = S("Mark the node as a good place for paths to connect to"), + is_ground_content = false, + tiles = { "wool_white.png" }, + wield_image = "wool_white.png", + wield_scale = { x = 1, y = 1, z = 0.5 }, + groups = { handy = 1, supported_node = 1, not_in_creative_inventory = 1 }, + sounds = mcl_sounds.node_sound_wool_defaults(), + paramtype = "light", + sunlight_propagates = true, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + { -8 / 16, -8 / 16, -8 / 16, 8 / 16, -7 / 16, 8 / 16 }, + }, + }, + _mcl_hardness = 0.1, + _mcl_blast_resistance = 0.1, +}) + +local schem_path = mcl_villages.modpath .. "/schematics/" + +mcl_villages.register_bell({ name = "belltower", mts = schem_path .. "new_villages/belltower.mts", yadjust = 1 }) + +mcl_villages.register_well({ + name = "well", + mts = schem_path .. "new_villages/well.mts", + yadjust = -1, +}) + +for i = 1, 6 do + mcl_villages.register_lamp({ + name = "lamp", + mts = schem_path .. "new_villages/lamp_" .. i .. ".mts", + yadjust = 1, + no_ground_turnip = true, + no_clearance = true, + }) +end + +mcl_villages.register_building({ + name = "house_big", + mts = schem_path .. "new_villages/house_4_bed.mts", + min_jobs = 6, + max_jobs = 99, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "house_large", + mts = schem_path .. "new_villages/house_3_bed.mts", + min_jobs = 4, + max_jobs = 99, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "house_medium", + mts = schem_path .. "new_villages/house_2_bed.mts", + min_jobs = 2, + max_jobs = 99, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "house_small", + mts = schem_path .. "new_villages/house_1_bed.mts", + min_jobs = 1, + max_jobs = 99, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "blacksmith", + mts = schem_path .. "new_villages/blacksmith.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "butcher", + mts = schem_path .. "new_villages/butcher.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "farm", + mts = schem_path .. "new_villages/farm.mts", + num_others = 3, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "fish_farm", + mts = schem_path .. "new_villages/fishery.mts", + num_others = 8, + yadjust = -2, +}) + +mcl_villages.register_building({ + name = "fletcher", + mts = schem_path .. "new_villages/fletcher.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "library", + mts = schem_path .. "new_villages/library.mts", + num_others = 15, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "map_shop", + mts = schem_path .. "new_villages/cartographer.mts", + num_others = 15, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "mason", + mts = schem_path .. "new_villages/mason.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "mill", + mts = schem_path .. "new_villages/mill.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "tannery", + mts = schem_path .. "new_villages/leather_worker.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "tool_smith", + mts = schem_path .. "new_villages/toolsmith.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "weapon_smith", + mts = schem_path .. "new_villages/weaponsmith.mts", + num_others = 8, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "chapel", + mts = schem_path .. "new_villages/chapel.mts", + num_others = 8, + min_jobs = 1, + max_jobs = 9, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "church", + mts = schem_path .. "new_villages/church.mts", + num_others = 20, + min_jobs = 10, + max_jobs = 99, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "farm_small", + mts = schem_path .. "new_villages/farm_small_1.mts", + num_others = 3, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "farm_small2", + mts = schem_path .. "new_villages/farm_small_2.mts", + num_others = 3, + yadjust = 1, +}) + +mcl_villages.register_building({ + name = "farm_large", + mts = schem_path .. "new_villages/farm_large_1.mts", + num_others = 6, + yadjust = 1, +}) + +for _, crop_type in pairs(mcl_villages.get_crop_types()) do + for count = 1, 8 do + local tile = crop_type .. "_" .. count .. ".png" + minetest.register_node("mcl_villages:crop_" .. crop_type .. "_" .. count, { + description = S("A place to plant @1 crops", crop_type), + is_ground_content = false, + tiles = { tile }, + wield_image = tile, + wield_scale = { x = 1, y = 1, z = 0.5 }, + groups = { handy = 1, supported_node = 1, not_in_creative_inventory = 1 }, + paramtype = "light", + sunlight_propagates = true, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + { -8 / 16, -8 / 16, -8 / 16, 8 / 16, -7 / 16, 8 / 16 }, + }, + }, + _mcl_hardness = 0.1, + _mcl_blast_resistance = 0.1, + }) + end +end + +mcl_villages.register_crop({ + type = "grain", + node = "mcl_farming:wheat_1", + biomes = { + acacia = 10, + bamboo = 10, + desert = 10, + jungle = 10, + plains = 10, + savanna = 10, + spruce = 10, + }, +}) + +mcl_villages.register_crop({ + type = "root", + node = "mcl_farming:carrot_1", + biomes = { + acacia = 10, + bamboo = 6, + desert = 10, + jungle = 6, + plains = 6, + spruce = 10, + }, +}) + +mcl_villages.register_crop({ + type = "root", + node = "mcl_farming:potato_1", + biomes = { + acacia = 6, + bamboo = 10, + desert = 6, + jungle = 10, + plains = 10, + spruce = 6, + }, +}) + +mcl_villages.register_crop({ + type = "root", + node = "mcl_farming:beetroot_0", + biomes = { + acacia = 3, + bamboo = 3, + desert = 3, + jungle = 3, + plains = 3, + spruce = 3, + }, +}) + +mcl_villages.register_crop({ + type = "gourd", + node = "mcl_farming:melontige_1", + biomes = { + bamboo = 10, + jungle = 10, + }, +}) + +mcl_villages.register_crop({ + type = "gourd", + node = "mcl_farming:pumpkin_1", + biomes = { + acacia = 10, + bamboo = 5, + desert = 10, + jungle = 5, + plains = 10, + spruce = 10, + }, +}) + +for name, def in pairs(minetest.registered_nodes) do + if def.groups["flower"] and not def.groups["double_plant"] and name ~= "mcl_flowers:wither_rose" then + mcl_villages.register_crop({ + type = "flower", + node = name, + biomes = { + acacia = 10, + bamboo = 6, + desert = 10, + jungle = 6, + plains = 6, + spruce = 10, + }, + }) + end +end diff --git a/mods/MAPGEN/mcl_villages/paths.lua b/mods/MAPGEN/mcl_villages/paths.lua index 8f3ef3104..c820b737d 100644 --- a/mods/MAPGEN/mcl_villages/paths.lua +++ b/mods/MAPGEN/mcl_villages/paths.lua @@ -1,91 +1,351 @@ ------------------------------------------------------------------------------- -- generate paths between buildings ------------------------------------------------------------------------------- -function mcl_villages.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 +local light_threshold = tonumber(minetest.settings:get("mcl_villages_light_threshold")) or 5 - -- define surrounding pos to starting_point - local north_p = {x=starting_point.x+1, y=starting_point.y, z=starting_point.z} - local south_p = {x=starting_point.x-1, y=starting_point.y, z=starting_point.z} - local west_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z+1} - local east_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z-1} - -- measure distance to end_point - local dist_north_p_to_end = math.sqrt( - ((north_p.x - end_point.x)*(north_p.x - end_point.x))+ - ((north_p.z - end_point.z)*(north_p.z - end_point.z)) - ) - local dist_south_p_to_end = math.sqrt( - ((south_p.x - end_point.x)*(south_p.x - end_point.x))+ - ((south_p.z - end_point.z)*(south_p.z - end_point.z)) - ) - local dist_west_p_to_end = math.sqrt( - ((west_p.x - end_point.x)*(west_p.x - end_point.x))+ - ((west_p.z - end_point.z)*(west_p.z - end_point.z)) - ) - local dist_east_p_to_end = math.sqrt( - ((east_p.x - end_point.x)*(east_p.x - end_point.x))+ - ((east_p.z - end_point.z)*(east_p.z - end_point.z)) - ) - -- evaluate which pos is closer to the end_point - if dist_north_p_to_end <= dist_south_p_to_end and - dist_north_p_to_end <= dist_west_p_to_end and - dist_north_p_to_end <= dist_east_p_to_end - then - starting_point = north_p - distance = dist_north_p_to_end +-- This ends up being a nested table. +-- 1st level is the blockseed which is the village +-- 2nd is the distance of the building from the bell +-- 3rd is the pos of the end points +local path_ends = {} - elseif dist_south_p_to_end <= dist_north_p_to_end and - dist_south_p_to_end <= dist_west_p_to_end and - dist_south_p_to_end <= dist_east_p_to_end - then - starting_point = south_p - distance = dist_south_p_to_end - - elseif dist_west_p_to_end <= dist_north_p_to_end and - dist_west_p_to_end <= dist_south_p_to_end and - dist_west_p_to_end <= dist_east_p_to_end - then - starting_point = west_p - distance = dist_west_p_to_end - - elseif dist_east_p_to_end <= dist_north_p_to_end and - dist_east_p_to_end <= dist_south_p_to_end and - dist_east_p_to_end <= dist_west_p_to_end - then - starting_point = east_p - distance = dist_east_p_to_end - end - -- find surface of new starting point - local surface_point, surface_mat = mcl_villages.find_surface(starting_point) - -- replace surface node with mcl_core:grass_path - if surface_point - then - if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then - minetest.swap_node(surface_point,{name="mcl_core:sandstonesmooth2"}) - else - minetest.swap_node(surface_point,{name="mcl_core:grass_path"}) - end - -- don't set y coordinate, surface might be too low or high - starting_point.x = surface_point.x - starting_point.z = surface_point.z - end - if distance <= 1 or - starting_point == end_point - then - break - end - end - end - end +-- Insert end points in to the nested tables +function mcl_villages.store_path_ends(lvm, minp, maxp, pos, blockseed, bell_pos) + -- We store by distance because we create paths far away from the bell first + local dist = vector.distance(bell_pos, pos) + local id = "block_" .. blockseed -- cannot use integers as keys + local tab = path_ends[id] + if not tab then + tab = {} + path_ends[id] = tab + end + if tab[dist] == nil then tab[dist] = {} end + -- TODO: improve, use LVM data instead of nodes + local v = vector.zero() + local i = 0 + for zi = minp.z-2, maxp.z+2 do + v.z = zi + for yi = minp.y-2, maxp.y+2 do + v.y = yi + for xi = minp.x-2, maxp.x+2 do + v.x = xi + local n = lvm:get_node_at(v) + if n and n.name == "mcl_villages:path_endpoint" then + i = i + 1 + table.insert(tab[dist], minetest.pos_to_string(v)) + lvm:set_node_at(v, { name = "air" }) + end + end + end + end +end + +local function place_lamp(pos, pr) + local lamp_index = pr:next(1, #mcl_villages.schematic_lamps) + local schema = mcl_villages.schematic_lamps[lamp_index] + local schem_lua = mcl_villages.substitute_materials(pos, schema.schem_lua, pr) + local schematic = loadstring(schem_lua)() + + minetest.place_schematic( + vector.offset(pos, 0, schema.yadjust or 0, 0), + schematic, + "0", + {["air"] = "ignore"}, -- avoid destroying stairs etc. + true, + { place_center_x = true, place_center_y = false, place_center_z = true } + ) +end + +local function smooth_path(path) + -- Smooth out bumps in path or stairs can look naf + for pass = 1, 3 do + for i = 2, #path - 1 do + local prev_y = path[i - 1].y + local y = path[i].y + local next_y = path[i + 1].y + local bump_node = minetest.get_node(path[i]) + + if minetest.get_item_group(bump_node.name, "water") ~= 0 then + -- ignore in this pass + elseif y >= next_y + 2 and y <= prev_y then + local under_pos = vector.offset(path[i], 0, -1, 0) + minetest.swap_node(under_pos, { name = "air" }) + path[i].y = path[i].y - 1 + elseif y <= next_y - 2 and y >= prev_y then + minetest.swap_node(path[i], { name = "mcl_core:dirt" }) + path[i].y = path[i].y + 1 + elseif y >= prev_y + 2 and y <= next_y then + local under_pos = vector.offset(path[i], 0, -1, 0) + minetest.swap_node(under_pos, { name = "air" }) + path[i].y = path[i].y - 1 + elseif y <= prev_y - 2 and y >= prev_y then + minetest.swap_node(path[i], { name = "mcl_core:dirt" }) + path[i].y = path[i].y + 1 + elseif y < prev_y and y < next_y then + -- Fill in dip to flatten path + minetest.swap_node(path[i], { name = "mcl_core:dirt" }) + path[i].y = path[i].y + 1 + elseif y > prev_y and y > next_y then + -- Remove peak to flatten path + local under_pos = vector.offset(path[i], 0, -1, 0) + minetest.swap_node(under_pos, { name = "air" }) + path[i].y = path[i].y - 1 + end + end + end +end + +local function place_path(path, pr, stair, slab) + -- Smooth out bumps in path or stairs can look naf + for i = 2, #path - 1 do + local prev_y = path[i - 1].y + local y = path[i].y + local next_y = path[i + 1].y + local bump_node = minetest.get_node(path[i]) + + if minetest.get_item_group(bump_node.name, "water") ~= 0 then + -- Find air + local found_surface = false + local up_pos = path[i] + while not found_surface do + up_pos = vector.offset(up_pos, 0, 1, 0) + local up_node = minetest.get_node(up_pos) + if up_node and minetest.get_item_group(up_node.name, "water") == 0 then + found_surface = true + minetest.swap_node(up_pos, { name = "air" }) + path[i] = up_pos + end + end + elseif y < prev_y and y < next_y then + -- Fill in dip to flatten path + -- TODO: do not break other path/stairs + minetest.swap_node(path[i], { name = "mcl_core:dirt" }) + path[i] = vector.offset(path[i], 0, 1, 0) + elseif y > prev_y and y > next_y then + -- TODO: do not break other path/stairs + -- Remove peak to flatten path + local under_pos = vector.offset(path[i], 0, -1, 0) + minetest.swap_node(under_pos, { name = "air" }) + path[i] = under_pos + end + end + + for i, pos in ipairs(path) do + -- replace decorations, grass and flowers, with air + local n0 = minetest.get_node(pos) + if n0.name ~= "air" then + minetest.swap_node(pos, { name = "air" }) + end + + local under_pos = vector.offset(pos, 0, -1, 0) + local n = minetest.get_node(under_pos) + local done = false + local is_stair = minetest.get_item_group(n.name, "stair") ~= 0 + + if i > 1 and pos.y > path[i - 1].y then + -- stairs up + if not is_stair then + done = true + local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1])) + minetest.swap_node(under_pos, { name = stair, param2 = param2 }) + end + elseif i < #path-1 and pos.y > path[i + 1].y then + -- stairs down + if not is_stair then + done = true + local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1])) + minetest.swap_node(under_pos, { name = stair, param2 = param2 }) + end + elseif not is_stair and i > 1 and pos.y < path[i - 1].y then + -- stairs down + local n2 = minetest.get_node(vector.offset(path[i - 1], 0, -1, 0)) + is_stair = minetest.get_item_group(n2.name, "stair") ~= 0 + if not is_stair then + done = true + local param2 = minetest.dir_to_facedir(vector.subtract(path[i - 1], pos)) + if i < #path - 1 then -- uglier, but easier to walk up? + param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1])) + end + minetest.add_node(pos, { name = stair, param2 = param2 }) + pos.y = pos.y + 1 + end + elseif not is_stair and i < #path-1 and pos.y < path[i + 1].y then + -- stairs up + local n2 = minetest.get_node(vector.offset(path[i + 1], 0, -1, 0)) + is_stair = minetest.get_item_group(n2.name, "stair") ~= 0 + if not is_stair then + done = true + local param2 = minetest.dir_to_facedir(vector.subtract(path[i + 1], pos)) + if i > 1 then -- uglier, but easier to walk up? + param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1])) + end + minetest.add_node(pos, { name = stair, param2 = param2 }) + pos.y = pos.y + 1 + end + end + + -- flat + if not done then + if minetest.get_item_group(n.name, "water") ~= 0 then + minetest.add_node(under_pos, { name = slab }) + elseif n.name == "mcl_core:sand" or n.name == "mcl_core:redsand" then + minetest.swap_node(under_pos, { name = "mcl_core:sandstonesmooth2" }) + elseif minetest.get_item_group(n.name, "soil") > 0 + and minetest.get_item_group(n.name, "dirtifies_below_solid") == 0 + then + minetest.swap_node(under_pos, { name = "mcl_core:grass_path" }) + end + end + + -- Clear space for villagers to walk + for j = 1, 2 do + local over_pos = vector.offset(pos, 0, j, 0) + local m = minetest.get_node(over_pos) + if m.name ~= "air" then + minetest.swap_node(over_pos, { name = "air" }) + end + end + end + + -- Do lamps afterwards so we don't put them where a path will be laid + for _, pos in ipairs(path) do + if minetest.get_node_light(pos, 0) < light_threshold then + local nn = minetest.find_nodes_in_area_under_air( + vector.offset(pos, -1, -1, -1), + vector.offset(pos, 1, 1, 1), + { "group:material_sand", "group:material_stone", "group:grass_block", "group:wood_slab" } + ) + for _, npos in ipairs(nn) do + local node = minetest.get_node(npos) + if node.name ~= "mcl_core:grass_path" and minetest.get_item_group(node.name, "stair") == 0 then + if minetest.get_item_group(node.name, "wood_slab") ~= 0 then + local over_pos = vector.offset(npos, 0, 1, 0) + minetest.add_node(over_pos, { name = "mcl_torches:torch", param2 = 1 }) + else + place_lamp(npos, pr) + end + break + end + end + end + end +end + +-- Work out which end points should be connected +-- works from the outside of the village in +function mcl_villages.paths(blockseed, biome_name) + local pr = PseudoRandom(blockseed) + local pathends = path_ends["block_" .. blockseed] + + if pathends == nil then + minetest.log("warning", string.format("[mcl_villages] Tried to set paths for block seed that doesn't exist %d", blockseed)) + return + end + + -- Use the same stair and slab throughout the entire village + local stair, slab = '"mcl_stairs:stair_oak"', '"mcl_stairs:slab_oak_top"' + + -- Change stair and slab for biome + if mcl_villages.biome_map[biome_name] and mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]] then + for _, sub in pairs(mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]]) do + stair = stair:gsub(sub[1], sub[2]) + slab = slab:gsub(sub[1], sub[2]) + end + end + -- translate MCLA values to VL + for _, sub in pairs(mcl_villages.mcla_to_vl) do + stair = stair:gsub(sub[1], sub[2]) + slab = slab:gsub(sub[1], sub[2]) + end + -- The quotes are to match what is in schemas, but we don't want them now + stair = stair:gsub('"', "") + slab = slab:gsub('"', "") + + -- Keep track of connections + local connected = {} + + -- get a list of reverse sorted keys, which are distances + local dist_keys = {} + for k in pairs(pathends) do + table.insert(dist_keys, k) + end + table.sort(dist_keys, function(a, b) + return a > b + end) + --minetest.log("Planning paths with "..#dist_keys.." nodes") + + for i, from in ipairs(dist_keys) do + -- ep == end_point + for _, from_ep in ipairs(pathends[from]) do + local from_ep_pos = minetest.string_to_pos(from_ep) + local closest_pos + local closest_bld + local best = 10000000 + + -- Most buildings only do other buildings that are closer to the bell + -- for the bell do any end points that don't have paths near them + local lindex = i + 1 + if i == 0 then + lindex = 1 + end + + for j = lindex, #dist_keys do + local to = dist_keys[j] + if from ~= to and connected[from .. "-" .. to] == nil and connected[to .. "-" .. from] == nil then + for _, to_ep in ipairs(pathends[to]) do + local to_ep_pos = minetest.string_to_pos(to_ep) + + local dist = vector.distance(from_ep_pos, to_ep_pos) + if dist < best then + best = dist + closest_pos = to_ep_pos + closest_bld = to + end + end + end + end + + if closest_pos then + local path = minetest.find_path(from_ep_pos, closest_pos, 64, 2, 2) + if path then smooth_path(path) end + if not path then + path = minetest.find_path(from_ep_pos, closest_pos, 64, 3, 3) + if path then smooth_path(path) end + end + path = minetest.find_path(from_ep_pos, closest_pos, 64, 1, 1) + if path and #path > 0 then + place_path(path, pr, stair, slab) + connected[from .. "-" .. closest_bld] = 1 + else + minetest.log( + "warning", + string.format( + "[mcl_villages] No path from %s to %s, distance %d", + minetest.pos_to_string(from_ep_pos), + minetest.pos_to_string(closest_pos), + vector.distance(from_ep_pos, closest_pos) + ) + ) + end + end + end + end + + -- Loop again to blow away no path nodes + for _, from in ipairs(dist_keys) do + for _, from_ep in ipairs(pathends[from]) do + local from_ep_pos = minetest.string_to_pos(from_ep) + local no_paths_nodes = minetest.find_nodes_in_area( + vector.offset(from_ep_pos, -32, -32, -32), + vector.offset(from_ep_pos, 32, 32, 32), + { "mcl_villages:no_paths" } + ) + if #no_paths_nodes > 0 then + minetest.bulk_set_node(no_paths_nodes, { name = "air" }) + end + end + end + + path_ends["block_" .. blockseed] = nil end diff --git a/mods/MAPGEN/mcl_villages/textures/bush_1.png b/mods/MAPGEN/mcl_villages/textures/bush_1.png new file mode 100644 index 000000000..789fb4b24 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_1.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_2.png b/mods/MAPGEN/mcl_villages/textures/bush_2.png new file mode 100644 index 000000000..bd6773785 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_2.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_3.png b/mods/MAPGEN/mcl_villages/textures/bush_3.png new file mode 100644 index 000000000..b0a4b8d1d Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_3.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_4.png b/mods/MAPGEN/mcl_villages/textures/bush_4.png new file mode 100644 index 000000000..15aa71855 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_4.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_5.png b/mods/MAPGEN/mcl_villages/textures/bush_5.png new file mode 100644 index 000000000..83369fb37 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_5.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_6.png b/mods/MAPGEN/mcl_villages/textures/bush_6.png new file mode 100644 index 000000000..7725e6245 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_6.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_7.png b/mods/MAPGEN/mcl_villages/textures/bush_7.png new file mode 100644 index 000000000..206d590bd Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_7.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/bush_8.png b/mods/MAPGEN/mcl_villages/textures/bush_8.png new file mode 100644 index 000000000..6b7e77dca Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/bush_8.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_1.png b/mods/MAPGEN/mcl_villages/textures/flower_1.png new file mode 100644 index 000000000..a02f72f69 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_1.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_2.png b/mods/MAPGEN/mcl_villages/textures/flower_2.png new file mode 100644 index 000000000..216557ad4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_2.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_3.png b/mods/MAPGEN/mcl_villages/textures/flower_3.png new file mode 100644 index 000000000..d1d190182 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_3.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_4.png b/mods/MAPGEN/mcl_villages/textures/flower_4.png new file mode 100644 index 000000000..49416a9ca Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_4.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_5.png b/mods/MAPGEN/mcl_villages/textures/flower_5.png new file mode 100644 index 000000000..fbdb81e4d Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_5.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_6.png b/mods/MAPGEN/mcl_villages/textures/flower_6.png new file mode 100644 index 000000000..eb22c0911 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_6.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_7.png b/mods/MAPGEN/mcl_villages/textures/flower_7.png new file mode 100644 index 000000000..608905458 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_7.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/flower_8.png b/mods/MAPGEN/mcl_villages/textures/flower_8.png new file mode 100644 index 000000000..bb29d5a03 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/flower_8.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_1.png b/mods/MAPGEN/mcl_villages/textures/gourd_1.png new file mode 100644 index 000000000..acdde8463 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_1.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_2.png b/mods/MAPGEN/mcl_villages/textures/gourd_2.png new file mode 100644 index 000000000..77f44436c Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_2.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_3.png b/mods/MAPGEN/mcl_villages/textures/gourd_3.png new file mode 100644 index 000000000..a081fd28f Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_3.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_4.png b/mods/MAPGEN/mcl_villages/textures/gourd_4.png new file mode 100644 index 000000000..e18911efc Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_4.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_5.png b/mods/MAPGEN/mcl_villages/textures/gourd_5.png new file mode 100644 index 000000000..39b21741e Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_5.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_6.png b/mods/MAPGEN/mcl_villages/textures/gourd_6.png new file mode 100644 index 000000000..51d82d05e Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_6.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_7.png b/mods/MAPGEN/mcl_villages/textures/gourd_7.png new file mode 100644 index 000000000..a23864a05 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_7.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/gourd_8.png b/mods/MAPGEN/mcl_villages/textures/gourd_8.png new file mode 100644 index 000000000..7d4ccbee3 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/gourd_8.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_1.png b/mods/MAPGEN/mcl_villages/textures/grain_1.png new file mode 100644 index 000000000..6c52939a4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_1.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_2.png b/mods/MAPGEN/mcl_villages/textures/grain_2.png new file mode 100644 index 000000000..05203f948 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_2.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_3.png b/mods/MAPGEN/mcl_villages/textures/grain_3.png new file mode 100644 index 000000000..1186db58f Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_3.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_4.png b/mods/MAPGEN/mcl_villages/textures/grain_4.png new file mode 100644 index 000000000..a2333dfe6 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_4.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_5.png b/mods/MAPGEN/mcl_villages/textures/grain_5.png new file mode 100644 index 000000000..08387a8c5 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_5.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_6.png b/mods/MAPGEN/mcl_villages/textures/grain_6.png new file mode 100644 index 000000000..22d3f02c3 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_6.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_7.png b/mods/MAPGEN/mcl_villages/textures/grain_7.png new file mode 100644 index 000000000..98c6d258c Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_7.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/grain_8.png b/mods/MAPGEN/mcl_villages/textures/grain_8.png new file mode 100644 index 000000000..81675cf3a Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/grain_8.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_1.png b/mods/MAPGEN/mcl_villages/textures/root_1.png new file mode 100644 index 000000000..d4f100217 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_1.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_2.png b/mods/MAPGEN/mcl_villages/textures/root_2.png new file mode 100644 index 000000000..366b917ea Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_2.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_3.png b/mods/MAPGEN/mcl_villages/textures/root_3.png new file mode 100644 index 000000000..de2163622 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_3.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_4.png b/mods/MAPGEN/mcl_villages/textures/root_4.png new file mode 100644 index 000000000..828b9b328 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_4.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_5.png b/mods/MAPGEN/mcl_villages/textures/root_5.png new file mode 100644 index 000000000..a7b972e29 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_5.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_6.png b/mods/MAPGEN/mcl_villages/textures/root_6.png new file mode 100644 index 000000000..2c04854a2 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_6.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_7.png b/mods/MAPGEN/mcl_villages/textures/root_7.png new file mode 100644 index 000000000..55a7edb65 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_7.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/root_8.png b/mods/MAPGEN/mcl_villages/textures/root_8.png new file mode 100644 index 000000000..239d7b420 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/root_8.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/src/farming_pumpkin_side_small.png b/mods/MAPGEN/mcl_villages/textures/src/farming_pumpkin_side_small.png new file mode 100644 index 000000000..e367174a7 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/src/farming_pumpkin_side_small.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/src/gen_images.sh b/mods/MAPGEN/mcl_villages/textures/src/gen_images.sh new file mode 100755 index 000000000..b38225312 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/textures/src/gen_images.sh @@ -0,0 +1,20 @@ +tiles=( + '../../../ITEMS/mcl_wool/textures/wool_white.png' + '../../../ITEMS/mcl_wool/textures/wool_pink.png' + '../../../ITEMS/mcl_wool/textures/wool_red.png' + '../../../ITEMS/mcl_wool/textures/wool_black.png' + '../../../ITEMS/mcl_wool/textures/wool_brown.png' + '../../../ITEMS/mcl_wool/textures/wool_grey.png' + '../../../ITEMS/mcl_wool/textures/mcl_wool_light_blue.png' + '../../../ITEMS/mcl_wool/textures/mcl_wool_lime.png' +) + +for (( i = 1; i <= ${#tiles[@]}; i++ )); do + tile=${tiles[$i - 1]} + composite ../../../ITEMS/mcl_farming/textures/mcl_farming_wheat_stage_7.png ${tile} grain_${i}.png; + composite ../../../ITEMS/mcl_farming/textures/farming_carrot.png ${tile} root_${i}.png; + composite src/farming_pumpkin_side_small.png ${tile} gourd_${i}.png; + composite ../../../ITEMS/mcl_farming/textures/mcl_farming_sweet_berry_bush_0.png ${tile} bush_${i}.png; + composite ../../../ITEMS/mcl_flowers/textures/flowers_tulip.png ${tile} flower_${i}.png; + composite ../../../ITEMS/mcl_core/textures/default_sapling.png ${tile} tree_${i}.png; +done diff --git a/mods/MAPGEN/mcl_villages/textures/tree_1.png b/mods/MAPGEN/mcl_villages/textures/tree_1.png new file mode 100644 index 000000000..4946d3a93 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_1.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_2.png b/mods/MAPGEN/mcl_villages/textures/tree_2.png new file mode 100644 index 000000000..727ea837d Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_2.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_3.png b/mods/MAPGEN/mcl_villages/textures/tree_3.png new file mode 100644 index 000000000..0513e71ff Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_3.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_4.png b/mods/MAPGEN/mcl_villages/textures/tree_4.png new file mode 100644 index 000000000..c50dddc3e Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_4.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_5.png b/mods/MAPGEN/mcl_villages/textures/tree_5.png new file mode 100644 index 000000000..3ab6ad8c4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_5.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_6.png b/mods/MAPGEN/mcl_villages/textures/tree_6.png new file mode 100644 index 000000000..9e5b839c5 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_6.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_7.png b/mods/MAPGEN/mcl_villages/textures/tree_7.png new file mode 100644 index 000000000..d23802c9f Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_7.png differ diff --git a/mods/MAPGEN/mcl_villages/textures/tree_8.png b/mods/MAPGEN/mcl_villages/textures/tree_8.png new file mode 100644 index 000000000..51ee9fe66 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/textures/tree_8.png differ diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua index e56ce8505..e0ca75422 100644 --- a/mods/MAPGEN/mcl_villages/utils.lua +++ b/mods/MAPGEN/mcl_villages/utils.lua @@ -19,20 +19,19 @@ local function is_above_surface(name) string.find(name,"flower") or string.find(name,"bush") end -local get_node = mcl_vars.get_node -function mcl_villages.find_surface_down(pos, surface_node) +function mcl_villages.find_surface_down(lvm, pos, surface_node) local p6 = vector.new(pos) - surface_node = surface_node or get_node(p6) --, true, 1000000) + surface_node = surface_node or lvm:get_node_at(p6) if not surface_node then return end for y = p6.y - 1, math.max(0,p6.y - 120), -1 do p6.y = y local top_node = surface_node - surface_node = get_node(p6) + surface_node = lvm:get_node_at(p6) if not surface_node then return nil end if is_above_surface(top_node.name) then if mcl_villages.surface_mat[surface_node.name] then -- minetest.log("verbose", "Found "..surface_node.name.." below "..top_node.name) - return p6, surface_node.name + return p6, surface_node end else local ndef = minetest.registered_nodes[surface_node.name] @@ -42,19 +41,19 @@ function mcl_villages.find_surface_down(pos, surface_node) end end end -function mcl_villages.find_surface_up(pos, surface_node) +function mcl_villages.find_surface_up(lvm, pos, surface_node) local p6 = vector.new(pos) - surface_node = surface_node or get_node(p6) --, true, 1000000) + surface_node = surface_node or lvm:get_node_at(p6) --, true, 1000000) if not surface_node then return end for y = p6.y + 1, p6.y + 50 do p6.y = y - local top_node = get_node(p6) + local top_node = lvm:get_node_at(p6) if not top_node then return nil end if is_above_surface(top_node.name) then if mcl_villages.surface_mat[surface_node.name] then -- minetest.log("verbose","Found "..surface_node.name.." below "..top_node.name) p6.y = p6.y - 1 - return p6, surface_node.name + return p6, surface_node end else local ndef = minetest.registered_nodes[surface_node.name] @@ -69,31 +68,24 @@ end -- function to find surface block y coordinate -- returns surface postion ------------------------------------------------------------------------------- -function mcl_villages.find_surface(pos, wait) +function mcl_villages.find_surface(lvm, pos) local p6 = vector.new(pos) if p6.y < 0 then p6.y = 0 end -- start at water level - local surface_node - if wait then - surface_node = get_node(p6, true, 10000000) - else - surface_node = get_node(p6) - end + local surface_node = lvm:get_node_at(p6) -- downward, if starting position is empty if is_above_surface(surface_node.name) then - return mcl_villages.find_surface_down(p6, surface_node) + return mcl_villages.find_surface_down(lvm, p6, surface_node) else - return mcl_villages.find_surface_up(p6, surface_node) + return mcl_villages.find_surface_up(lvm, p6, surface_node) end end -------------------------------------------------------------------------------- --- check distance for new building, use maximum norm -------------------------------------------------------------------------------- -function mcl_villages.check_distance(settlement_info, building_pos, building_size) - for i, built_house in ipairs(settlement_info) do - local dx, dz = building_pos.x - built_house["pos"].x, building_pos.z - built_house["pos"].z - --local d = math.sqrt(dx*dx+dz*dz) - --if 2 * d < building_size + built_house["hsize"] then return false end - if math.max(math.abs(dx), math.abs(dz)) * 2 - 8 <= building_size + built_house["hsize"] then return false end +-- check the minimum distance of two squares, on axes +function mcl_villages.check_distance(settlement, cpos, sizex, sizez, limit) + for i, building in ipairs(settlement) do + local opos, osizex, osizez = building.pos, building.size.x, building.size.z + local dx = math.abs(cpos.x - opos.x) - (sizex + osizex) * 0.5 + local dz = math.abs(cpos.z - opos.z) - (sizez + osizez) * 0.5 + if math.max(dx, dz) < limit then return false end end return true end diff --git a/settingtypes.txt b/settingtypes.txt index 7b110b8ba..966068b7c 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -46,7 +46,7 @@ mcl_disabled_structures (Disabled structures) string mcl_disabled_events (Disabled events) string # Amount of village to generate -mcl_villages_village_chance (Amount of villages) int 5 0 100 +mcl_villages_village_probability (Probability of villages) int 5 0 100 [Players] # If enabled, players respawn at the bed they last lay on instead of normal