mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-17 00:21:07 +01:00
clean up villages code, add biome farming support
This commit is contained in:
parent
92cffe42b4
commit
eaaa2a4dd2
6 changed files with 313 additions and 582 deletions
|
@ -6,39 +6,28 @@ 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', ""))
|
||||
count = count + select(2, string.gsub(schem_lua, '"mcl_cauldrons:cauldron', ""))
|
||||
else
|
||||
local name = string.sub(n, 6, -1)
|
||||
local num = select(2, string.gsub(str, name, ""))
|
||||
local num = select(2, string.gsub(schem_lua, name, ""))
|
||||
if num then
|
||||
minetest.log(
|
||||
"info",
|
||||
string.format("[mcl_villages] Guessing how to handle %s counting it as %d job sites", name, num)
|
||||
)
|
||||
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)
|
||||
)
|
||||
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 .. '"', ""))
|
||||
count = count + select(2, string.gsub(schem_lua, '{name="' .. n .. '"', ""))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -62,18 +51,13 @@ 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
|
||||
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
|
||||
|
||||
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
|
||||
|
@ -105,23 +89,15 @@ 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
|
||||
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
|
||||
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)
|
||||
|
@ -132,102 +108,42 @@ function mcl_villages.register_building(record)
|
|||
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"
|
||||
function mcl_villages.register_crop(crop_def)
|
||||
local crops = crop_list[crop_def.type] or {}
|
||||
for biome, weight in pairs(crop_def.biomes) do
|
||||
if crops[biome] == nil then crops[biome] = {} end
|
||||
crops[biome][crop_def.node] = weight
|
||||
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)
|
||||
crop_list[crop_def.type] = crops
|
||||
end
|
||||
|
||||
function mcl_villages.get_crop_types()
|
||||
return table.copy(supported_crop_types)
|
||||
local ret = {}
|
||||
for k, _ in pairs(crop_list) do
|
||||
table.insert(ret, k)
|
||||
end
|
||||
|
||||
function mcl_villages.get_crops()
|
||||
return table.copy(crop_list)
|
||||
return ret
|
||||
end
|
||||
|
||||
function mcl_villages.get_weighted_crop(biome, crop_type, pr)
|
||||
if weighted_crops[biome] == nil then
|
||||
biome = "plains"
|
||||
end
|
||||
local crops = crop_list[crop_type]
|
||||
if not crops then return end -- unknown crop
|
||||
local crops = crops[biome] or crops["plains"]
|
||||
|
||||
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
|
||||
local total = 0
|
||||
for _, weight in pairs(crops) do total = total + weight end
|
||||
|
||||
local rand = pr:next(0, 1e7) * 1e-7 * total
|
||||
for node, weight in pairs(crops) do
|
||||
if rand <= weight then
|
||||
return node
|
||||
end
|
||||
rand = rand - weight
|
||||
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
|
||||
|
@ -235,3 +151,4 @@ end
|
|||
function mcl_villages.register_on_villager_spawned(func)
|
||||
table.insert(mcl_villages.on_villager_placed, func)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,59 +2,8 @@ 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 foundation_materials = {}
|
||||
foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
|
||||
foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone"
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
-- initialize settlement_info
|
||||
function mcl_villages.initialize_settlement_info(pr)
|
||||
local count_buildings = {
|
||||
number_of_jobs = pr:next(min_jobs, max_jobs),
|
||||
num_jobs = 0,
|
||||
num_beds = 0,
|
||||
}
|
||||
return count_buildings
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- evaluate settlement_info and place schematics
|
||||
-------------------------------------------------------------------------------
|
||||
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 _ = 1, math.random(3) do
|
||||
local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat")
|
||||
if v and v:get_luaentity() then v:get_luaentity()._home = pos end
|
||||
end
|
||||
end
|
||||
|
||||
local function init_nodes(p1, p2, pr)
|
||||
vl_structures.construct_nodes(p1, p2, {
|
||||
"mcl_itemframes:item_frame",
|
||||
"mcl_itemframes:glow_item_frame",
|
||||
"mcl_furnaces:furnace",
|
||||
"mcl_anvils:anvil",
|
||||
"mcl_books:bookshelf",
|
||||
"mcl_armor_stand:armor_stand",
|
||||
-- "mcl_smoker:smoker",
|
||||
-- "mcl_barrels:barrel_closed",
|
||||
-- "mcl_blast_furnace:blast_furnace",
|
||||
-- "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
|
||||
vl_structures.init_node_construct(v)
|
||||
end
|
||||
|
||||
local nodes = vl_structures.construct_nodes(p1, p2, {"mcl_chests:chest_small", "mcl_chests:chest" }) or {}
|
||||
for p=1, #nodes do
|
||||
mcl_villages.fill_chest(nodes[p], pr)
|
||||
end
|
||||
end
|
||||
|
||||
local function add_building(settlement, building, count_buildings)
|
||||
table.insert(settlement, building)
|
||||
count_buildings[building.name] = (count_buildings[building.name] or 0) + 1
|
||||
|
@ -138,7 +87,7 @@ function mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
|||
local settlement = {}
|
||||
|
||||
-- initialize all settlement_info table
|
||||
local count_buildings = mcl_villages.initialize_settlement_info(pr)
|
||||
local count_buildings = { num_jobs = 0, num_beds = 0, target_jobs = pr:next(min_jobs, max_jobs) }
|
||||
|
||||
-- first building is townhall in the center
|
||||
local bindex = pr:next(1, #mcl_villages.schematic_bells)
|
||||
|
@ -151,13 +100,13 @@ function mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
|||
end
|
||||
end
|
||||
|
||||
while count_buildings.num_jobs < count_buildings.number_of_jobs do
|
||||
while count_buildings.num_jobs < count_buildings.target_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)
|
||||
(building_info.min_jobs == nil or count_buildings.target_jobs >= building_info.min_jobs)
|
||||
and (building_info.max_jobs == nil or count_buildings.target_jobs <= building_info.max_jobs)
|
||||
and (
|
||||
building_info.num_others == nil
|
||||
or (count_buildings[building_info.group or building_info.name] or 0) == 0
|
||||
|
@ -180,8 +129,8 @@ function mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
|||
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)
|
||||
(building_info.min_jobs == nil or count_buildings.target_jobs >= building_info.min_jobs)
|
||||
and (building_info.max_jobs == nil or count_buildings.target_jobs <= building_info.max_jobs)
|
||||
and (
|
||||
building_info.num_others == nil
|
||||
or (count_buildings[building_info.group or building_info.name] or 0) == 0
|
||||
|
@ -212,12 +161,32 @@ function mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
|||
return layout_town(vm, minp, maxp, pr, settlement)
|
||||
end
|
||||
|
||||
local function init_nodes(p1, p2, pr)
|
||||
vl_structures.construct_nodes(p1, p2, {
|
||||
"mcl_itemframes:item_frame",
|
||||
"mcl_itemframes:glow_item_frame",
|
||||
"mcl_furnaces:furnace",
|
||||
"mcl_anvils:anvil",
|
||||
"mcl_books:bookshelf",
|
||||
"mcl_armor_stand:armor_stand",
|
||||
-- jobsite: "mcl_smoker:smoker",
|
||||
-- jobsite: "mcl_barrels:barrel_closed",
|
||||
-- jobsite: "mcl_blast_furnace:blast_furnace",
|
||||
-- jobsite: "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 vl_structures.init_node_construct(v) end
|
||||
|
||||
local nodes = vl_structures.construct_nodes(p1, p2, {"mcl_chests:chest_small", "mcl_chests:chest" })
|
||||
for _, n in pairs(nodes) do mcl_villages.fill_chest(n, pr) end
|
||||
end
|
||||
|
||||
-- important: the vm will be written and then is outdated!
|
||||
function mcl_villages.place_schematics(vm, settlement, blockseed, pr)
|
||||
-- local vm = VoxelManip()
|
||||
-- first building is always the bell
|
||||
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
|
||||
|
@ -232,87 +201,60 @@ function mcl_villages.place_schematics(vm, settlement, blockseed, pr)
|
|||
local schematic = loadstring(schem_lua)()
|
||||
|
||||
-- the foundation and air space for the building was already built before
|
||||
-- vm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z))
|
||||
-- vm: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(
|
||||
vm,
|
||||
minp,
|
||||
schematic,
|
||||
rotation,
|
||||
{ ["mcl_core:dirt_with_grass"]=schematic.surface_mat or "mcl_core:dirt" },
|
||||
true,
|
||||
{ place_center_x = false, place_center_y = false, place_center_z = false }
|
||||
)
|
||||
-- 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 = vm:get_node_at(p)
|
||||
if n and n.name == "mcl_villages:no_paths" then
|
||||
p.y = y+1
|
||||
n = vm:get_node_at(p)
|
||||
if n and n.name == "air" then
|
||||
vm:set_node_at(p, {name="mcl_villages:no_paths"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.place_schematic_on_vmanip(vm, minp, schematic, rotation, nil, true, { place_center_x = false, place_center_y = false, place_center_z = false })
|
||||
mcl_villages.store_path_ends(vm, minp, maxp, cpos, blockseed, bell_pos)
|
||||
|
||||
if building.name == "belltower" then -- TODO: allow multiple types?
|
||||
bell_center_pos = cpos
|
||||
local center_node = vm:get_node_at(cpos)
|
||||
bell_center_node_type = center_node.name
|
||||
end
|
||||
mcl_villages.increase_no_paths(vm, minp, maxp) -- help the path finder
|
||||
end
|
||||
|
||||
vm: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)
|
||||
|
||||
-- Path planning and placement
|
||||
mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome))
|
||||
-- Clean up paths and initialize nodes
|
||||
for i, building in ipairs(settlement) do
|
||||
mcl_villages.clean_no_paths(building.minp, building.maxp)
|
||||
init_nodes(building.minp, building.maxp, pr)
|
||||
end
|
||||
|
||||
-- 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)
|
||||
-- Replace center block with a temporary block, which will be used run delayed actions
|
||||
local block_name = minetest.get_node(bell_pos).name -- to restore the node afterwards
|
||||
minetest.swap_node(bell_pos, { name = "mcl_villages:village_block" })
|
||||
local meta = minetest.get_meta(bell_pos)
|
||||
meta:set_string("node_type", block_name)
|
||||
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)
|
||||
|
||||
-- read back any changes (fixme: would be better if we would not need this often.
|
||||
--local emin, emax = vm:get_emerged_area()
|
||||
--vm:read_from_map(emin, emax)
|
||||
meta:set_string("infotext", S("The timer for this village has not run yet!"))
|
||||
minetest.get_node_timer(bell_pos):start(1.0)
|
||||
end
|
||||
|
||||
minetest.register_node("mcl_villages:village_block", {
|
||||
drawtype = "glasslike",
|
||||
groups = { not_in_creative_inventory = 1 },
|
||||
light_source = 14, -- This is a light source so that lamps don't get placed near it
|
||||
-- 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, _)
|
||||
local meta = minetest.get_meta(pos)
|
||||
minetest.swap_node(pos, { name = meta:get_string("node_type") })
|
||||
mcl_villages.post_process_village(meta:get_string("blockseed"))
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
function mcl_villages.post_process_village(blockseed)
|
||||
local village_info = mcl_villages.get_village(blockseed)
|
||||
if not village_info then
|
||||
return
|
||||
end
|
||||
if not village_info then return end
|
||||
-- minetest.log("Postprocessing village")
|
||||
|
||||
local settlement_info = village_info.data
|
||||
local jobs = {}
|
||||
local beds = {}
|
||||
local jobs, 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 bell_pos = vector.copy(settlement_info[1].pos)
|
||||
local bell = vector.offset(bell_pos, 0, 1, 0)
|
||||
local biome_name = minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome)
|
||||
|
||||
-- Spawn Golem
|
||||
local l = minetest.add_entity(bell, "mobs_mc:iron_golem"):get_luaentity()
|
||||
if l then
|
||||
l._home = bell
|
||||
|
@ -320,45 +262,41 @@ function mcl_villages.post_process_village(blockseed)
|
|||
minetest.log("info", "Could not create a golem!")
|
||||
end
|
||||
|
||||
spawn_cats(bell)
|
||||
-- Spawn cats
|
||||
local sp = minetest.find_nodes_in_area_under_air(vector.offset(bell, -20, -10, -20),vector.offset(bell, 20, 10, 20), { "group:opaque" })
|
||||
for _ = 1, math.random(3) do
|
||||
local v = minetest.add_entity(vector.offset(sp[math.random(#sp)], 0, 1, 0), "mobs_mc:cat")
|
||||
if v and v:get_luaentity() then
|
||||
v:get_luaentity()._home = bell_pos -- help them stay local
|
||||
else
|
||||
minetest.log("info", "Could not spawn a cat")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- collect beds and job sites
|
||||
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 minp, maxp = building.minp, building.maxp
|
||||
if building.num_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
|
||||
for _, job_pos in pairs(jobsites) do table.insert(jobs, job_pos) end
|
||||
end
|
||||
|
||||
if has_beds then
|
||||
if building.num_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 = minetest.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
|
||||
local bed_group = minetest.get_item_group(minetest.get_node(bed_pos).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
|
||||
-- TODO: shuffle jobs?
|
||||
|
||||
-- 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
|
||||
minetest.forceload_block(bed_pos, true)
|
||||
local m = minetest.get_meta(bed_pos)
|
||||
m:set_string("bell_pos", minetest.pos_to_string(bell_pos))
|
||||
if m:get_string("villager") == "" then
|
||||
|
@ -371,18 +309,13 @@ function mcl_villages.post_process_village(blockseed)
|
|||
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
|
||||
if job_pos then villager_employ(l, job_pos) end -- HACK: merge more MCLA villager job code?
|
||||
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"))
|
||||
minetest.log("info", "bed already owned by " .. m:get_string("villager")) -- should not happen unless villages overlap
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -395,7 +328,8 @@ function mcl_villages.terraform(vm, settlement, pr)
|
|||
for i, building in ipairs(settlement) do
|
||||
if not building.no_clearance then
|
||||
local pos, size = building.pos, building.size
|
||||
pos = vector.offset(pos, -math.floor(size.x/2), 0, -math.floor(size.z/2))
|
||||
pos = vector.offset(pos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
|
||||
-- TODO: allow different clearance for different buildings?
|
||||
vl_terraforming.clearance_vm(vm, pos.x-1, pos.y, pos.z-1, size.x+2, size.y, size.z+2, 2, building.surface_mat, building.dust_mat, pr)
|
||||
end
|
||||
end
|
||||
|
@ -403,8 +337,8 @@ function mcl_villages.terraform(vm, settlement, pr)
|
|||
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" }
|
||||
local stone_mat = building.stone_mat or { name = "mcl_core:stone" }
|
||||
local platform_mat = building.platform_mat or { name = mcl_villages.foundation_materials[surface_mat.name] or "mcl_core:dirt" }
|
||||
local stone_mat = building.stone_mat or { name = mcl_villages.stone_materials[surface_mat.name] or "mcl_core:stone" }
|
||||
local dust_mat = building.dust_mat
|
||||
building.platform_mat = platform_mat -- remember for use in schematic placement
|
||||
building.stone_mat = stone_mat
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
-- switch for debugging
|
||||
function mcl_villages.debug(message)
|
||||
minetest.log("verbose", "[mcl_villages] "..message)
|
||||
end
|
||||
-- maximum allowed difference in height for building a settlement
|
||||
mcl_villages.max_height_difference = 56
|
||||
|
||||
-- legacy type in old schematics
|
||||
minetest.register_alias("mcl_villages:stonebrickcarved", "mcl_core:stonebrickcarved")
|
||||
|
||||
-- possible surfaces where buildings can be built
|
||||
mcl_villages.surface_mat = {}
|
||||
|
@ -30,10 +31,15 @@ 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
|
||||
|
||||
-- maximum allowed difference in height for building a settlement
|
||||
mcl_villages.max_height_difference = 56
|
||||
-- substitute foundation materials
|
||||
mcl_villages.foundation_materials = {}
|
||||
mcl_villages.foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
|
||||
mcl_villages.foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone"
|
||||
|
||||
mcl_villages.half_map_chunk_size = 40
|
||||
-- substitute stone materials in foundation
|
||||
mcl_villages.stone_materials = {}
|
||||
|
||||
mcl_villages.default_crop = "mcl_farming:wheat_1"
|
||||
|
||||
--
|
||||
-- Biome based block substitutions
|
||||
|
@ -119,16 +125,6 @@ mcl_villages.vl_to_mcla = {
|
|||
{ '"mcl_bamboo:bamboo_door', '"mcl_doors:door_bamboo'},
|
||||
}
|
||||
mcl_villages.mcla_to_vl = {
|
||||
-- oneway
|
||||
--{ '"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
|
||||
{ '"mcl_villages:crop_flower_0"', '"mcl_flowers:tulip_red"'}, -- TODO: support biome specific farming
|
||||
{ '"mcl_villages:crop_flower_1"', '"mcl_flowers:tulip_orange"'}, -- TODO: support biome specific farming
|
||||
{ '"mcl_villages:crop_flower_2"', '"mcl_flowers:tulip_pink"'}, -- TODO: support biome specific farming
|
||||
{ '"mcl_villages:crop_flower_3"', '"mcl_flowers:tulip_white"'}, -- TODO: support biome specific farming
|
||||
-- bidirectional
|
||||
{ '"mcl_trees:tree_oak"', '"mcl_core:tree"'},
|
||||
{ '"mcl_trees:tree_dark_oak"', '"mcl_core:darktree"'},
|
||||
|
|
|
@ -11,37 +11,23 @@ dofile(mcl_villages.modpath.."/api.lua")
|
|||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
minetest.register_alias("mcl_villages:stonebrickcarved", "mcl_core:stonebrickcarved")
|
||||
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:building_block", "air")
|
||||
--
|
||||
-- on map generation, try to build a settlement
|
||||
--
|
||||
local function build_a_settlement(minp, maxp, blockseed)
|
||||
if mcl_villages.village_exists(blockseed) then return end
|
||||
local pr = PcgRandom(blockseed)
|
||||
local vm = VoxelManip(minp, maxp)
|
||||
local settlement = mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
||||
local function ecb_village(blockpos, action, calls_remaining, param)
|
||||
if calls_remaining >= 1 then return end
|
||||
if mcl_villages.village_exists(param.blockseed) then return end
|
||||
local pr = PcgRandom(param.blockseed)
|
||||
local vm = VoxelManip(param.minp, param.maxp)
|
||||
local settlement = mcl_villages.create_site_plan(vm, param.minp, param.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(vm, settlement, pr)
|
||||
mcl_villages.place_schematics(vm, settlement, blockseed, pr)
|
||||
mcl_villages.add_village(blockseed, settlement)
|
||||
--lvm:write_to_map(true) -- destory paths as of now
|
||||
--mcl_villages.paths(blockseed) -- TODO: biome
|
||||
mcl_villages.place_schematics(vm, settlement, param.blockseed, pr)
|
||||
mcl_villages.add_village(param.blockseed, settlement)
|
||||
--lvm:write_to_map(true) -- destorys paths as of now, as they are placed afterwards
|
||||
for _, on_village_placed_callback in pairs(mcl_villages.on_village_placed) do
|
||||
on_village_placed_callback(settlement, blockseed)
|
||||
on_village_placed_callback(settlement, param.blockseed)
|
||||
end
|
||||
end
|
||||
|
||||
local function ecb_village(blockpos, action, calls_remaining, param)
|
||||
if calls_remaining >= 1 then return end
|
||||
local minp, maxp, blockseed = param.minp, param.maxp, param.blockseed
|
||||
build_a_settlement(minp, maxp, blockseed)
|
||||
end
|
||||
|
||||
-- Disable natural generation in singlenode.
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
if mg_name ~= "singlenode" then
|
||||
|
@ -50,85 +36,22 @@ if mg_name ~= "singlenode" then
|
|||
if village_boost == 0 then return end
|
||||
local pr = PcgRandom(blockseed)
|
||||
if pr:next(0,1e9) * 100e-9 >= village_boost then return end
|
||||
local big_minp = vector.copy(minp) --vector.offset(minp, -16, -16, -16)
|
||||
local big_maxp = vector.copy(maxp) --vector.offset(maxp, 16, 16, 16)
|
||||
minetest.emerge_area(big_minp, big_maxp, ecb_village,
|
||||
{ minp = vector.copy(minp), maxp = vector.copy(maxp), blockseed = blockseed }
|
||||
)
|
||||
if village_boost < 25 then -- otherwise, this tends to transitively emerge too much
|
||||
minp, maxp = vector.offset(minp, -16, 0, -16), vector.offset(maxp, 16, 0, 16)
|
||||
end
|
||||
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed })
|
||||
end)
|
||||
--[[ 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
|
||||
|
||||
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,1e9) * 100e-9 > village_boost 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)
|
||||
]]--
|
||||
--[[ causes issues when vertically close to the chunk boundary:
|
||||
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,1e9) * 10ee-9 > village_boost 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, _)
|
||||
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,
|
||||
})
|
||||
|
||||
-- struct to cause a village spawn when it can be fully emerged
|
||||
-- Handle legacy structblocks that are not fully emerged yet.
|
||||
minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1}})
|
||||
minetest.register_lbm({
|
||||
name = "mcl_villages:structblock",
|
||||
run_at_every_load = true,
|
||||
nodenames = {"mcl_villages:structblock"},
|
||||
action = function(pos, node)
|
||||
minetest.set_node(pos, {name = "air"})
|
||||
local minp=vector.offset(pos, -40, -40, -40)
|
||||
local maxp=vector.offset(pos, 40, 40, 40)
|
||||
local minp, maxp = vector.offset(pos, -40, -40, -40), vector.offset(pos, 40, 40, 40)
|
||||
local blockseed = PcgRandom(minetest.hash_node_position(pos)):next()
|
||||
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed})
|
||||
end
|
||||
|
@ -146,24 +69,21 @@ if minetest.is_creative_enabled("") then
|
|||
minetest.chat_send_player(placer:get_player_name(), S("Placement denied. You need the “server” privilege to place villages."))
|
||||
return
|
||||
end
|
||||
local minp = vector.subtract(pointed_thing.under, mcl_villages.half_map_chunk_size)
|
||||
local maxp = vector.add(pointed_thing.under, mcl_villages.half_map_chunk_size)
|
||||
build_a_settlement(minp, maxp, math.random(0,32767))
|
||||
local pos = pointed_thing.under
|
||||
local minp, maxp = vector.offset(pos, -40, -40, -40), vector.offset(pos, 40, 40, 40)
|
||||
local blockseed = PcgRandom(minetest.hash_node_position(pos)):next()
|
||||
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed })
|
||||
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
|
||||
local drawtype = minetest.is_creative_enabled("") and "glasslike" or "airlike"
|
||||
|
||||
-- Special node for schematics editing: no path on this place
|
||||
minetest.register_node("mcl_villages:no_paths", {
|
||||
description = S(
|
||||
"Prevent paths from being placed during villager generation. Replaced by air after village path generation"
|
||||
),
|
||||
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",
|
||||
|
@ -173,6 +93,7 @@ minetest.register_node("mcl_villages:no_paths", {
|
|||
groups = { creative_breakable = 1, not_solid = 1, not_in_creative_inventory = 1 },
|
||||
})
|
||||
|
||||
-- Special node for schematics editing: path endpoint
|
||||
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,
|
||||
|
@ -184,12 +105,7 @@ minetest.register_node("mcl_villages:path_endpoint", {
|
|||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -8 / 16, -8 / 16, -8 / 16, 8 / 16, -7 / 16, 8 / 16 },
|
||||
},
|
||||
},
|
||||
node_box = { type = "fixed", fixed = { { -0.5, -0.5, -0.5, 0.5, -0.45, 0.5 } } },
|
||||
_mcl_hardness = 0.1,
|
||||
_mcl_blast_resistance = 0.1,
|
||||
})
|
||||
|
@ -412,31 +328,6 @@ mcl_villages.register_building({
|
|||
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",
|
||||
|
@ -512,8 +403,9 @@ mcl_villages.register_crop({
|
|||
},
|
||||
})
|
||||
|
||||
-- TODO: make flowers biome-specific
|
||||
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
|
||||
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,
|
||||
|
@ -528,3 +420,25 @@ for name, def in pairs(minetest.registered_nodes) do
|
|||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Crop placeholder nodes at different growth stages, for designing schematics
|
||||
for _, crop_type in ipairs(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 = { { -0.5, -0.5, -0.5, 0.5, -0.45, 0.5 } } },
|
||||
_mcl_hardness = 0.1,
|
||||
_mcl_blast_resistance = 0.1,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
-------------------------------------------------------------------------------
|
||||
-- generate paths between buildings
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local light_threshold = tonumber(minetest.settings:get("mcl_villages_light_threshold")) or 5
|
||||
|
||||
-- This ends up being a nested table.
|
||||
|
@ -10,8 +9,40 @@ local light_threshold = tonumber(minetest.settings:get("mcl_villages_light_thres
|
|||
-- 3rd is the pos of the end points
|
||||
local path_ends = {}
|
||||
|
||||
-- note: not using LVM here, as this runs after the pathfinder
|
||||
-- simple function to increase "no_paths" walls
|
||||
function mcl_villages.clean_no_paths(minp, maxp)
|
||||
local no_paths_nodes = minetest.find_nodes_in_area(minp, maxp, { "mcl_villages:no_paths" })
|
||||
if #no_paths_nodes > 0 then
|
||||
minetest.bulk_set_node(no_paths_nodes, { name = "air" })
|
||||
end
|
||||
end
|
||||
|
||||
-- this can still run in LVM
|
||||
-- simple function to increase "no_paths" walls
|
||||
function mcl_villages.increase_no_paths(vm, minp, maxp)
|
||||
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 = vm:get_node_at(p)
|
||||
if n and n.name == "mcl_villages:no_paths" then
|
||||
p.y = y + 1
|
||||
n = vm:get_node_at(p)
|
||||
if n and n.name == "air" then
|
||||
vm:set_node_at(p, {name = "mcl_villages:no_paths" })
|
||||
end
|
||||
end
|
||||
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)
|
||||
function mcl_villages.store_path_ends(vm, 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
|
||||
|
@ -21,20 +52,18 @@ function mcl_villages.store_path_ends(lvm, minp, maxp, pos, blockseed, bell_pos)
|
|||
path_ends[id] = tab
|
||||
end
|
||||
if tab[dist] == nil then tab[dist] = {} end
|
||||
-- TODO: improve, use LVM data instead of nodes
|
||||
-- TODO: use LVM data instead of nodes? But we only process a subset of the area
|
||||
local v = vector.zero()
|
||||
local i = 0
|
||||
for zi = minp.z-2, maxp.z+2 do
|
||||
for zi = minp.z, maxp.z do
|
||||
v.z = zi
|
||||
for yi = minp.y-2, maxp.y+2 do
|
||||
for yi = minp.y, maxp.y do
|
||||
v.y = yi
|
||||
for xi = minp.x-2, maxp.x+2 do
|
||||
for xi = minp.x, maxp.x do
|
||||
v.x = xi
|
||||
local n = lvm:get_node_at(v)
|
||||
local n = vm: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" })
|
||||
vm:set_node_at(v, { name = "air" })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -47,16 +76,14 @@ local function place_lamp(pos, pr)
|
|||
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",
|
||||
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
|
||||
|
||||
-- TODO: port this to lvm.
|
||||
local function smooth_path(path)
|
||||
-- Smooth out bumps in path or stairs can look naf
|
||||
for pass = 1, 3 do
|
||||
|
@ -64,10 +91,10 @@ local function smooth_path(path)
|
|||
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])
|
||||
local bump = minetest.get_node(path[i]).name
|
||||
|
||||
-- TODO: replace bamboo underneath with dirt here?
|
||||
if minetest.get_item_group(bump_node.name, "water") ~= 0 then
|
||||
-- TODO: also replace bamboo underneath with dirt here?
|
||||
if minetest.get_item_group(bump, "water") ~= 0 then
|
||||
-- ignore in this pass
|
||||
elseif y >= next_y + 2 and y <= prev_y then
|
||||
minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" })
|
||||
|
@ -94,25 +121,25 @@ local function smooth_path(path)
|
|||
end
|
||||
end
|
||||
|
||||
-- TODO: port this to lvm.
|
||||
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])
|
||||
local bump = minetest.get_node(path[i]).name
|
||||
|
||||
if minetest.get_item_group(bump_node.name, "water") ~= 0 then
|
||||
if minetest.get_item_group(bump, "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
|
||||
local up_pos = vector.copy(path[i])
|
||||
while true do
|
||||
up_pos.y = up_pos.y + 1
|
||||
local up_node = minetest.get_node(up_pos).name
|
||||
if minetest.get_item_group(up_node, "water") == 0 then
|
||||
minetest.swap_node(up_pos, { name = "air" })
|
||||
path[i] = up_pos
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif y < prev_y and y < next_y then
|
||||
|
@ -129,36 +156,32 @@ local function place_path(path, pr, stair, slab)
|
|||
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 n0 = minetest.get_node(pos).name
|
||||
if n0 ~= "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 n = minetest.get_node(under_pos).name
|
||||
local ndef = minetest.registered_nodes[n]
|
||||
local groups = ndef and ndef.groups or {}
|
||||
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
|
||||
if not groups.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
|
||||
if not groups.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
|
||||
elseif not groups.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
|
||||
local n2 = minetest.get_node(vector.offset(path[i - 1], 0, -1, 0)).name
|
||||
if not minetest.get_item_group(n2, "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?
|
||||
|
@ -167,11 +190,10 @@ local function place_path(path, pr, stair, slab)
|
|||
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
|
||||
elseif not groups.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
|
||||
local n2 = minetest.get_node(vector.offset(path[i + 1], 0, -1, 0)).name
|
||||
if not minetest.get_item_group(n2, "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?
|
||||
|
@ -184,13 +206,11 @@ local function place_path(path, pr, stair, slab)
|
|||
|
||||
-- flat
|
||||
if not done then
|
||||
if minetest.get_item_group(n.name, "water") ~= 0 then
|
||||
if groups.water then
|
||||
minetest.add_node(under_pos, { name = slab })
|
||||
elseif n.name == "mcl_core:sand" or n.name == "mcl_core:redsand" then
|
||||
elseif groups.sand 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
|
||||
elseif groups.soil and not groups.dirtifies_below_solid then
|
||||
minetest.swap_node(under_pos, { name = "mcl_core:grass_path" })
|
||||
end
|
||||
end
|
||||
|
@ -198,8 +218,7 @@ local function place_path(path, pr, stair, slab)
|
|||
-- 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
|
||||
if minetest.get_node(over_pos).name ~= "air" then
|
||||
minetest.swap_node(over_pos, { name = "air" })
|
||||
end
|
||||
end
|
||||
|
@ -208,17 +227,15 @@ local function place_path(path, pr, stair, slab)
|
|||
-- 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),
|
||||
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" }
|
||||
)
|
||||
-- todo: shuffle nn?
|
||||
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 })
|
||||
local node = minetest.get_node(npos).name
|
||||
if node ~= "mcl_core:grass_path" and minetest.get_item_group(node, "stair") == 0 then
|
||||
if minetest.get_item_group(node, "wood_slab") ~= 0 then
|
||||
minetest.add_node(vector.offset(npos, 0, 1, 0), { name = "mcl_torches:torch", param2 = 1 })
|
||||
else
|
||||
place_lamp(npos, pr)
|
||||
end
|
||||
|
@ -229,20 +246,11 @@ local function place_path(path, pr, stair, slab)
|
|||
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
|
||||
|
||||
-- FIXME: ugly
|
||||
function get_biome_stair_slab(biome_name)
|
||||
-- Use the same stair and slab throughout the entire village
|
||||
-- The quotes are necessary to be matched as JSON strings
|
||||
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
|
||||
|
@ -255,44 +263,45 @@ function mcl_villages.paths(blockseed, biome_name)
|
|||
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('"', "")
|
||||
-- The quotes are to match what is in JSON schemas, but we don't want them now
|
||||
return stair:gsub('"', ""), slab:gsub('"', "")
|
||||
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 = PcgRandom(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
|
||||
|
||||
-- Stair and slab style of the village
|
||||
local stair, slab = get_biome_stair_slab(biome_name)
|
||||
-- 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)
|
||||
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
|
||||
local closest_pos, closest_bld, best = nil, nil, 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 j = from == 0 and 1 or (i + 1)
|
||||
for j = j, #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
|
||||
|
@ -315,10 +324,9 @@ function mcl_villages.paths(blockseed, biome_name)
|
|||
place_path(path, pr, stair, slab)
|
||||
connected[from .. "-" .. closest_bld] = 1
|
||||
else
|
||||
minetest.log(
|
||||
"warning",
|
||||
minetest.log("warning",
|
||||
string.format(
|
||||
"[mcl_villages] No path from %s to %s, distance %d",
|
||||
"[mcl_villages] No good 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)
|
||||
|
@ -329,20 +337,5 @@ function mcl_villages.paths(blockseed, biome_name)
|
|||
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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
-- check the minimum distance of two squares, on axes
|
||||
-- TODO: make local in village planning code only?
|
||||
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
|
||||
|
@ -22,8 +23,7 @@ function mcl_villages.fill_chest(pos, pr)
|
|||
end
|
||||
-- fill chest
|
||||
local inv = minetest.get_inventory( {type="node", pos=pos} )
|
||||
|
||||
local function get_treasures(prand)
|
||||
local function get_treasures(pr)
|
||||
local loottable = {{
|
||||
stacks_min = 3,
|
||||
stacks_max = 8,
|
||||
|
@ -47,46 +47,11 @@ function mcl_villages.fill_chest(pos, pr)
|
|||
{ itemstring = "mcl_mobitems:diamond_horse_armor", weight = 1 },
|
||||
}
|
||||
}}
|
||||
local items = mcl_loot.get_multi_loot(loottable, prand)
|
||||
return items
|
||||
return mcl_loot.get_multi_loot(loottable, pr)
|
||||
end
|
||||
mcl_loot.fill_inventory(inv, "main", get_treasures(pr), pr)
|
||||
end
|
||||
|
||||
local items = get_treasures(pr)
|
||||
mcl_loot.fill_inventory(inv, "main", items, pr)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- initialize furnace
|
||||
-------------------------------------------------------------------------------
|
||||
function mcl_villages.initialize_furnace(pos)
|
||||
-- find chests within radius
|
||||
local furnacepos = minetest.find_node_near(pos,
|
||||
7, --radius
|
||||
{"mcl_furnaces:furnace"})
|
||||
-- initialize furnacepos (mts furnacepos don't have meta)
|
||||
if furnacepos then
|
||||
local meta = minetest.get_meta(furnacepos)
|
||||
if meta:get_string("infotext") ~= "furnace" then
|
||||
minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos)
|
||||
end
|
||||
end
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- initialize anvil
|
||||
-------------------------------------------------------------------------------
|
||||
function mcl_villages.initialize_anvil(pos)
|
||||
-- find chests within radius
|
||||
local anvilpos = minetest.find_node_near(pos,
|
||||
7, --radius
|
||||
{"mcl_anvils:anvil"})
|
||||
-- initialize anvilpos (mts anvilpos don't have meta)
|
||||
if anvilpos then
|
||||
local meta = minetest.get_meta(anvilpos)
|
||||
if meta:get_string("infotext") ~= "anvil" then
|
||||
minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos)
|
||||
end
|
||||
end
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- randomize table
|
||||
-------------------------------------------------------------------------------
|
||||
|
@ -100,28 +65,41 @@ end
|
|||
|
||||
-- Load a schema and replace nodes in it based on biome
|
||||
function mcl_villages.substitute_materials(pos, schem_lua, pr)
|
||||
local modified_schem_lua = schem_lua
|
||||
local biome_data = minetest.get_biome_data(pos)
|
||||
local biome_name = minetest.get_biome_name(biome_data.biome)
|
||||
local biome_name = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
|
||||
|
||||
-- for now, map to MCLA, later back, so we can keep their rules unchanged
|
||||
for _, sub in pairs(mcl_villages.vl_to_mcla) do
|
||||
modified_schem_lua = modified_schem_lua:gsub(sub[1], sub[2])
|
||||
schem_lua = schem_lua:gsub(sub[1], sub[2])
|
||||
end
|
||||
|
||||
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
|
||||
modified_schem_lua = modified_schem_lua:gsub(sub[1], sub[2])
|
||||
schem_lua = schem_lua:gsub(sub[1], sub[2])
|
||||
end
|
||||
end
|
||||
|
||||
-- MCLA node names back to VL
|
||||
for _, sub in pairs(mcl_villages.mcla_to_vl) do
|
||||
modified_schem_lua = modified_schem_lua:gsub(sub[1], sub[2])
|
||||
end
|
||||
return modified_schem_lua
|
||||
schem_lua = schem_lua:gsub(sub[1], sub[2])
|
||||
end
|
||||
|
||||
-- Farming: place crops
|
||||
if string.find(schem_lua, "mcl_villages:crop_") then
|
||||
local map_name = mcl_villages.biome_map[biome_name] or "plains"
|
||||
for _, crop in ipairs(mcl_villages.get_crop_types()) do
|
||||
if string.find(schem_lua, "mcl_villages:crop_" .. crop) then
|
||||
for count = 1, 8 do
|
||||
local name = "mcl_villages:crop_" .. crop .. "_" .. count
|
||||
local replacement = mcl_villages.get_weighted_crop(map_name, crop, pr)
|
||||
schem_lua = schem_lua:gsub(name, replacement or mcl_villages.default_crop)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return schem_lua
|
||||
end
|
||||
|
||||
-- Persistent registry for villages
|
||||
local villages = {}
|
||||
local mod_storage = minetest.get_mod_storage()
|
||||
|
||||
|
@ -152,8 +130,7 @@ function mcl_villages.add_village(name, data)
|
|||
minetest.log("info", "Village already exists: " .. name )
|
||||
return false
|
||||
end
|
||||
|
||||
local new_village = {name = name, data = data}
|
||||
mod_storage:set_string("mcl_villages." .. name, minetest.serialize(new_village))
|
||||
mod_storage:set_string("mcl_villages." .. name, minetest.serialize({ name = name, data = data }))
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue