1
0
Fork 0

more voxelmanipulator, more MCLA

This commit is contained in:
kno10 2024-07-23 01:09:17 +02:00
parent 8e94eca3be
commit 5b520d8713
61 changed files with 1847 additions and 542 deletions

View File

@ -2328,6 +2328,16 @@ mcl_mobs.register_mob("mobs_mc:villager", {
end,
})
-- HACK: for compatibility with the new mcl_villages code, but will not allow easy modding yet
mobs_mc.jobsites = {}
for _,p in pairs(professions) do
if p.jobsite then
table.insert(mobs_mc.jobsites, p.jobsite)
end
end
function villager_employ(v, jobsite_pos)
if jobsite_pos then employ(v, jobsite_pos) end
end
--[[
Villager spawning in mcl_villages

View File

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

View File

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

View File

@ -0,0 +1,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

View File

@ -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
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")
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
local l=v:get_luaentity()
l._bed = bed
m:set_string("villager",l._id)
end
v._home = pos
end
end
end
local function fix_village_water(minp,maxp)
local palettenodes = minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20), "group:water_palette")
for _, palettenodepos in pairs(palettenodes) do
local palettenode = minetest.get_node(palettenodepos)
minetest.set_node(palettenodepos, {name = palettenode.name})
end
end
local function init_nodes(p1, p2, pr)
--[[for _, n in pairs(minetest.find_nodes_in_area(p1, p2, { "group:wall" })) do
mcl_walls.update_wall(n)
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
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
r = r + pr:next(2,4)
if r > lastr + 25 then -- too disconnected
break
end
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
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
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))()
function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
local settlement = {}
-- 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)
-- 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)
-- 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_all_info["name"] == "belltower" then
spawn_iron_golem(pos)
end
spawn_villagers(pos,p2)
fix_village_water(pos,p2)
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
end
end

View File

@ -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,12 +79,12 @@ mcl_villages.biome_map = {
CherryGrove = "cherry",
-- no change, but try to convert MCLA material
-- no change
--FlowerForest = "oak",
--Forest = "oak",
-- MushroomIsland = "oak",
--MushroomIsland = "",
--Plains = "oak",
-- StoneBeach = "oak",
--StoneBeach = "",
--SunflowerPlains = "oak",
--Swampland = "oak",
}
@ -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"' },

View File

@ -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)
--local lvm, emin, emax = minetest.get_mapgen_object("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
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
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)
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 platform_mat = foundation_materials[surface_mat] or "mcl_core:dirt"
--local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip")
--local lvm = VoxelManip()
-- 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"})
-- 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
--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
end
end
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
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
-- 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
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 = 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)
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 = 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)
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

View File

@ -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

View File

@ -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
-- 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
-- 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
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
if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then
minetest.swap_node(surface_point,{name="mcl_core:sandstonesmooth2"})
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
minetest.swap_node(surface_point,{name="mcl_core:grass_path"})
place_lamp(npos, pr)
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
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

View File

@ -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

View File

@ -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