more voxelmanipulator, more MCLA
|
@ -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
|
||||
|
|
|
@ -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|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -1,128 +1,29 @@
|
|||
local min_jobs = tonumber(minetest.settings:get("mcl_villages_min_jobs")) or 1
|
||||
local max_jobs = tonumber(minetest.settings:get("mcl_villages_max_jobs")) or 12
|
||||
local placement_priority = minetest.settings:get("mcl_villages_placement_priority") or "random"
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- initialize settlement_info
|
||||
-------------------------------------------------------------------------------
|
||||
function mcl_villages.initialize_settlement_info(pr)
|
||||
local count_buildings = {}
|
||||
local count_buildings = {
|
||||
number_of_jobs = pr:next(min_jobs, max_jobs),
|
||||
num_jobs = 0,
|
||||
num_beds = 0,
|
||||
}
|
||||
|
||||
-- count_buildings table reset
|
||||
for k,v in pairs(mcl_villages.schematic_table) do
|
||||
for k, v in pairs(mcl_villages.schematic_houses) do
|
||||
count_buildings[v["name"]] = 0
|
||||
end
|
||||
for k, v in pairs(mcl_villages.schematic_jobs) do
|
||||
count_buildings[v["name"]] = 0
|
||||
end
|
||||
|
||||
-- randomize number of buildings
|
||||
local number_of_buildings = pr:next(10, 25)
|
||||
local number_built = 0
|
||||
mcl_villages.debug("Village ".. number_of_buildings)
|
||||
|
||||
return count_buildings, number_of_buildings, number_built
|
||||
return count_buildings
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- check ground for a single building
|
||||
-------------------------------------------------------------------------------
|
||||
local function try_place_building(minp, maxp, pos_surface, building_all_info, rotation, settlement_info, pr)
|
||||
local fwidth, fdepth = building_all_info["hwidth"] or 5, building_all_info["hdepth"] or 5
|
||||
if rotation == "90" or rotation == "270" then fwidth, fdepth = fdepth, fwidth end
|
||||
local fheight = building_all_info["hheight"] or 5
|
||||
-- use building centers for better placement
|
||||
pos_surface.x = pos_surface.x - math.ceil(fwidth / 2)
|
||||
pos_surface.z = pos_surface.z - math.ceil(fdepth / 2)
|
||||
-- ensure we have 3 space for terraforming
|
||||
if pos_surface.x - 3 < minp.x or pos_surface.z + 3 < minp.z or pos_surface.x + fwidth + 3 > maxp.x or pos_surface.z + fheight + 3 > maxp.z then return nil end
|
||||
-- to find the y position, also check the corners
|
||||
local ys = {pos_surface.y}
|
||||
local pos_c
|
||||
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x, pos_surface.y+fheight, pos_surface.z))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x+fwidth-1, pos_surface.y+fheight, pos_surface.z))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x, pos_surface.y+fheight, pos_surface.z+fdepth-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x+fwidth-1, pos_surface.y+fheight, pos_surface.z+fdepth-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
table.sort(ys)
|
||||
-- well supported base, not too uneven?
|
||||
if #ys < 5 or ys[#ys]-ys[1] > fheight + 3 then return nil end
|
||||
pos_surface.y = 0.5 * (ys[math.floor(#ys/2)] + ys[math.ceil(#ys/2)]) -- median
|
||||
-- check distance to other buildings
|
||||
if not mcl_villages.check_distance(settlement_info, pos_surface, math.max(fheight, fdepth)) then return nil end
|
||||
return pos_surface
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- fill settlement_info
|
||||
--------------------------------------------------------------------------------
|
||||
function mcl_villages.create_site_plan(minp, maxp, pr)
|
||||
local center = vector.new(math.floor((minp.x+maxp.x)/2),maxp.y,math.floor((minp.z+maxp.z)/2))
|
||||
minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(center))
|
||||
local possible_rotations = {"0", "90", "180", "270"}
|
||||
local center_surface
|
||||
|
||||
local count_buildings, number_of_buildings, number_built = mcl_villages.initialize_settlement_info(pr)
|
||||
local settlement_info = {}
|
||||
-- now some buildings around in a circle, radius = size of town center
|
||||
local x, y, z, r = center.x, maxp.y, center.z, 0
|
||||
-- draw j circles around center and increase radius by math.random(2,5)
|
||||
for j = 1,15 do
|
||||
for a = 0, 23, 1 do
|
||||
local angle = a * 71 / 24 * math.pi * 2 -- prime to increase randomness
|
||||
local pos1 = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5))
|
||||
local pos_surface, surface_material = mcl_villages.find_surface(pos1, false)
|
||||
if pos_surface then
|
||||
local randomized_schematic_table = mcl_villages.shuffle(mcl_villages.schematic_table, pr)
|
||||
if #settlement_info == 0 then randomized_schematic_table = { mcl_villages.schematic_table[1] } end -- place town bell first
|
||||
-- pick schematic
|
||||
local size = #randomized_schematic_table
|
||||
for i = 1, #randomized_schematic_table do
|
||||
local building_all_info = randomized_schematic_table[i]
|
||||
-- already enough buildings of that type?
|
||||
if count_buildings[building_all_info["name"]] < building_all_info["max_num"]*number_of_buildings
|
||||
and (r >= 25 or not string.find(building_all_info["name"], "lamp")) then -- no lamps in the center
|
||||
local rotation = possible_rotations[pr:next(1, #possible_rotations)]
|
||||
local pos = try_place_building(minp, maxp, pos_surface, building_all_info, rotation, settlement_info, pr)
|
||||
if pos then
|
||||
if #settlement_info == 0 then -- town bell
|
||||
center_surface, y = pos, pos.y + max_height_difference * 0.5 + 1
|
||||
end
|
||||
-- limit height differences to town center
|
||||
if math.abs(pos.y - center_surface.y) > max_height_difference * 0.5 then
|
||||
break -- other buildings likely will not fit either
|
||||
end
|
||||
count_buildings[building_all_info["name"]] = (count_buildings[building_all_info["name"]] or 0) + 1
|
||||
number_built = number_built + 1
|
||||
|
||||
pos.y = pos.y + (building_all_info["yadjust"] or 0)
|
||||
table.insert(settlement_info, {
|
||||
pos = pos,
|
||||
name = building_all_info["name"],
|
||||
hsize = math.max(building_all_info["hwidth"], building_all_info["hdepth"]), -- ,building_all_info["hsize"],
|
||||
rotat = rotation,
|
||||
surface_mat = surface_material
|
||||
})
|
||||
-- minetest.log("action", "[mcl_villages] Placing "..building_all_info["name"].." at "..minetest.pos_to_string(pos))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if number_of_buildings == number_built then
|
||||
break
|
||||
end
|
||||
end
|
||||
if r == 0 then break end -- no angles in the very center
|
||||
end
|
||||
if number_built >= number_of_buildings then
|
||||
break
|
||||
end
|
||||
r = r + pr:next(2,5)
|
||||
end
|
||||
mcl_villages.debug("really ".. number_built)
|
||||
if number_built < 7 then
|
||||
minetest.log("action", "[mcl_villages] Bad village location, could only place "..number_built.." buildings.")
|
||||
return
|
||||
end
|
||||
minetest.log("action", "[mcl_villages] village plan completed at " .. minetest.pos_to_string(center))
|
||||
--minetest.log("[mcl_villages] village plan completed at " .. minetest.pos_to_string(center)) -- for debugging only
|
||||
return settlement_info
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- evaluate settlement_info and place schematics
|
||||
-------------------------------------------------------------------------------
|
||||
|
@ -139,130 +40,401 @@ local function construct_node(p1, p2, name)
|
|||
return nodes
|
||||
end
|
||||
|
||||
local function spawn_iron_golem(pos)
|
||||
--minetest.log("action", "[mcl_villages] Attempt to spawn iron golem.")
|
||||
local p = minetest.find_node_near(pos,50,"mcl_core:grass_path")
|
||||
if p then
|
||||
p.y = p.y + 1
|
||||
local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity()
|
||||
if l then
|
||||
l._home = p
|
||||
local function spawn_cats(pos)
|
||||
local sp=minetest.find_nodes_in_area_under_air(vector.offset(pos,-20,-20,-20),vector.offset(pos,20,20,20),{"group:opaque"})
|
||||
for i=1,math.random(3) do
|
||||
local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat"):get_luaentity()
|
||||
if v then
|
||||
v._home = pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function spawn_villagers(minp,maxp)
|
||||
--minetest.log("action", "[mcl_villages] Attempt to spawn villagers.")
|
||||
local beds=minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20),{"mcl_beds:bed_red_bottom"})
|
||||
for _,bed in pairs(beds) do
|
||||
local m = minetest.get_meta(bed)
|
||||
if m:get_string("villager") == "" then
|
||||
local v=minetest.add_entity(bed,"mobs_mc:villager")
|
||||
if v then
|
||||
local l=v:get_luaentity()
|
||||
l._bed = bed
|
||||
m:set_string("villager",l._id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local function init_nodes(p1, p2, pr)
|
||||
--[[for _, n in pairs(minetest.find_nodes_in_area(p1, p2, { "group:wall" })) do
|
||||
mcl_walls.update_wall(n)
|
||||
end]]--
|
||||
|
||||
local function fix_village_water(minp,maxp)
|
||||
local palettenodes = minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20), "group:water_palette")
|
||||
for _, palettenodepos in pairs(palettenodes) do
|
||||
local palettenode = minetest.get_node(palettenodepos)
|
||||
minetest.set_node(palettenodepos, {name = palettenode.name})
|
||||
end
|
||||
end
|
||||
|
||||
local function init_nodes(p1, p2, size, rotation, pr)
|
||||
construct_node(p1, p2, "mcl_itemframes:item_frame")
|
||||
construct_node(p1, p2, "mcl_itemframes:glow_item_frame")
|
||||
construct_node(p1, p2, "mcl_furnaces:furnace")
|
||||
construct_node(p1, p2, "mcl_anvils:anvil")
|
||||
|
||||
construct_node(p1, p2, "mcl_smoker:smoker")
|
||||
construct_node(p1, p2, "mcl_barrels:barrel_closed")
|
||||
construct_node(p1, p2, "mcl_blast_furnace:blast_furnace")
|
||||
construct_node(p1, p2, "mcl_brewing:stand_000")
|
||||
local nodes = construct_node(p1, p2, "mcl_chests:chest")
|
||||
if nodes and #nodes > 0 then
|
||||
for p=1, #nodes do
|
||||
local pos = nodes[p]
|
||||
mcl_villages.fill_chest(pos, pr)
|
||||
end
|
||||
construct_node(p1, p2, "mcl_books:bookshelf")
|
||||
construct_node(p1, p2, "mcl_armor_stand:armor_stand")
|
||||
--construct_node(p1, p2, "mcl_smoker:smoker")
|
||||
--construct_node(p1, p2, "mcl_barrels:barrel_closed")
|
||||
--construct_node(p1, p2, "mcl_blast_furnace:blast_furnace")
|
||||
--construct_node(p1, p2, "mcl_brewing:stand_000")
|
||||
|
||||
-- Support mods with custom job sites
|
||||
local job_sites = minetest.find_nodes_in_area(p1, p2, mobs_mc.jobsites)
|
||||
for _, v in pairs(job_sites) do
|
||||
mcl_structures.init_node_construct(v)
|
||||
end
|
||||
|
||||
-- Do new chest nodes first
|
||||
local nodes = construct_node(p1, p2, "mcl_chests:chest_small")
|
||||
if nodes and #nodes > 0 then
|
||||
for p=1, #nodes do
|
||||
local pos = nodes[p]
|
||||
mcl_villages.fill_chest(pos, pr)
|
||||
mcl_villages.fill_chest(nodes[p], pr)
|
||||
end
|
||||
end
|
||||
|
||||
-- Do old chest nodes after
|
||||
local nodes = construct_node(p1, p2, "mcl_chests:chest")
|
||||
if nodes and #nodes > 0 then
|
||||
for p=1, #nodes do
|
||||
mcl_villages.fill_chest(nodes[p], pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_villages.place_schematics(settlement_info, pr)
|
||||
local building_all_info
|
||||
local lvm = VoxelManip()
|
||||
-- check ground for a single building, adjust position
|
||||
local function check_ground(lvm, cpos, size)
|
||||
local cpos, surface_material = mcl_villages.find_surface(lvm, cpos)
|
||||
if not cpos then return nil, nil end
|
||||
local pos = vector.offset(cpos, -math.floor(size.x/2), 0, -math.floor(size.z/2))
|
||||
local ys = {pos.y}
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, 0))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, 0))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, size.z-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, size.z-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
table.sort(ys)
|
||||
-- well supported base, not too uneven?
|
||||
if #ys < 5 or ys[#ys]-ys[1] > 6 then return nil, nil end
|
||||
cpos.y = math.floor(0.5 * (ys[math.floor(#ys/2)] + ys[math.ceil(#ys/2)]) + 0.5) -- median, rounded
|
||||
return cpos, surface_material
|
||||
end
|
||||
|
||||
for i, built_house in ipairs(settlement_info) do
|
||||
local is_last = i == #settlement_info
|
||||
local function add_building(settlement, building, count_buildings)
|
||||
table.insert(settlement, building)
|
||||
count_buildings[building["name"]] = count_buildings[building["name"]] + 1
|
||||
count_buildings.num_jobs = count_buildings.num_jobs + (building["num_jobs"] or 0)
|
||||
count_buildings.num_beds = count_buildings.num_beds + (building["num_beds"] or 0)
|
||||
end
|
||||
|
||||
for j, schem in ipairs(mcl_villages.schematic_table) do
|
||||
if settlement_info[i]["name"] == schem["name"] then
|
||||
building_all_info = schem
|
||||
break
|
||||
local function layout_town(lvm, minp, maxp, pr, input_settlement)
|
||||
local center = vector.new(pr:next(minp.x + 24, maxp.x - 24), maxp.y, pr:next(minp.z + 24, maxp.z - 24))
|
||||
minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(center))
|
||||
local possible_rotations = {"0", "90", "180", "270"}
|
||||
local center_surface
|
||||
|
||||
local settlement = {}
|
||||
-- now some buildings around in a circle, radius = size of town center
|
||||
local x, y, z, r, lastr = center.x, maxp.y, center.z, 0, 99
|
||||
local mindist = 4
|
||||
if #input_settlement >= 12 then mindist = 3 end
|
||||
-- draw j circles around center and increase radius by math.random(2,4)
|
||||
for j = 1,20 do
|
||||
local steps = math.min(math.floor(math.pi * 2 * r / 2), 30) -- try up to 30 angles
|
||||
for a = 0, steps - 1 do
|
||||
if #settlement == #input_settlement then break end -- everything placed
|
||||
local angle = a * 71 / steps * math.pi * 2 -- prime to increase randomness
|
||||
local cpos = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5))
|
||||
local building = table.copy(input_settlement[#settlement + 1])
|
||||
local size = vector.copy(building.size)
|
||||
--local rotation = possible_rotations[pr:next(1, #possible_rotations)]
|
||||
local rotation = math.floor(math.atan2(center.x-cpos.x, center.z-cpos.z) / math.pi * 2+4.5)%4
|
||||
local rotation = possible_rotations[1+rotation]
|
||||
if rotation == "90" or rotation == "270" then size.x, size.z = size.z, size.x end
|
||||
local tlpos = vector.offset(cpos, -math.floor(size.x / 2), 0, -math.floor(size.z / 2))
|
||||
|
||||
-- ensure we have 3 space for terraforming, and avoid problems with VoxelManip
|
||||
if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x
|
||||
and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then
|
||||
local pos, surface_material = check_ground(lvm, cpos, size)
|
||||
-- check distance to other buildings. Note that we still want to add baseplates etc.
|
||||
if pos and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then
|
||||
-- use town bell as new reference point for placement height
|
||||
if #settlement == 0 then
|
||||
center_surface, y = cpos, pos.y + mcl_villages.max_height_difference * 0.5 + 1
|
||||
end
|
||||
-- limit height differences to town center, but gradually
|
||||
if math.abs(pos.y - center_surface.y) <= mcl_villages.max_height_difference * (0.3 + math.min(r/30,0.5)) then
|
||||
local minp = vector.offset(pos, -math.floor(size.x/2), building.yadjust, -math.floor(size.z/2))
|
||||
building.minp = minp
|
||||
building.maxp = vector.offset(minp, size.x, size.y, size.z)
|
||||
building.pos = pos
|
||||
building.size = size
|
||||
building.rotation = rotation
|
||||
building.surface_mat = surface_material
|
||||
table.insert(settlement, building)
|
||||
-- minetest.log("verbose", "[mcl_villages] Placing "..schema["name"].." at "..minetest.pos_to_string(pos))
|
||||
lastr = r
|
||||
--else
|
||||
-- minetest.log("Height difference "..math.abs(pos.y - center_surface.y))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local pos = settlement_info[i]["pos"]
|
||||
local rotation = settlement_info[i]["rotat"]
|
||||
-- get building node material for better integration to surrounding
|
||||
local surface_material = settlement_info[i]["surface_mat"] or "mcl_core:stone"
|
||||
local platform_material = surface_material
|
||||
local schem_lua = building_all_info["schem_lua"]
|
||||
if not schem_lua then
|
||||
schem_lua = minetest.serialize_schematic(building_all_info["mts"], "lua", { lua_use_comments = false, lua_num_indent_spaces = 0 }) .. " return schematic"
|
||||
-- MCLA node names to VL for import
|
||||
for _, sub in pairs(mcl_villages.mcla_to_vl) do
|
||||
schem_lua = schem_lua:gsub(sub[1], sub[2])
|
||||
end
|
||||
if not schem_lua then error("schema failed to load "..building_all_info["name"]) end
|
||||
local schematic = loadstring(schem_lua)
|
||||
if not schematic then error("schema failed to load "..building_all_info["name"].." "..schem_lua) end
|
||||
schematic = schematic() -- evaluate
|
||||
if schematic.size["x"] ~= building_all_info["hwidth"] or schematic.size["y"] ~= building_all_info["hheight"] or schematic.size["z"] ~= building_all_info["hdepth"] then
|
||||
minetest.log("warning", "[mcl_villages] schematic size differs: "..building_all_info["name"].." width "..schematic.size["x"].." height "..schematic.size["y"].." depth "..schematic.size["z"])
|
||||
end
|
||||
building_all_info["schem_lua"] = schem_lua
|
||||
r = r + pr:next(2,4)
|
||||
if r > lastr + 25 then -- too disconnected
|
||||
break
|
||||
end
|
||||
schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material..'"')
|
||||
schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material..'"')
|
||||
local schematic = loadstring(mcl_villages.substitute_materials(pos, schem_lua, pr))()
|
||||
end
|
||||
-- minetest.log("verbose", "Planned "..#input_settlement.." buildings, placed "..#settlement)
|
||||
if #settlement < #input_settlement and #settlement < 6 then
|
||||
minetest.log("action", "[mcl_villages] Bad village location, could only place "..#settlement.." buildings.")
|
||||
return
|
||||
end
|
||||
minetest.log("action", "[mcl_villages] village plan completed at " .. minetest.pos_to_string(center))
|
||||
return settlement
|
||||
end
|
||||
|
||||
-- already built the foundation for the building and made room above
|
||||
local sx, sy, sz = schematic.size.x, schematic.size.y, schematic.size.z
|
||||
if rotation == "90" or rotation == "270" then sx, sz = sz, sx end
|
||||
local p2 = vector.new(pos.x+sx-1,pos.y+sy-1,pos.z+sz-1)
|
||||
lvm:read_from_map(vector.new(pos.x-3, pos.y-40, pos.z-3), vector.new(pos.x+sx+3, pos.y+sy+40, pos.z+sz+3)) -- safety margins for foundation
|
||||
lvm:get_data()
|
||||
-- TODO: make configurable as in MCLA
|
||||
mcl_villages.foundation(lvm, pos, sx, sy, sz, surface_material, pr)
|
||||
function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
|
||||
local settlement = {}
|
||||
|
||||
-- initialize all settlement_info table
|
||||
local count_buildings = mcl_villages.initialize_settlement_info(pr)
|
||||
|
||||
-- first building is townhall in the center
|
||||
local bindex = pr:next(1, #mcl_villages.schematic_bells)
|
||||
local bell_info = table.copy(mcl_villages.schematic_bells[bindex])
|
||||
|
||||
if mcl_villages.mandatory_buildings['jobs'] then
|
||||
for _, bld_name in pairs(mcl_villages.mandatory_buildings['jobs']) do
|
||||
local building_info = info_for_building(bld_name, mcl_villages.schematic_jobs)
|
||||
add_building(settlement, building_info, count_buildings)
|
||||
end
|
||||
end
|
||||
|
||||
while count_buildings.num_jobs < count_buildings.number_of_jobs do
|
||||
local rindex = pr:next(1, #mcl_villages.schematic_jobs)
|
||||
local building_info = mcl_villages.schematic_jobs[rindex]
|
||||
|
||||
if
|
||||
(building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"])
|
||||
and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"])
|
||||
and (
|
||||
building_info["num_others"] == nil
|
||||
or count_buildings[building_info["name"]] == 0
|
||||
or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs
|
||||
)
|
||||
then
|
||||
add_building(settlement, building_info, count_buildings)
|
||||
end
|
||||
end
|
||||
|
||||
if mcl_villages.mandatory_buildings['houses'] then
|
||||
for _, bld_name in pairs(mcl_villages.mandatory_buildings['houses']) do
|
||||
local building_info = info_for_building(bld_name, mcl_villages.schematic_houses)
|
||||
add_building(settlement, building_info, count_buildings)
|
||||
end
|
||||
end
|
||||
|
||||
while count_buildings.num_beds <= count_buildings.num_jobs do
|
||||
local rindex = pr:next(1, #mcl_villages.schematic_houses)
|
||||
local building_info = mcl_villages.schematic_houses[rindex]
|
||||
|
||||
if
|
||||
(building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"])
|
||||
and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"])
|
||||
and (
|
||||
building_info["num_others"] == nil
|
||||
or count_buildings[building_info["name"]] == 0
|
||||
or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs
|
||||
)
|
||||
then
|
||||
add_building(settlement, building_info, count_buildings)
|
||||
end
|
||||
end
|
||||
|
||||
-- Based on number of villagers
|
||||
local num_wells = pr:next(1, math.ceil(count_buildings.num_beds / 10))
|
||||
for i = 1, num_wells do
|
||||
local windex = pr:next(1, #mcl_villages.schematic_wells)
|
||||
local cur_schem = table.copy(mcl_villages.schematic_wells[windex])
|
||||
table.insert(settlement, pr:next(1, #settlement), cur_schem)
|
||||
end
|
||||
|
||||
if placement_priority == "jobs" then
|
||||
-- keep ordered as is
|
||||
elseif placement_priority == "houses" then
|
||||
table.reverse(settlement)
|
||||
else
|
||||
settlement = mcl_villages.shuffle(settlement, pr)
|
||||
end
|
||||
|
||||
table.insert(settlement, 1, bell_info)
|
||||
return layout_town(lvm, minp, maxp, pr, settlement)
|
||||
end
|
||||
|
||||
|
||||
function mcl_villages.place_schematics(lvm, settlement, blockseed, pr)
|
||||
-- local lvm = VoxelManip()
|
||||
local bell_pos = vector.offset(settlement[1].minp, math.floor(settlement[1].size.x/2), 0, math.floor(settlement[1].size.z/2))
|
||||
local bell_center_pos
|
||||
local bell_center_node_type
|
||||
|
||||
for i, building in ipairs(settlement) do
|
||||
local minp, cpos, maxp, size, rotation = building.minp, building.pos, building.maxp, building.size, building.rotation
|
||||
|
||||
-- adjust the schema to match location and biome
|
||||
local surface_material = building.surface_mat or {name = "mcl_core:dirt" }
|
||||
local platform_material = building.platform_mat or building.surface_mat or {name = "mcl_core:stone" }
|
||||
local schem_lua = building.schem_lua
|
||||
schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material.name..'"') -- also keeping param2 would be nicer, grass color
|
||||
schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material.name..'"')
|
||||
schem_lua = mcl_villages.substitute_materials(cpos, schem_lua, pr)
|
||||
local schematic = loadstring(schem_lua)()
|
||||
|
||||
-- the foundation and air space for the building was already built before
|
||||
-- lvm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z))
|
||||
-- lvm:get_data()
|
||||
-- now added in placement code already, pos has the primary height if (building.yadjust or 0) ~= 0 then minp = vector.offset(minp, 0, building.yadjust, 0) end
|
||||
-- minetest.log("debug", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material)
|
||||
minetest.place_schematic_on_vmanip(
|
||||
lvm,
|
||||
pos,
|
||||
minp,
|
||||
schematic,
|
||||
rotation,
|
||||
nil,
|
||||
true,
|
||||
{ place_center_x = false, place_center_y = false, place_center_z = false }
|
||||
)
|
||||
lvm:write_to_map(true) -- FIXME: postpone
|
||||
init_nodes(pos, p2, schematic.size, rotation, pr)
|
||||
|
||||
if building_all_info["name"] == "belltower" then
|
||||
spawn_iron_golem(pos)
|
||||
-- to help pathing, increase the height of no_path areas
|
||||
local p = vector.zero()
|
||||
for z = minp.z,maxp.z do
|
||||
p.z = z
|
||||
for x = minp.x,maxp.x do
|
||||
p.x = x
|
||||
for y = minp.y,maxp.y-1 do
|
||||
p.y = y
|
||||
local n = lvm:get_node_at(p)
|
||||
if n and n.name == "mcl_villages:no_paths" then
|
||||
p.y = y+1
|
||||
n = lvm:get_node_at(p)
|
||||
if n and n.name == "air" then
|
||||
lvm:set_node_at(p, {name="mcl_villages:no_paths"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
mcl_villages.store_path_ends(lvm, minp, maxp, cpos, blockseed, bell_pos)
|
||||
|
||||
if building.name == "belltower" then -- TODO: allow multiple types?
|
||||
bell_center_pos = cpos
|
||||
local center_node = lvm:get_node_at(cpos)
|
||||
bell_center_node_type = center_node.name
|
||||
end
|
||||
end
|
||||
|
||||
lvm:write_to_map(true) -- for path finder and light
|
||||
|
||||
local biome_data = minetest.get_biome_data(bell_pos)
|
||||
local biome_name = minetest.get_biome_name(biome_data.biome)
|
||||
mcl_villages.paths(blockseed, biome_name)
|
||||
|
||||
-- this will run delayed actions, such as spawning mobs
|
||||
minetest.set_node(bell_center_pos, { name = "mcl_villages:village_block" })
|
||||
local meta = minetest.get_meta(bell_center_pos)
|
||||
meta:set_string("blockseed", blockseed)
|
||||
meta:set_string("node_type", bell_center_node_type)
|
||||
meta:set_string("infotext", S("The timer for this @1 has not run yet!", bell_center_node_type))
|
||||
minetest.get_node_timer(bell_center_pos):start(1.0)
|
||||
|
||||
for i, building in ipairs(settlement) do
|
||||
init_nodes(vector.offset(building.minp,-2,-2,-2), vector.offset(building.maxp,2,2,2), pr)
|
||||
end
|
||||
|
||||
-- read back any changes
|
||||
local emin, emax = lvm:get_emerged_area()
|
||||
lvm:read_from_map(emin, emax)
|
||||
end
|
||||
|
||||
function mcl_villages.post_process_village(blockseed)
|
||||
local village_info = mcl_villages.get_village(blockseed)
|
||||
if not village_info then
|
||||
return
|
||||
end
|
||||
-- minetest.log("Postprocessing village")
|
||||
|
||||
local settlement_info = village_info.data
|
||||
local jobs = {}
|
||||
local beds = {}
|
||||
|
||||
local bell_pos = vector.copy(settlement_info[1]["pos"])
|
||||
local bell = vector.offset(bell_pos, 0, 2, 0)
|
||||
local biome_data = minetest.get_biome_data(bell_pos)
|
||||
local biome_name = minetest.get_biome_name(biome_data.biome)
|
||||
--mcl_villages.paths(blockseed, biome_name)
|
||||
|
||||
local l = minetest.add_entity(bell, "mobs_mc:iron_golem"):get_luaentity()
|
||||
if l then
|
||||
l._home = bell
|
||||
else
|
||||
minetest.log("info", "Could not create a golem!")
|
||||
end
|
||||
spawn_cats(bell)
|
||||
|
||||
for _, building in pairs(settlement_info) do
|
||||
local has_beds = building["num_beds"] and building["num_beds"] ~= nil
|
||||
local has_jobs = building["num_jobs"] and building["num_jobs"] ~= nil
|
||||
|
||||
local minp, maxp = building["minp"], building["maxp"]
|
||||
|
||||
if has_jobs then
|
||||
local jobsites = minetest.find_nodes_in_area(minp, maxp, mobs_mc.jobsites)
|
||||
|
||||
for _, job_pos in pairs(jobsites) do
|
||||
table.insert(jobs, job_pos)
|
||||
end
|
||||
end
|
||||
|
||||
if has_beds then
|
||||
local bld_beds = minetest.find_nodes_in_area(minp, maxp, { "group:bed" })
|
||||
|
||||
for _, bed_pos in pairs(bld_beds) do
|
||||
local bed_node = minetest.get_node(bed_pos)
|
||||
local bed_group = core.get_item_group(bed_node.name, "bed")
|
||||
|
||||
-- We only spawn at bed bottoms
|
||||
-- 1 is bottom, 2 is top
|
||||
if bed_group == 1 then
|
||||
table.insert(beds, bed_pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- minetest.log("beds: "..#beds.." jobsites: "..#jobs)
|
||||
if beds then
|
||||
for _, bed_pos in pairs(beds) do
|
||||
local res = minetest.forceload_block(bed_pos, true)
|
||||
if res then
|
||||
mcl_villages.forced_blocks[minetest.pos_to_string(bed_pos)] = minetest.get_us_time()
|
||||
end
|
||||
local m = minetest.get_meta(bed_pos)
|
||||
m:set_string("bell_pos", minetest.pos_to_string(bell_pos))
|
||||
if m:get_string("villager") == "" then
|
||||
local v = minetest.add_entity(bed_pos, "mobs_mc:villager")
|
||||
if v then
|
||||
local l = v:get_luaentity()
|
||||
l._bed = bed_pos
|
||||
l._bell = bell_pos
|
||||
m:set_string("villager", l._id)
|
||||
m:set_string("infotext", S("A villager sleeps here"))
|
||||
|
||||
local job_pos = table.remove(jobs, 1)
|
||||
if job_pos then
|
||||
villager_employ(l, job_pos) -- HACK: merge more MCLA villager code
|
||||
end
|
||||
|
||||
for _, callback in pairs(mcl_villages.on_villager_placed) do
|
||||
callback(v, blockseed)
|
||||
end
|
||||
else
|
||||
minetest.log("info", "Could not create a villager!")
|
||||
end
|
||||
else
|
||||
minetest.log("info", "bed already owned by " .. m:get_string("villager"))
|
||||
end
|
||||
end
|
||||
spawn_villagers(pos,p2)
|
||||
fix_village_water(pos,p2)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,28 +1,9 @@
|
|||
-- switch for debugging
|
||||
function mcl_villages.debug(message)
|
||||
-- minetest.chat_send_all(message)
|
||||
-- minetest.log("warning", "[mcl_villages] "..message)
|
||||
minetest.log("verbose", "[mcl_villages] "..message)
|
||||
end
|
||||
|
||||
--[[ Manually set in 'buildings.lua'
|
||||
-- material to replace cobblestone with
|
||||
local wallmaterial = {
|
||||
"mcl_core:junglewood",
|
||||
"mcl_core:sprucewood",
|
||||
"mcl_core:wood",
|
||||
"mcl_core:birchwood",
|
||||
"mcl_core:acaciawood",
|
||||
"mcl_core:stonebrick",
|
||||
"mcl_core:cobble",
|
||||
"mcl_core:sandstonecarved",
|
||||
"mcl_core:sandstone",
|
||||
"mcl_core:sandstonesmooth2"
|
||||
}
|
||||
--]]
|
||||
--
|
||||
-- possible surfaces where buildings can be built
|
||||
--
|
||||
mcl_villages.surface_mat = {}
|
||||
mcl_villages.surface_mat["mcl_core:andesite"] = true
|
||||
mcl_villages.surface_mat["mcl_core:diorite"] = true
|
||||
|
@ -40,7 +21,7 @@ mcl_villages.surface_mat["mcl_core:sandstone"] = true
|
|||
mcl_villages.surface_mat["mcl_core:sandstonesmooth"] = true
|
||||
mcl_villages.surface_mat["mcl_core:sandstonesmooth2"] = true
|
||||
--mcl_villages.surface_mat["mcl_core:silver_sand"] = true
|
||||
mcl_villages.surface_mat["mcl_core:snow"] = true
|
||||
--mcl_villages.surface_mat["mcl_core:snow"] = true
|
||||
mcl_villages.surface_mat["mcl_core:stone"] = true
|
||||
mcl_villages.surface_mat["mcl_core:stone_with_coal"] = true
|
||||
mcl_villages.surface_mat["mcl_core:stone_with_iron"] = true
|
||||
|
@ -49,68 +30,10 @@ mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_orange"] = true
|
|||
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_red"] = true
|
||||
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_white"] = true
|
||||
|
||||
--
|
||||
-- path to schematics
|
||||
--
|
||||
schem_path = mcl_villages.modpath.."/schematics/"
|
||||
--
|
||||
-- list of schematics
|
||||
--
|
||||
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true)
|
||||
|
||||
mcl_villages.schematic_table = {
|
||||
{name = "belltower", mts = schem_path.."new_villages/belltower.mts", hwidth = 9, hdepth = 9, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1, yadjust = 1 },
|
||||
--{name = "old_belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 6, hsize = 8, max_num = 0, rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
--{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 10, hsize = 18, max_num = 0.08 , rplc = basic_pseudobiome_villages },
|
||||
--{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 8, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages },
|
||||
{name = "new_blacksmith", mts = schem_path.."new_villages/blacksmith.mts", hwidth = 9, hdepth = 11, hheight = 8, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "weaponsmith", mts = schem_path.."new_villages/weaponsmith.mts", hwidth = 11, hdepth = 9, hheight = 6, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "toolsmith", mts = schem_path.."new_villages/toolsmith.mts", hwidth = 9, hdepth = 11, hheight = 6, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "tannery", mts = schem_path.."new_villages/leather_worker.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
--{name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, hdepth = 8, hheight = 10, hsize = 15, max_num = 0.01 , rplc = basic_pseudobiome_villages },
|
||||
--{name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 14, hheight = 15, hsize = 20, max_num = 0.01 , rplc = basic_pseudobiome_villages },
|
||||
{name = "newchurch", mts = schem_path.."new_villages/church.mts", hwidth = 14, hdepth = 16, hheight = 13, hsize = 22, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "chapel", mts = schem_path.."new_villages/chapel.mts", hwidth = 9, hdepth = 10, hheight = 6, hsize = 14, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
--{name = "farm", mts = schem_path.."farm.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 12, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 0 },
|
||||
--{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 4, hheight = 6, hsize = 6, max_num = 0.001 , rplc = false },
|
||||
{name = "lamp_1", mts = schem_path.."new_villages/lamp_1.mts", hwidth = 1, hdepth = 1, hheight = 4, hsize = 4, max_num = 0.001 , rplc = false, yadjust = 1 },
|
||||
{name = "lamp_2", mts = schem_path.."new_villages/lamp_2.mts", hwidth = 1, hdepth = 2, hheight = 6, hsize = 5, max_num = 0.001 , rplc = false, yadjust = 1 },
|
||||
{name = "lamp_3", mts = schem_path.."new_villages/lamp_3.mts", hwidth = 3, hdepth = 3, hheight = 4, hsize = 6, max_num = 0.001 , rplc = false, yadjust = 1 },
|
||||
{name = "lamp_4", mts = schem_path.."new_villages/lamp_4.mts", hwidth = 1, hdepth = 2, hheight = 5, hsize = 5, max_num = 0.001 , rplc = false, yadjust = 1 },
|
||||
{name = "lamp_5", mts = schem_path.."new_villages/lamp_5.mts", hwidth = 1, hdepth = 1, hheight = 2, hsize = 4, max_num = 0.001 , rplc = false, yadjust = 1 },
|
||||
{name = "lamp_6", mts = schem_path.."new_villages/lamp_6.mts", hwidth = 1, hdepth = 1, hheight = 3, hsize = 4, max_num = 0.001 , rplc = false, yadjust = 1 },
|
||||
--{name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 18, max_num = 0.01 , rplc = basic_pseudobiome_villages },
|
||||
{name = "newlibrary", mts = schem_path.."new_villages/library.mts", hwidth = 14, hdepth = 14, hheight = 7, hsize = 21, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
--{name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 9, hdepth = 12, hheight = 9, hsize = 16, max_num = 0.08 , rplc = basic_pseudobiome_villages },
|
||||
--{name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 8, hheight = 9, hsize = 13, max_num = 0.3 , rplc = basic_pseudobiome_villages },
|
||||
{name = "house_1_bed", mts = schem_path.."new_villages/house_1_bed.mts", hwidth = 9, hdepth = 8, hheight = 7, hsize = 13, max_num = 0.3 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "house_2_bed", mts = schem_path.."new_villages/house_2_bed.mts", hwidth = 11, hdepth = 8, hheight = 7, hsize = 15, max_num = 0.2 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "house_3_bed", mts = schem_path.."new_villages/house_3_bed.mts", hwidth = 11, hdepth = 13, hheight = 9, hsize = 18, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "house_4_bed", mts = schem_path.."new_villages/house_4_bed.mts", hwidth = 11, hdepth = 13, hheight = 10, hsize = 18, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "mason", mts = schem_path.."new_villages/mason.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "mill", mts = schem_path.."new_villages/mill.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "cartographer", mts = schem_path.."new_villages/cartographer.mts", hwidth = 9, hdepth = 12, hheight = 6, hsize = 16, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 2 },
|
||||
{name = "fletcher", mts = schem_path.."new_villages/fletcher.mts", hwidth = 8, hdepth = 8, hheight = 7, hsize = 12, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "new_butcher", mts = schem_path.."new_villages/butcher.mts", hwidth = 8, hdepth = 14, hheight = 9, hsize = 17, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "fish_farm", mts = schem_path.."new_villages/fishery.mts", hwidth = 10, hdepth = 7, hheight = 9, hsize = 13, max_num = 0.01 , rplc = basic_pseudobiome_villages, yadjust=-2 },
|
||||
--{name = "tavern", mts = schem_path.."tavern.mts", hwidth = 12, hdepth = 10, hheight = 13, hsize = 17, max_num = 0.050, rplc = basic_pseudobiome_villages },
|
||||
--{name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 7, hsize = 11, max_num = 0.01, rplc = basic_pseudobiome_villages },
|
||||
{name = "new_well", mts = schem_path.."new_villages/well.mts", hwidth = 6, hdepth = 6, hheight = 8, hsize = 9, max_num = 0.01, rplc = basic_pseudobiome_villages, yadjust=-1 },
|
||||
{name = "new_farm", mts = schem_path.."new_villages/farm.mts", hwidth=10, hdepth=9, hheight=6, hsize=14, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "farm_small", mts = schem_path.."new_villages/farm_small_1.mts", hwidth=10, hdepth=9, hheight=6, hsize=14, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "farm_small2", mts = schem_path.."new_villages/farm_small_2.mts", hwidth=9, hdepth=9, hheight=3, hsize=14, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
{name = "farm_large", mts = schem_path.."new_villages/farm_large_1.mts", hwidth=13, hdepth=13, hheight=4, hsize=19, max_num = 0.1, rplc = basic_pseudobiome_villages, yadjust = 1 },
|
||||
}
|
||||
|
||||
--
|
||||
-- maximum allowed difference in height for building a settlement
|
||||
--
|
||||
max_height_difference = 56
|
||||
--
|
||||
--
|
||||
--
|
||||
half_map_chunk_size = 40
|
||||
--quarter_map_chunk_size = 20
|
||||
mcl_villages.max_height_difference = 56
|
||||
|
||||
mcl_villages.half_map_chunk_size = 40
|
||||
|
||||
--
|
||||
-- Biome based block substitutions
|
||||
|
@ -156,14 +79,14 @@ mcl_villages.biome_map = {
|
|||
|
||||
CherryGrove = "cherry",
|
||||
|
||||
-- no change, but try to convert MCLA material
|
||||
-- FlowerForest = "oak",
|
||||
-- Forest = "oak",
|
||||
-- MushroomIsland = "oak",
|
||||
-- Plains = "oak",
|
||||
-- StoneBeach = "oak",
|
||||
-- SunflowerPlains = "oak",
|
||||
-- Swampland = "oak",
|
||||
-- no change
|
||||
--FlowerForest = "oak",
|
||||
--Forest = "oak",
|
||||
--MushroomIsland = "",
|
||||
--Plains = "oak",
|
||||
--StoneBeach = "",
|
||||
--SunflowerPlains = "oak",
|
||||
--Swampland = "oak",
|
||||
}
|
||||
|
||||
mcl_villages.vl_to_mcla = {
|
||||
|
@ -190,8 +113,8 @@ mcl_villages.vl_to_mcla = {
|
|||
}
|
||||
mcl_villages.mcla_to_vl = {
|
||||
-- oneway
|
||||
{ '"mcl_villages:no_paths"', '"air"'}, -- TODO: support these
|
||||
{ '"mcl_villages:path_endpoint"', '"air"'}, -- TODO: support these
|
||||
--{ '"mcl_villages:no_paths"', '"air"'}, -- TODO: support these
|
||||
--{ '"mcl_villages:path_endpoint"', '"air"'}, -- TODO: support these
|
||||
{ '"mcl_villages:crop_root', '"mcl_farming:potato'}, -- TODO: support biome specific farming
|
||||
{ '"mcl_villages:crop_grain', '"mcl_farming:wheat'}, -- TODO: support biome specific farming
|
||||
{ '"mcl_villages:crop_gourd', '"mcl_farming:pumpkin'}, -- TODO: support biome specific farming
|
||||
|
@ -228,7 +151,7 @@ mcl_villages.mcla_to_vl = {
|
|||
}
|
||||
mcl_villages.material_substitions = {
|
||||
desert = {
|
||||
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth%1"' },
|
||||
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1?
|
||||
{
|
||||
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
|
||||
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
|
||||
|
@ -254,7 +177,7 @@ mcl_villages.material_substitions = {
|
|||
{ "mcl_trees:wood_oak", "mcl_core:redsandstonesmooth" },
|
||||
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
|
||||
{ '"mcl_stairs:stair_oak_bark([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' },
|
||||
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth%1"' },
|
||||
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1?
|
||||
},
|
||||
spruce = {
|
||||
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sprucewood%1"' },
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
local foundation_materials = {}
|
||||
foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
|
||||
--"mcl_core:sandstonecarved"
|
||||
foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone"
|
||||
|
||||
local function is_air(node)
|
||||
return not node or node.name == "air" or node.name == "ignore"
|
||||
return not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths"
|
||||
end
|
||||
local function is_solid(node)
|
||||
if not node or node.name == "air" or node.name == "ignore" then return false end
|
||||
if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end
|
||||
--if string.find(node.name,"leaf") then return false end
|
||||
--if string.find(node.name,"tree") then return false end
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
|
@ -14,8 +14,8 @@ local function is_solid(node)
|
|||
end
|
||||
local function make_solid(lvm, cp, with, except)
|
||||
local cur = lvm:get_node_at(cp)
|
||||
if not is_solid(cur) or (except and cur.name == except) then
|
||||
lvm:set_node_at(cp, {name=with})
|
||||
if not is_solid(cur) or (except and cur.name == except.name) then
|
||||
lvm:set_node_at(cp, with)
|
||||
end
|
||||
end
|
||||
local function excavate(lvm,xi,yi,zi,pr)
|
||||
|
@ -34,7 +34,7 @@ local function excavate(lvm,xi,yi,zi,pr)
|
|||
-- try to completely remove trees overhead
|
||||
if not string.find(node.name, "leaf") and not string.find(node.name, "tree") then
|
||||
-- stop randomly depending on fill, to narrow down the caves
|
||||
if pr:next(0,31)^2 > c * 100 then return false end
|
||||
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
|
||||
end
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
|
||||
return true -- modified
|
||||
|
@ -45,9 +45,9 @@ local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat)
|
|||
pos.y = pos.y+1
|
||||
local cur = lvm:get_node_at(pos)
|
||||
if not is_solid(cur) then return false end -- above is empty, do not fill below
|
||||
if cur and cur.name and cur.name ~= surface_mat then platform_mat = cur.name end
|
||||
if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end
|
||||
if pr:next(1,5) == 5 then -- randomly switch to stone sometimes
|
||||
platform_mat = "mcl_core:stone"
|
||||
platform_mat = { name = "mcl_core:stone" }
|
||||
end
|
||||
-- count solid nodes above otherwise
|
||||
for x = xi-1,xi+1 do
|
||||
|
@ -57,137 +57,144 @@ local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat)
|
|||
end
|
||||
end
|
||||
-- stop randomly depending on fill, to narrow down the foundation
|
||||
if pr:next(0,31)^2 > c * 100 then return false end
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),{name=platform_mat})
|
||||
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), platform_mat)
|
||||
return true -- modified
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- function clear space above baseplate
|
||||
-------------------------------------------------------------------------------
|
||||
function mcl_villages.terraform(settlement_info, pr)
|
||||
function mcl_villages.terraform(lvm, settlement, pr)
|
||||
-- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft.
|
||||
--local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local lvm = VoxelManip()
|
||||
--local lvm = VoxelManip()
|
||||
|
||||
for i, built_house in ipairs(settlement_info) do
|
||||
-- pick right schematic_info to current built_house
|
||||
for j, schem in ipairs(mcl_villages.schematic_table) do
|
||||
if settlement_info[i]["name"] == schem["name"] then
|
||||
schematic_data = schem
|
||||
break
|
||||
end
|
||||
-- we make the foundations 1 node wider than requested, to have one node for path laying
|
||||
for i, building in ipairs(settlement) do
|
||||
--lvm:read_from_map(vector.new(pos.x-2, pos.y-20, pos.z-2), vector.new(pos.x+sx+2, pos.y+sy+20, pos.z+sz+2))
|
||||
--lvm:get_data()
|
||||
if not building.no_clearance then
|
||||
local pos, size = building.pos, building.size
|
||||
pos = vector.offset(pos, -math.floor(size.x/2)-1, 0, -math.floor(size.z/2)-1)
|
||||
mcl_villages.clearance(lvm, pos.x, pos.y, pos.z, size.x+2, size.y, size.z+2, pr)
|
||||
end
|
||||
local pos = settlement_info[i]["pos"]
|
||||
local fwidth, fheight, fdepth = schematic_data["hwidth"], schematic_data["hheight"], schematic_data["hdepth"]
|
||||
local surface_mat = settlement_info[i]["surface_mat"]
|
||||
if settlement_info[i]["rotat"] == "90" or settlement_info[i]["rotat"] == "270" then
|
||||
fwidth, fdepth = fdepth, fwidth
|
||||
--lvm:write_to_map(false)
|
||||
end
|
||||
for i, building in ipairs(settlement) do
|
||||
if not building.no_ground_turnip then
|
||||
local pos, size = building.pos, building.size
|
||||
local surface_mat = building.surface_mat
|
||||
local platform_mat = building.platform_mat or { name = foundation_materials[surface_mat.name] or "mcl_core:dirt" }
|
||||
building.platform_mat = platform_mat -- remember for use in schematic placement
|
||||
pos = vector.offset(pos, -math.floor(size.x/2)-1, 0, -math.floor(size.z/2)-1)
|
||||
mcl_villages.foundation(lvm, pos.x, pos.y, pos.z, size.x+2, -4, size.z+2, surface_mat, platform_mat, pr)
|
||||
end
|
||||
lvm:read_from_map(vector.new(pos.x-2, pos.y-20, pos.z-2), vector.new(pos.x+fwidth+2, pos.y+fheight+20, pos.z+fdepth+2))
|
||||
lvm:get_data()
|
||||
mcl_villages.foundation(lvm, pos, fwidth, fheight, fdepth, surface_mat, pr)
|
||||
lvm:write_to_map(false)
|
||||
end
|
||||
end
|
||||
function mcl_villages.foundation(lvm, pos, fwidth, fheight, fdepth, surface_mat, pr)
|
||||
-- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft.
|
||||
local platform_mat = foundation_materials[surface_mat] or "mcl_core:dirt"
|
||||
|
||||
local AIR = {name = "air"}
|
||||
function mcl_villages.clearance(lvm, px, py, pz, sx, sy, sz, pr)
|
||||
-- excavate the needed volume, some headroom, and add a baseplate
|
||||
local p2 = vector.new(pos)
|
||||
for xi = pos.x,pos.x+fwidth-1 do
|
||||
for zi = pos.z,pos.z+fdepth-1 do
|
||||
lvm:set_node_at(vector.new(xi, pos.y+1, zi),{name="air"})
|
||||
-- pos.y+2 to pos.y+5 are filled larger below!
|
||||
for yi = pos.y+6,pos.y+fheight do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
|
||||
for xi = px,px+sx-1 do
|
||||
for zi = pz,pz+sz-1 do
|
||||
lvm:set_node_at(vector.new(xi, py+1, zi),AIR)
|
||||
-- py+2 to py+5 are filled larger below!
|
||||
for yi = py+6,py+sy do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),AIR)
|
||||
end
|
||||
make_solid(lvm, vector.new(xi, pos.y, zi), surface_mat, platform_mat)
|
||||
make_solid(lvm, vector.new(xi, pos.y - 1, zi), platform_mat)
|
||||
end
|
||||
end
|
||||
-- slightly widen the cave, to make easier to enter for mobs
|
||||
for xi = pos.x-1,pos.x+fwidth do
|
||||
for zi = pos.z-1,pos.z+fdepth do
|
||||
for yi = pos.y+2,pos.y+5 do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
|
||||
for xi = px-1,px+sx do
|
||||
for zi = pz-1,pz+sz do
|
||||
for yi = py+2,py+5 do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),AIR)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- some extra gaps
|
||||
for xi = pos.x-2,pos.x+fwidth+1 do
|
||||
for zi = pos.z-2,pos.z+fdepth+1 do
|
||||
for xi = px-2,px+sx+1 do
|
||||
for zi = pz-2,pz+sz+1 do
|
||||
if pr:next(1,4) == 1 then
|
||||
for yi = pos.y+3,pos.y+5 do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
|
||||
for yi = py+3,py+5 do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi),AIR)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- slightly widen the baseplate, to make easier to enter for mobs
|
||||
for xi = pos.x,pos.x+fwidth-1 do
|
||||
make_solid(lvm, vector.new(xi, pos.y-1, pos.z-1), surface_mat, platform_mat)
|
||||
make_solid(lvm, vector.new(xi, pos.y-1, pos.z), platform_mat)
|
||||
make_solid(lvm, vector.new(xi, pos.y-1, pos.z+fdepth-1), platform_mat)
|
||||
make_solid(lvm, vector.new(xi, pos.y-1, pos.z+fdepth), surface_mat, platform_mat)
|
||||
-- cave some additional area overhead, try to make it interesting though
|
||||
for yi = py+3,py+sy*3 do
|
||||
local active = false
|
||||
for xi = px-2,px+sx+1 do
|
||||
for zi = pz-2,pz+sz+1 do
|
||||
if excavate(lvm,xi,yi,zi,pr) then active = true end
|
||||
end
|
||||
end
|
||||
if not active and yi > py+sy+5 then break end
|
||||
end
|
||||
for zi = pos.z,pos.z+fdepth-1 do
|
||||
make_solid(lvm, vector.new(pos.x-1, pos.y-1, zi), surface_mat, platform_mat)
|
||||
make_solid(lvm, vector.new(pos.x, pos.y-1, zi), platform_mat)
|
||||
make_solid(lvm, vector.new(pos.x+fwidth-1, pos.y-1, zi), platform_mat)
|
||||
make_solid(lvm, vector.new(pos.x+fwidth, pos.y-1, zi), surface_mat, platform_mat)
|
||||
end
|
||||
function mcl_villages.foundation(lvm, px, py, pz, sx, sy, sz, surface_mat, platform_mat, pr)
|
||||
-- generate a baseplate
|
||||
for xi = px,px+sx-1 do
|
||||
for zi = pz,pz+sz-1 do
|
||||
lvm:set_node_at(vector.new(xi, py, zi), surface_mat)
|
||||
make_solid(lvm, vector.new(xi, py - 1, zi), platform_mat)
|
||||
end
|
||||
end
|
||||
-- slightly widen the baseplate, to make easier to enter for mobs
|
||||
for xi = px,px+sx-1 do
|
||||
make_solid(lvm, vector.new(xi, py-1, pz-1), surface_mat, platform_mat)
|
||||
make_solid(lvm, vector.new(xi, py-1, pz), platform_mat)
|
||||
make_solid(lvm, vector.new(xi, py-1, pz+sz-1), platform_mat)
|
||||
make_solid(lvm, vector.new(xi, py-1, pz+sz), surface_mat, platform_mat)
|
||||
end
|
||||
for zi = pz,pz+sz-1 do
|
||||
make_solid(lvm, vector.new(px-1, py-1, zi), surface_mat, platform_mat)
|
||||
make_solid(lvm, vector.new(px, py-1, zi), platform_mat)
|
||||
make_solid(lvm, vector.new(px+sx-1, py-1, zi), platform_mat)
|
||||
make_solid(lvm, vector.new(px+sx, py-1, zi), surface_mat, platform_mat)
|
||||
end
|
||||
-- make some additional steps, along both x sides
|
||||
for xi = pos.x,pos.x+fwidth-1 do
|
||||
local cp = vector.new(xi, pos.y-3, pos.z-1)
|
||||
for xi = px,px+sx-1 do
|
||||
local cp = vector.new(xi, py-3, pz-1)
|
||||
if is_solid(lvm:get_node_at(cp)) then
|
||||
cp = vector.new(xi, pos.y-2, pos.z-1)
|
||||
cp = vector.new(xi, py-2, pz-1)
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
cp.z = pos.z-2
|
||||
cp.z = pz-2
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
end
|
||||
local cp = vector.new(xi, pos.y-3, pos.z+fdepth)
|
||||
local cp = vector.new(xi, py-3, pz+sz)
|
||||
if is_solid(lvm:get_node_at(cp)) then
|
||||
cp = vector.new(xi, pos.y-2, pos.z+fdepth)
|
||||
cp = vector.new(xi, py-2, pz+sz)
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
cp.z = pos.z + fdepth + 1
|
||||
cp.z = pz + sz + 1
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
end
|
||||
end
|
||||
-- make some additional steps, along both z sides
|
||||
for zi = pos.z,pos.z+fdepth-1 do
|
||||
local cp = vector.new(pos.x-1, pos.y-3, zi)
|
||||
for zi = pz,pz+sz-1 do
|
||||
local cp = vector.new(px-1, py-3, zi)
|
||||
if is_solid(lvm:get_node_at(cp)) then
|
||||
cp = vector.new(pos.x-1, pos.y-2, zi)
|
||||
cp = vector.new(px-1, py-2, zi)
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
cp.x = pos.x-2
|
||||
cp.x = px-2
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
end
|
||||
local cp = vector.new(pos.x+fwidth, pos.y-3, zi)
|
||||
local cp = vector.new(px+sx, py-3, zi)
|
||||
if is_solid(lvm:get_node_at(cp)) then
|
||||
cp = vector.new(pos.x+fwidth, pos.y-2, zi)
|
||||
cp = vector.new(px+sx, py-2, zi)
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
cp.x = pos.x+fwidth+1
|
||||
cp.x = px+sx+1
|
||||
make_solid(lvm, cp, surface_mat, platform_mat)
|
||||
end
|
||||
end
|
||||
-- cave some additional area overhead, try to make it interesting though
|
||||
for yi = pos.y+3,pos.y+fheight*3 do
|
||||
local active = false
|
||||
for xi = pos.x-2,pos.x+fwidth+1 do
|
||||
for zi = pos.z-2,pos.z+fdepth+1 do
|
||||
if excavate(lvm,xi,yi,zi,pr) then active = true end
|
||||
end
|
||||
end
|
||||
if not active and yi > pos.y+fheight+5 then break end
|
||||
end
|
||||
-- construct additional baseplate below, also try to make it interesting
|
||||
for yi = pos.y-2,pos.y-20,-1 do
|
||||
for yi = py-2,py-20,-1 do
|
||||
local active = false
|
||||
for xi = pos.x-1,pos.x+fwidth do
|
||||
for zi = pos.z-1,pos.z+fdepth do
|
||||
for xi = px-1,px+sx do
|
||||
for zi = pz-1,pz+sz do
|
||||
if grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat) then active = true end
|
||||
end
|
||||
end
|
||||
if not active and yi < pos.y-5 then break end
|
||||
if not active and yi < py + sy then break end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,91 +1,351 @@
|
|||
-------------------------------------------------------------------------------
|
||||
-- generate paths between buildings
|
||||
-------------------------------------------------------------------------------
|
||||
function mcl_villages.paths(settlement_info)
|
||||
local starting_point
|
||||
local end_point
|
||||
local distance
|
||||
--for k,v in pairs(settlement_info) do
|
||||
starting_point = settlement_info[1]["pos"]
|
||||
for o,p in pairs(settlement_info) do
|
||||
|
||||
end_point = settlement_info[o]["pos"]
|
||||
if starting_point ~= end_point
|
||||
then
|
||||
-- loop until end_point is reched (distance == 0)
|
||||
while true do
|
||||
local light_threshold = tonumber(minetest.settings:get("mcl_villages_light_threshold")) or 5
|
||||
|
||||
-- define surrounding pos to starting_point
|
||||
local north_p = {x=starting_point.x+1, y=starting_point.y, z=starting_point.z}
|
||||
local south_p = {x=starting_point.x-1, y=starting_point.y, z=starting_point.z}
|
||||
local west_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z+1}
|
||||
local east_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z-1}
|
||||
-- measure distance to end_point
|
||||
local dist_north_p_to_end = math.sqrt(
|
||||
((north_p.x - end_point.x)*(north_p.x - end_point.x))+
|
||||
((north_p.z - end_point.z)*(north_p.z - end_point.z))
|
||||
)
|
||||
local dist_south_p_to_end = math.sqrt(
|
||||
((south_p.x - end_point.x)*(south_p.x - end_point.x))+
|
||||
((south_p.z - end_point.z)*(south_p.z - end_point.z))
|
||||
)
|
||||
local dist_west_p_to_end = math.sqrt(
|
||||
((west_p.x - end_point.x)*(west_p.x - end_point.x))+
|
||||
((west_p.z - end_point.z)*(west_p.z - end_point.z))
|
||||
)
|
||||
local dist_east_p_to_end = math.sqrt(
|
||||
((east_p.x - end_point.x)*(east_p.x - end_point.x))+
|
||||
((east_p.z - end_point.z)*(east_p.z - end_point.z))
|
||||
)
|
||||
-- evaluate which pos is closer to the end_point
|
||||
if dist_north_p_to_end <= dist_south_p_to_end and
|
||||
dist_north_p_to_end <= dist_west_p_to_end and
|
||||
dist_north_p_to_end <= dist_east_p_to_end
|
||||
then
|
||||
starting_point = north_p
|
||||
distance = dist_north_p_to_end
|
||||
-- This ends up being a nested table.
|
||||
-- 1st level is the blockseed which is the village
|
||||
-- 2nd is the distance of the building from the bell
|
||||
-- 3rd is the pos of the end points
|
||||
local path_ends = {}
|
||||
|
||||
elseif dist_south_p_to_end <= dist_north_p_to_end and
|
||||
dist_south_p_to_end <= dist_west_p_to_end and
|
||||
dist_south_p_to_end <= dist_east_p_to_end
|
||||
then
|
||||
starting_point = south_p
|
||||
distance = dist_south_p_to_end
|
||||
|
||||
elseif dist_west_p_to_end <= dist_north_p_to_end and
|
||||
dist_west_p_to_end <= dist_south_p_to_end and
|
||||
dist_west_p_to_end <= dist_east_p_to_end
|
||||
then
|
||||
starting_point = west_p
|
||||
distance = dist_west_p_to_end
|
||||
|
||||
elseif dist_east_p_to_end <= dist_north_p_to_end and
|
||||
dist_east_p_to_end <= dist_south_p_to_end and
|
||||
dist_east_p_to_end <= dist_west_p_to_end
|
||||
then
|
||||
starting_point = east_p
|
||||
distance = dist_east_p_to_end
|
||||
end
|
||||
-- find surface of new starting point
|
||||
local surface_point, surface_mat = mcl_villages.find_surface(starting_point)
|
||||
-- replace surface node with mcl_core:grass_path
|
||||
if surface_point
|
||||
then
|
||||
if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then
|
||||
minetest.swap_node(surface_point,{name="mcl_core:sandstonesmooth2"})
|
||||
else
|
||||
minetest.swap_node(surface_point,{name="mcl_core:grass_path"})
|
||||
end
|
||||
-- don't set y coordinate, surface might be too low or high
|
||||
starting_point.x = surface_point.x
|
||||
starting_point.z = surface_point.z
|
||||
end
|
||||
if distance <= 1 or
|
||||
starting_point == end_point
|
||||
then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Insert end points in to the nested tables
|
||||
function mcl_villages.store_path_ends(lvm, minp, maxp, pos, blockseed, bell_pos)
|
||||
-- We store by distance because we create paths far away from the bell first
|
||||
local dist = vector.distance(bell_pos, pos)
|
||||
local id = "block_" .. blockseed -- cannot use integers as keys
|
||||
local tab = path_ends[id]
|
||||
if not tab then
|
||||
tab = {}
|
||||
path_ends[id] = tab
|
||||
end
|
||||
if tab[dist] == nil then tab[dist] = {} end
|
||||
-- TODO: improve, use LVM data instead of nodes
|
||||
local v = vector.zero()
|
||||
local i = 0
|
||||
for zi = minp.z-2, maxp.z+2 do
|
||||
v.z = zi
|
||||
for yi = minp.y-2, maxp.y+2 do
|
||||
v.y = yi
|
||||
for xi = minp.x-2, maxp.x+2 do
|
||||
v.x = xi
|
||||
local n = lvm:get_node_at(v)
|
||||
if n and n.name == "mcl_villages:path_endpoint" then
|
||||
i = i + 1
|
||||
table.insert(tab[dist], minetest.pos_to_string(v))
|
||||
lvm:set_node_at(v, { name = "air" })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function place_lamp(pos, pr)
|
||||
local lamp_index = pr:next(1, #mcl_villages.schematic_lamps)
|
||||
local schema = mcl_villages.schematic_lamps[lamp_index]
|
||||
local schem_lua = mcl_villages.substitute_materials(pos, schema.schem_lua, pr)
|
||||
local schematic = loadstring(schem_lua)()
|
||||
|
||||
minetest.place_schematic(
|
||||
vector.offset(pos, 0, schema.yadjust or 0, 0),
|
||||
schematic,
|
||||
"0",
|
||||
{["air"] = "ignore"}, -- avoid destroying stairs etc.
|
||||
true,
|
||||
{ place_center_x = true, place_center_y = false, place_center_z = true }
|
||||
)
|
||||
end
|
||||
|
||||
local function smooth_path(path)
|
||||
-- Smooth out bumps in path or stairs can look naf
|
||||
for pass = 1, 3 do
|
||||
for i = 2, #path - 1 do
|
||||
local prev_y = path[i - 1].y
|
||||
local y = path[i].y
|
||||
local next_y = path[i + 1].y
|
||||
local bump_node = minetest.get_node(path[i])
|
||||
|
||||
if minetest.get_item_group(bump_node.name, "water") ~= 0 then
|
||||
-- ignore in this pass
|
||||
elseif y >= next_y + 2 and y <= prev_y then
|
||||
local under_pos = vector.offset(path[i], 0, -1, 0)
|
||||
minetest.swap_node(under_pos, { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
elseif y <= next_y - 2 and y >= prev_y then
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i].y = path[i].y + 1
|
||||
elseif y >= prev_y + 2 and y <= next_y then
|
||||
local under_pos = vector.offset(path[i], 0, -1, 0)
|
||||
minetest.swap_node(under_pos, { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
elseif y <= prev_y - 2 and y >= prev_y then
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i].y = path[i].y + 1
|
||||
elseif y < prev_y and y < next_y then
|
||||
-- Fill in dip to flatten path
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i].y = path[i].y + 1
|
||||
elseif y > prev_y and y > next_y then
|
||||
-- Remove peak to flatten path
|
||||
local under_pos = vector.offset(path[i], 0, -1, 0)
|
||||
minetest.swap_node(under_pos, { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function place_path(path, pr, stair, slab)
|
||||
-- Smooth out bumps in path or stairs can look naf
|
||||
for i = 2, #path - 1 do
|
||||
local prev_y = path[i - 1].y
|
||||
local y = path[i].y
|
||||
local next_y = path[i + 1].y
|
||||
local bump_node = minetest.get_node(path[i])
|
||||
|
||||
if minetest.get_item_group(bump_node.name, "water") ~= 0 then
|
||||
-- Find air
|
||||
local found_surface = false
|
||||
local up_pos = path[i]
|
||||
while not found_surface do
|
||||
up_pos = vector.offset(up_pos, 0, 1, 0)
|
||||
local up_node = minetest.get_node(up_pos)
|
||||
if up_node and minetest.get_item_group(up_node.name, "water") == 0 then
|
||||
found_surface = true
|
||||
minetest.swap_node(up_pos, { name = "air" })
|
||||
path[i] = up_pos
|
||||
end
|
||||
end
|
||||
elseif y < prev_y and y < next_y then
|
||||
-- Fill in dip to flatten path
|
||||
-- TODO: do not break other path/stairs
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i] = vector.offset(path[i], 0, 1, 0)
|
||||
elseif y > prev_y and y > next_y then
|
||||
-- TODO: do not break other path/stairs
|
||||
-- Remove peak to flatten path
|
||||
local under_pos = vector.offset(path[i], 0, -1, 0)
|
||||
minetest.swap_node(under_pos, { name = "air" })
|
||||
path[i] = under_pos
|
||||
end
|
||||
end
|
||||
|
||||
for i, pos in ipairs(path) do
|
||||
-- replace decorations, grass and flowers, with air
|
||||
local n0 = minetest.get_node(pos)
|
||||
if n0.name ~= "air" then
|
||||
minetest.swap_node(pos, { name = "air" })
|
||||
end
|
||||
|
||||
local under_pos = vector.offset(pos, 0, -1, 0)
|
||||
local n = minetest.get_node(under_pos)
|
||||
local done = false
|
||||
local is_stair = minetest.get_item_group(n.name, "stair") ~= 0
|
||||
|
||||
if i > 1 and pos.y > path[i - 1].y then
|
||||
-- stairs up
|
||||
if not is_stair then
|
||||
done = true
|
||||
local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1]))
|
||||
minetest.swap_node(under_pos, { name = stair, param2 = param2 })
|
||||
end
|
||||
elseif i < #path-1 and pos.y > path[i + 1].y then
|
||||
-- stairs down
|
||||
if not is_stair then
|
||||
done = true
|
||||
local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1]))
|
||||
minetest.swap_node(under_pos, { name = stair, param2 = param2 })
|
||||
end
|
||||
elseif not is_stair and i > 1 and pos.y < path[i - 1].y then
|
||||
-- stairs down
|
||||
local n2 = minetest.get_node(vector.offset(path[i - 1], 0, -1, 0))
|
||||
is_stair = minetest.get_item_group(n2.name, "stair") ~= 0
|
||||
if not is_stair then
|
||||
done = true
|
||||
local param2 = minetest.dir_to_facedir(vector.subtract(path[i - 1], pos))
|
||||
if i < #path - 1 then -- uglier, but easier to walk up?
|
||||
param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1]))
|
||||
end
|
||||
minetest.add_node(pos, { name = stair, param2 = param2 })
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
elseif not is_stair and i < #path-1 and pos.y < path[i + 1].y then
|
||||
-- stairs up
|
||||
local n2 = minetest.get_node(vector.offset(path[i + 1], 0, -1, 0))
|
||||
is_stair = minetest.get_item_group(n2.name, "stair") ~= 0
|
||||
if not is_stair then
|
||||
done = true
|
||||
local param2 = minetest.dir_to_facedir(vector.subtract(path[i + 1], pos))
|
||||
if i > 1 then -- uglier, but easier to walk up?
|
||||
param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1]))
|
||||
end
|
||||
minetest.add_node(pos, { name = stair, param2 = param2 })
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- flat
|
||||
if not done then
|
||||
if minetest.get_item_group(n.name, "water") ~= 0 then
|
||||
minetest.add_node(under_pos, { name = slab })
|
||||
elseif n.name == "mcl_core:sand" or n.name == "mcl_core:redsand" then
|
||||
minetest.swap_node(under_pos, { name = "mcl_core:sandstonesmooth2" })
|
||||
elseif minetest.get_item_group(n.name, "soil") > 0
|
||||
and minetest.get_item_group(n.name, "dirtifies_below_solid") == 0
|
||||
then
|
||||
minetest.swap_node(under_pos, { name = "mcl_core:grass_path" })
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear space for villagers to walk
|
||||
for j = 1, 2 do
|
||||
local over_pos = vector.offset(pos, 0, j, 0)
|
||||
local m = minetest.get_node(over_pos)
|
||||
if m.name ~= "air" then
|
||||
minetest.swap_node(over_pos, { name = "air" })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Do lamps afterwards so we don't put them where a path will be laid
|
||||
for _, pos in ipairs(path) do
|
||||
if minetest.get_node_light(pos, 0) < light_threshold then
|
||||
local nn = minetest.find_nodes_in_area_under_air(
|
||||
vector.offset(pos, -1, -1, -1),
|
||||
vector.offset(pos, 1, 1, 1),
|
||||
{ "group:material_sand", "group:material_stone", "group:grass_block", "group:wood_slab" }
|
||||
)
|
||||
for _, npos in ipairs(nn) do
|
||||
local node = minetest.get_node(npos)
|
||||
if node.name ~= "mcl_core:grass_path" and minetest.get_item_group(node.name, "stair") == 0 then
|
||||
if minetest.get_item_group(node.name, "wood_slab") ~= 0 then
|
||||
local over_pos = vector.offset(npos, 0, 1, 0)
|
||||
minetest.add_node(over_pos, { name = "mcl_torches:torch", param2 = 1 })
|
||||
else
|
||||
place_lamp(npos, pr)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Work out which end points should be connected
|
||||
-- works from the outside of the village in
|
||||
function mcl_villages.paths(blockseed, biome_name)
|
||||
local pr = PseudoRandom(blockseed)
|
||||
local pathends = path_ends["block_" .. blockseed]
|
||||
|
||||
if pathends == nil then
|
||||
minetest.log("warning", string.format("[mcl_villages] Tried to set paths for block seed that doesn't exist %d", blockseed))
|
||||
return
|
||||
end
|
||||
|
||||
-- Use the same stair and slab throughout the entire village
|
||||
local stair, slab = '"mcl_stairs:stair_oak"', '"mcl_stairs:slab_oak_top"'
|
||||
|
||||
-- Change stair and slab for biome
|
||||
if mcl_villages.biome_map[biome_name] and mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]] then
|
||||
for _, sub in pairs(mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]]) do
|
||||
stair = stair:gsub(sub[1], sub[2])
|
||||
slab = slab:gsub(sub[1], sub[2])
|
||||
end
|
||||
end
|
||||
-- translate MCLA values to VL
|
||||
for _, sub in pairs(mcl_villages.mcla_to_vl) do
|
||||
stair = stair:gsub(sub[1], sub[2])
|
||||
slab = slab:gsub(sub[1], sub[2])
|
||||
end
|
||||
-- The quotes are to match what is in schemas, but we don't want them now
|
||||
stair = stair:gsub('"', "")
|
||||
slab = slab:gsub('"', "")
|
||||
|
||||
-- Keep track of connections
|
||||
local connected = {}
|
||||
|
||||
-- get a list of reverse sorted keys, which are distances
|
||||
local dist_keys = {}
|
||||
for k in pairs(pathends) do
|
||||
table.insert(dist_keys, k)
|
||||
end
|
||||
table.sort(dist_keys, function(a, b)
|
||||
return a > b
|
||||
end)
|
||||
--minetest.log("Planning paths with "..#dist_keys.." nodes")
|
||||
|
||||
for i, from in ipairs(dist_keys) do
|
||||
-- ep == end_point
|
||||
for _, from_ep in ipairs(pathends[from]) do
|
||||
local from_ep_pos = minetest.string_to_pos(from_ep)
|
||||
local closest_pos
|
||||
local closest_bld
|
||||
local best = 10000000
|
||||
|
||||
-- Most buildings only do other buildings that are closer to the bell
|
||||
-- for the bell do any end points that don't have paths near them
|
||||
local lindex = i + 1
|
||||
if i == 0 then
|
||||
lindex = 1
|
||||
end
|
||||
|
||||
for j = lindex, #dist_keys do
|
||||
local to = dist_keys[j]
|
||||
if from ~= to and connected[from .. "-" .. to] == nil and connected[to .. "-" .. from] == nil then
|
||||
for _, to_ep in ipairs(pathends[to]) do
|
||||
local to_ep_pos = minetest.string_to_pos(to_ep)
|
||||
|
||||
local dist = vector.distance(from_ep_pos, to_ep_pos)
|
||||
if dist < best then
|
||||
best = dist
|
||||
closest_pos = to_ep_pos
|
||||
closest_bld = to
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if closest_pos then
|
||||
local path = minetest.find_path(from_ep_pos, closest_pos, 64, 2, 2)
|
||||
if path then smooth_path(path) end
|
||||
if not path then
|
||||
path = minetest.find_path(from_ep_pos, closest_pos, 64, 3, 3)
|
||||
if path then smooth_path(path) end
|
||||
end
|
||||
path = minetest.find_path(from_ep_pos, closest_pos, 64, 1, 1)
|
||||
if path and #path > 0 then
|
||||
place_path(path, pr, stair, slab)
|
||||
connected[from .. "-" .. closest_bld] = 1
|
||||
else
|
||||
minetest.log(
|
||||
"warning",
|
||||
string.format(
|
||||
"[mcl_villages] No path from %s to %s, distance %d",
|
||||
minetest.pos_to_string(from_ep_pos),
|
||||
minetest.pos_to_string(closest_pos),
|
||||
vector.distance(from_ep_pos, closest_pos)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Loop again to blow away no path nodes
|
||||
for _, from in ipairs(dist_keys) do
|
||||
for _, from_ep in ipairs(pathends[from]) do
|
||||
local from_ep_pos = minetest.string_to_pos(from_ep)
|
||||
local no_paths_nodes = minetest.find_nodes_in_area(
|
||||
vector.offset(from_ep_pos, -32, -32, -32),
|
||||
vector.offset(from_ep_pos, 32, 32, 32),
|
||||
{ "mcl_villages:no_paths" }
|
||||
)
|
||||
if #no_paths_nodes > 0 then
|
||||
minetest.bulk_set_node(no_paths_nodes, { name = "air" })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
path_ends["block_" .. blockseed] = nil
|
||||
end
|
||||
|
|
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 458 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 459 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 459 B |
After Width: | Height: | Size: 461 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 162 B |
|
@ -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
|
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 462 B |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|