From 4b4e29b3c1345cbdd376419968ab53532d972a67 Mon Sep 17 00:00:00 2001 From: kay27 Date: Tue, 8 Feb 2022 07:39:01 +0400 Subject: [PATCH] Slightly rewrite villages at all --- mods/CORE/mcl_mapgen/init.lua | 4 + mods/MAPGEN/mcl_villages/buildings.lua | 208 --------- mods/MAPGEN/mcl_villages/const.lua | 81 ---- mods/MAPGEN/mcl_villages/foundation.lua | 65 --- mods/MAPGEN/mcl_villages/init.lua | 428 ++++++++++++++++--- mods/MAPGEN/mcl_villages/locale/template.txt | 2 + mods/MAPGEN/mcl_villages/mod.conf | 2 +- mods/MAPGEN/mcl_villages/paths.lua | 91 ---- mods/MAPGEN/mcl_villages/utils.lua | 198 --------- 9 files changed, 370 insertions(+), 709 deletions(-) delete mode 100644 mods/MAPGEN/mcl_villages/buildings.lua delete mode 100644 mods/MAPGEN/mcl_villages/const.lua delete mode 100644 mods/MAPGEN/mcl_villages/foundation.lua create mode 100644 mods/MAPGEN/mcl_villages/locale/template.txt delete mode 100644 mods/MAPGEN/mcl_villages/paths.lua delete mode 100644 mods/MAPGEN/mcl_villages/utils.lua diff --git a/mods/CORE/mcl_mapgen/init.lua b/mods/CORE/mcl_mapgen/init.lua index 3cc455a88..bc390e597 100644 --- a/mods/CORE/mcl_mapgen/init.lua +++ b/mods/CORE/mcl_mapgen/init.lua @@ -511,3 +511,7 @@ end function mcl_mapgen.get_chunk_ending(x) return mcl_mapgen.get_chunk_beginning(x) + LAST_NODE_IN_CHUNK end + +mcl_mapgen.get_block_seed = get_block_seed +mcl_mapgen.get_block_seed2 = get_block_seed2 +mcl_mapgen.get_block_seed3 = get_block_seed3 diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua deleted file mode 100644 index b21c0157d..000000000 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ /dev/null @@ -1,208 +0,0 @@ -function settlements.initialize_settlement_info(pr) - local count_buildings = {} - - -- count_buildings table reset - for k,v in pairs(settlements.schematic_table) do - count_buildings[v["name"]] = 0 - end - - -- randomize number of buildings - local number_of_buildings = pr:next(10, 25) - local number_built = 1 - settlements.debug("Village ".. number_of_buildings) - - return count_buildings, number_of_buildings, number_built -end -------------------------------------------------------------------------------- --- fill settlement_info --------------------------------------------------------------------------------- -local possible_rotations = {"0", "90", "180", "270"} -function settlements.create_site_plan(minp, maxp, pr) - local settlement_info = {} - local building_all_info - -- find center of chunk - local center = vector.add(minp, mcl_mapgen.HALF_CS_NODES) - -- find center_surface of chunk - local center_surface, surface_material = settlements.find_surface(center) - - -- go build settlement around center - if not center_surface then return false end - - -- add settlement to list - table.insert(settlements_in_world, center_surface) - -- save list to file - settlements.save() - -- initialize all settlement_info table - local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr) - -- first building is townhall in the center - building_all_info = settlements.schematic_table[1] - local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] - -- add to settlement info table - local index = 1 - settlement_info[index] = { - pos = center_surface, - name = building_all_info["name"], - hsize = building_all_info["hsize"], - rotat = rotation, - surface_mat = surface_material - } - --increase index for following buildings - index = index + 1 - -- now some buildings around in a circle, radius = size of town center - local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"] - -- draw j circles around center and increase radius by math.random(2,5) - for j = 1,20 do - -- set position on imaginary circle - for j = 0, 360, 15 do - local angle = j * math.pi / 180 - local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle ) - ptx = settlements.round(ptx, 0) - ptz = settlements.round(ptz, 0) - local pos1 = { x=ptx, y=center_surface.y, z=ptz} - local pos_surface, surface_material = settlements.find_surface(pos1) - if not pos_surface then break end - - local randomized_schematic_table = shuffle(settlements.schematic_table, pr) - -- pick schematic - local size = #randomized_schematic_table - for i = size, 1, -1 do - -- already enough buildings of that type? - if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then - building_all_info = randomized_schematic_table[i] - -- check distance to other buildings - local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) - if distance_to_other_buildings_ok then - -- count built houses - count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1 - rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] - number_built = number_built + 1 - settlement_info[index] = { - pos = pos_surface, - name = building_all_info["name"], - hsize = building_all_info["hsize"], - rotat = rotation, - surface_mat = surface_material - } - index = index + 1 - break - end - end - end - if number_of_buildings == number_built then - break - end - end - if number_built >= number_of_buildings then - break - end - r = r + pr:next(2,5) - end - settlements.debug("really ".. number_built) - return settlement_info -end -------------------------------------------------------------------------------- --- evaluate settlement_info and place schematics -------------------------------------------------------------------------------- --- Initialize node -local function construct_node(p1, p2, name) - local r = minetest.registered_nodes[name] - if r then - if r.on_construct then - local nodes = minetest.find_nodes_in_area(p1, p2, name) - for p=1, #nodes do - local pos = nodes[p] - r.on_construct(pos) - end - return nodes - end - minetest.log("warning", "[mcl_villages] No on_construct defined for node name " .. name) - return - end - minetest.log("warning", "[mcl_villages] Attempt to 'construct' inexistant nodes: " .. name) -end -local function init_nodes(p1, rotation, pr, size) - local p2 = vector.subtract(vector.add(p1, size), 1) - construct_node(p1, p2, "mcl_itemframes:item_frame") - construct_node(p1, p2, "mcl_furnaces:furnace") - construct_node(p1, p2, "mcl_anvils:anvil") - - local nodes = construct_node(p1, p2, "mcl_chests:chest") - if nodes and #nodes > 0 then - for p=1, #nodes do - local pos = nodes[p] - settlements.fill_chest(pos, pr) - end - end -end -function settlements.place_schematics(settlement_info, pr) - local building_all_info - for i, built_house in ipairs(settlement_info) do - for j, schem in ipairs(settlements.schematic_table) do - if settlement_info[i]["name"] == schem["name"] then - building_all_info = schem - break - end - end - - local pos = settlement_info[i]["pos"] - local rotation = settlement_info[i]["rotat"] - -- get building node material for better integration to surrounding - local platform_material = settlement_info[i]["surface_mat"] - --platform_material_name = minetest.get_name_from_content_id(platform_material) - -- pick random material - --local material = wallmaterial[pr:next(1,#wallmaterial)] - -- - local building = building_all_info["mts"] - local replace_wall = building_all_info["rplc"] - -- schematic conversion to lua - local schem_lua = minetest.serialize_schematic(building, - "lua", - {lua_use_comments = false, lua_num_indent_spaces = 0}).." return schematic" - schem_lua = schem_lua:gsub("mcl_core:stonebrickcarved", "mcl_villages:stonebrickcarved") - -- replace material - if replace_wall then - --Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs. - -- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled. - if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then - schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree") - schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood") - --schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:spruce_fence") - --schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_sprucewood_top") - --schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_sprucewood") - --schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_sprucewood_off") - elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then - schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved") - schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone") - schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth") - --schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:birch_fence") - --schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_birchwood_top") - --schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_birchwood") - --schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_birchwood_off") - --schem_lua = schem_lua:gsub("mcl_stairs:stair_stonebrick", "mcl_stairs:stair_redsandstone") - --schem_lua = schem_lua:gsub("mcl_core:stonebrick", "mcl_core:redsandstonesmooth") - schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone") - end - end - schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material) - - --[[ Disable special junglewood for now. - -- special material for spawning npcs - schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood") - --]] - - schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood") - schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air") - - -- format schematic string - local schematic = loadstring(schem_lua)() - -- build foundation for the building an make room above - -- place schematic - mcl_structures.place_schematic({ - pos = pos, - schematic = schematic, - rotation = rotation, - on_placed = init_nodes, - pr = pr, - }) - end -end diff --git a/mods/MAPGEN/mcl_villages/const.lua b/mods/MAPGEN/mcl_villages/const.lua deleted file mode 100644 index eb7806209..000000000 --- a/mods/MAPGEN/mcl_villages/const.lua +++ /dev/null @@ -1,81 +0,0 @@ --- switch for debugging -function settlements.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" -} ---]] -settlements.surface_mat = {} -------------------------------------------------------------------------------- --- Set array to list --- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list -------------------------------------------------------------------------------- -function settlements.grundstellungen() - settlements.surface_mat = settlements.Set { - "mcl_core:dirt_with_grass", - --"mcl_core:dry_dirt_with_grass", - "mcl_core:dirt_with_grass_snow", - --"mcl_core:dirt_with_dry_grass", - "mcl_core:podzol", - "mcl_core:sand", - "mcl_core:redsand", - --"mcl_core:silver_sand", - "mcl_core:snow" - } -end --- --- possible surfaces where buildings can be built --- - --- --- path to schematics --- -schem_path = settlements.modpath.."/schematics/" --- --- list of schematics --- -local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) - -settlements.schematic_table = { - {name = "large_house", mts = schem_path.."large_house.mts", hwidth = 11, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, - {name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages }, - {name = "butcher", mts = schem_path.."butcher.mts", hwidth = 11, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages }, - {name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages }, - {name = "farm", mts = schem_path.."farm.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages }, - {name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 3, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false }, - {name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages }, - {name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 8, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, - {name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 13, max_num = 0.7 , rplc = basic_pseudobiome_villages }, - {name = "tavern", mts = schem_path.."tavern.mts", hwidth = 11, hdepth = 10, hheight = 10, hsize = 13, max_num = 0.050, rplc = basic_pseudobiome_villages }, - {name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 6, hsize = 10, max_num = 0.045, rplc = basic_pseudobiome_villages }, -} - --- --- list of settlements, load on server start up --- -settlements_in_world = {} --- --- --- maximum allowed difference in height for building a sttlement --- -max_height_difference = 56 --- --- --- -half_map_chunk_size = 40 ---quarter_map_chunk_size = 20 diff --git a/mods/MAPGEN/mcl_villages/foundation.lua b/mods/MAPGEN/mcl_villages/foundation.lua deleted file mode 100644 index 71c5cfdda..000000000 --- a/mods/MAPGEN/mcl_villages/foundation.lua +++ /dev/null @@ -1,65 +0,0 @@ -------------------------------------------------------------------------------- --- function to fill empty space below baseplate when building on a hill -------------------------------------------------------------------------------- -function settlements.ground(pos, pr) -- role model: Wendelsteinkircherl, Brannenburg - local p2 = vector.new(pos) - local cnt = 0 - local mat = "mcl_core:dirt" - p2.y = p2.y-1 - while true do - cnt = cnt+1 - if cnt > 20 then break end - if cnt>pr:next(2,4) then - mat = "mcl_core:stone" - end - minetest.swap_node(p2, {name=mat}) - p2.y = p2.y-1 - end -end -------------------------------------------------------------------------------- --- function clear space above baseplate -------------------------------------------------------------------------------- -function settlements.terraform(settlement_info, pr) - local fheight, fwidth, fdepth, schematic_data - - for i, built_house in ipairs(settlement_info) do - -- pick right schematic_info to current built_house - for j, schem in ipairs(settlements.schematic_table) do - if settlement_info[i]["name"] == schem["name"] then - schematic_data = schem - break - end - end - local pos = settlement_info[i]["pos"] - if settlement_info[i]["rotat"] == "0" or settlement_info[i]["rotat"] == "180" then - fwidth = schematic_data["hwidth"] - fdepth = schematic_data["hdepth"] - else - fwidth = schematic_data["hdepth"] - fdepth = schematic_data["hwidth"] - end - --fheight = schematic_data["hheight"] * 3 -- remove trees and leaves above - fheight = schematic_data["hheight"] -- remove trees and leaves above - -- - -- now that every info is available -> create platform and clear space above - -- - for xi = 0,fwidth-1 do - for zi = 0,fdepth-1 do - for yi = 0,fheight *3 do - if yi == 0 then - local p = {x=pos.x+xi, y=pos.y, z=pos.z+zi} - settlements.ground(p, pr) - else - -- write ground --- local p = {x=pos.x+xi, y=pos.y+yi, z=pos.z+zi} --- local node = mcl_vars.get_node(p) --- if node and node.name ~= "air" then --- minetest.swap_node(p,{name="air"}) --- end - minetest.swap_node({x=pos.x+xi, y=pos.y+yi, z=pos.z+zi},{name="air"}) - end - end - end - end - end -end diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua index 386056ac6..6f563e8a5 100644 --- a/mods/MAPGEN/mcl_villages/init.lua +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -1,26 +1,338 @@ -settlements = {} -settlements.modpath = minetest.get_modpath(minetest.get_current_modname()) +mcl_villages = {} +local chunk_offset_top = 16 +local chunk_offset_bottom = 3 +local max_height_difference = 12 +local minp_min = -64 +local chance_per_chunk = 1 +local noise_multiplier = 1 +local random_offset = 1 +local random_multiply = 19 +local struct_threshold = chance_per_chunk - 1 +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local S = minetest.get_translator(modname) +local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) +local schem_path = modpath .. "/schematics/" +local schematic_table = { + {name = "large_house", mts = schem_path.."large_house.mts", hwidth = 11, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, + {name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages }, + {name = "butcher", mts = schem_path.."butcher.mts", hwidth = 11, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages }, + {name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages }, + {name = "farm", mts = schem_path.."farm.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages }, + {name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 3, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false }, + {name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages }, + {name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 8, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, + {name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 13, max_num = 0.7 , rplc = basic_pseudobiome_villages }, + {name = "tavern", mts = schem_path.."tavern.mts", hwidth = 11, hdepth = 10, hheight = 10, hsize = 13, max_num = 0.050, rplc = basic_pseudobiome_villages }, + {name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 6, hsize = 10, max_num = 0.045, rplc = basic_pseudobiome_villages }, +} +local surface_mat = { + ["mcl_core:dirt_with_dry_grass"] = true, + ["mcl_core:dirt_with_grass"] = true, + ["mcl_core:dirt_with_grass_snow"] = true, + ["mcl_core:podzol"] = true, + ["mcl_core:redsand"] = true, + ["mcl_core:sand"] = true, + ["mcl_core:snow"] = true, +} +local storage = minetest.get_mod_storage() +local villages = minetest.deserialize(storage:get_string("villages") or "return {}") or {} +local minetest_get_spawn_level = minetest.get_spawn_level +local minetest_get_node = minetest.get_node +local minetest_find_nodes_in_area = minetest.find_nodes_in_area +local mcl_structures_get_perlin_noise_level = mcl_structures.get_perlin_noise_level +local math_pi = math.pi +local math_cos = math.cos +local math_sin = math.sin +local math_floor = math.floor +local math_ceil = math.ceil +local minetest_swap_node = minetest.swap_node +local minetest_registered_nodes = minetest.registered_nodes +local air_offset = chunk_offset_top - 1 +local ground_offset = chunk_offset_bottom + 1 +local surface_search_list = {} +for k, _ in surface_mat do + table.insert(surface_search_list, k) +end -local minetest_get_spawn_level = minetest.get_spawn_level +local function math_round(x) + return (x < 0) and math_ceil(x - 0.5) or math_floor(x + 0.5) +end -dofile(settlements.modpath.."/const.lua") -dofile(settlements.modpath.."/utils.lua") -dofile(settlements.modpath.."/foundation.lua") -dofile(settlements.modpath.."/buildings.lua") -dofile(settlements.modpath.."/paths.lua") +local function find_surface(pos, minp, maxp) + local x, z = pos.x, pos.z + local y_top = maxp.y + local y_max = y_top - air_offset + if #minetest_find_nodes_in_area({x=x, y=y_max, z=z}, {x=x, y=y_top, z=z}, "air") < chunk_offset_top then return end + y_max = y_max - 1 + local y_bottom = minp.y + local y_min = y_bottom + chunk_offset_bottom + local nodes = minetest_find_nodes_in_area({x=x, y=y_min, z=z}, {x=x, y=y_max, z=z}, surface_search_list) + for _, surface_pos in pairs(nodes) do + local node_name_from_above = minetest_get_node({x=surface_pos.x, y=surface_pos.y+1, z=surface_pos.z}).name + if string.find(node_name_from_above, "air" ) + or string.find(node_name_from_above, "snow" ) + or string.find(node_name_from_above, "fern" ) + or string.find(node_name_from_above, "flower") + or string.find(node_name_from_above, "bush" ) + or string.find(node_name_from_above, "tree" ) + or string.find(node_name_from_above, "grass" ) + then return surface_pos, minetese_get_node(surface_pos).name + end +end --- --- load settlements on server --- -settlements_in_world = settlements.load() -settlements.grundstellungen() +local function get_treasures(pr) + local loottable = {{ + stacks_min = 3, + stacks_max = 8, + items = { + { itemstring = "mcl_core:diamond" , weight = 3, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_core:iron_ingot" , weight = 10, amount_min = 1, amount_max = 5 }, + { itemstring = "mcl_core:gold_ingot" , weight = 5, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_farming:bread" , weight = 15, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_core:apple" , weight = 15, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_tools:pick_iron" , weight = 5, }, + { itemstring = "mcl_tools:sword_iron" , weight = 5, }, + { itemstring = "mcl_armor:chestplate_iron" , weight = 5, }, + { itemstring = "mcl_armor:helmet_iron" , weight = 5, }, + { itemstring = "mcl_armor:leggings_iron" , weight = 5, }, + { itemstring = "mcl_armor:boots_iron" , weight = 5, }, + { itemstring = "mcl_core:obsidian" , weight = 5, amount_min = 3, amount_max = 7 }, + { itemstring = "mcl_core:sapling" , weight = 5, amount_min = 3, amount_max = 7 }, + { itemstring = "mcl_mobitems:saddle" , weight = 3, }, + { itemstring = "mobs_mc:iron_horse_armor" , weight = 1, }, + { itemstring = "mobs_mc:gold_horse_armor" , weight = 1, }, + { itemstring = "mobs_mc:diamond_horse_armor", weight = 1, }, + } + }} + local items = mcl_loot.get_multi_loot(loottable, pr) + return items +end +local function fill_chest(pos, pr) + local meta = minetest.get_meta(pos) + minetest.registered_nodes["mcl_chests:chest_small"].on_construct(pos) + local inv = minetest.get_inventory( {type="node", pos=pos} ) + local items = get_treasures(pr) + mcl_loot.fill_inventory(inv, "main", items, pr) +end + +local possible_rotations = {"0", "90", "180", "270"} + +local function get_random_rotation(pr) + return possible_rotations[pr:next(1, #possible_rotations)] +end + +local function create_site_plan(minp, maxp, pr) + local plan = {} + local building_all_info + local center = vector.add(minp, mcl_mapgen.HALF_CS_NODES) + local center_surface, surface_material = find_surface(center, minp, maxp) + if not center_surface then return end + + local number_of_buildings = pr:next(10, 25) + local shuffle = {} + local count_buildings = {} + for i = 1, #schematic_table do + shuffle[i] = i + count_buildings[i] = 0 + end + for i = #shuffle, 2, -1 do + local j = pr:next(1, i) + shuffle[i], shuffle[j] = shuffle[j], shuffle[i] + end + local number_built = 1 + local shuffle_index = pr:next(1, #schematic_table) + + -- first building is townhall in the center + plan[#plan + 1] = { + pos = center_surface, + building = schematic_table[shuffle_index], + rotation = get_random_rotation(pr), + surface_mat = surface_material, + } + count_buildings[1] = count_buildings[1] + 1 + -- now some buildings around in a circle, radius = size of town center + local x, z, r = center_surface.x, center_surface.z, schematic_table[1].hsize + -- draw j circles around center and increase radius by random(2, 5) + for k = 1, 20 do + -- set position on imaginary circle + for j = 0, 360, 15 do + local angle = j * math_pi / 180 + local pos_surface, surface_material = find_surface( + { + x = math_round(x + r * math_cos(angle)), + z = math_round(z + r * math_sin(angle)) + }, + minp, + maxp, + ) + if pos_surface then + shuffle_index = (shuffle_index % (#schematic_table)) + 1 + local schematic_index = shuffle[shuffle_index] + local schematic = schematic_table[schematic_index] + if count_buildings[schematic_index] < schematic.max_num * number_of_buildings then + local hsize2 = schematic.hsize^2 + local is_distance_ok = true + for _, built_house in pairs(plan) do + local pos = built_house.pos + local building = built_house.building + local distance2 = (pos_surface.x - pos.x)^2 + (pos_surface.z - pos.z)^2 + if distance2 < building.hsize^2 or distance < hsize2 then + is_distance_ok = false + break + end + end + if is_distance_ok then + plan[#plan + 1] = { + pos = pos_surface, + building = schematic, + rotation = get_random_rotation(pr), + surface_mat = surface_material, + } + count_buildinigs[schematic_index] = count_buildinigs[schematic_index] + 1 + number_built = number_built + 1 + break + end + end + end + if number_built >= number_of_buildings then + break + end + end + if number_built >= number_of_buildings then + break + end + r = r + pr:next(2, 5) + end + return plan +end + +local function ground(pos, minp, maxp, pr) + local p2 = vector.new(pos) + local cnt = 0 + local mat = "mcl_core:dirt" + p2.y = p2.y - 1 + local min_y = math_max(minp.y, p2.y - pr:next(17,27)) + local stone_level = p2.y - pr:next(2, 4) + while p2.y >= min_y do + if p2.y == stone_level then + mat = "mcl_core:stone" + end + minetest.swap_node(p2, {name=mat}) + p2.y = p2.y - 1 + end +end + +local function terraform(plan, minp, maxp, pr) + local fheight, fwidth, fdepth, schematic_data, pos, rotation + for _, built_house in pairs(plan) do + schematic_data = plan[i].building + pos = plan[i].pos + rotation = plan[i].rotation + if rotation == "0" or rotation = "180" then + fwidth = schematic_data.hwidth + fdepth = schematic_data.hdepth + else + fwidth = schematic_data.hdepth + fdepth = schematic_data.hwidth + end + fheight = schematic_data.hheight + for xi = pos.x, pos.x + fwidth - 1 do + for zi = pos.z, pos.z + fdepth - 1 do + for yi = pos.y, math_min(pos.y + fheight * 3, maxp.y) do + local p = {x = xi, y = yi, z = zi} + if yi == pos.y then + ground(p, pr) + else + minetest_swap_node(p, {name = "air"}) + end + end + end + end + end +end + +local function paths(plan) + local starting_point + local end_point + local distance + starting_point = plan[1].pos + for o, p in pairs(plan) do + end_point = settlement_info[o].pos + local path = minetest.find_path(starting_point, end_point, mcl_mapgen.CS_NODES, 2, 2) + if path then + for _, pos in pairs(path) do + local surface_mat = minetest.get_node(pos).name + 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 + end + end + end +end + +local function init_nodes(p1, rotation, pr, size) + local p2 = vector.subtract(vector.add(p1, size), 1) + local nodes = minetest.find_nodes_in_area(p1, p2, {"mcl_itemframes:item_frame", "mcl_furnaces:furnace", "mcl_anvils:anvil"}) + for _, pos in pairs(nodes) do + local name = minetest_get_node(pos).name + local def = minetest_registered_nodes[minetest_get_node(pos).name] + def.on_construct(pos) + if name == "mcl_chests:chest" then + fill_chest(pos, pr) + end + end +end + +local function place_schematics(plan, pr) + for _, built_house in pairs(plan) do + local pos = built_house.pos + local rotation = built_house.rotation + local platform_material = built_house.surface_mat + local building = built_house.building.mts + local replace_wall = built_house.building.rplc + local schem_lua = minetest.serialize_schematic(building, + "lua", + {lua_use_comments = false, lua_num_indent_spaces = 0}).." return schematic" + schem_lua = schem_lua:gsub("mcl_core:stonebrickcarved", "mcl_villages:stonebrickcarved") + if replace_wall then + --Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs. + -- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled. + if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then + schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree") + schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood") + elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then + schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved") + schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone") + schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth") + schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone") + end + end + schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material) + schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood") + schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air") + + local schematic = loadstring(schem_lua)() + -- build foundation for the building an make room above + -- place schematic + mcl_structures.place_schematic({ + pos = pos, + schematic = schematic, + rotation = rotation, + on_placed = init_nodes, + pr = pr, + }) + end +end -- -- register block for npc spawn -- minetest.register_node("mcl_villages:stonebrickcarved", { - description = ("Chiseled Stone Village Bricks"), + description = S("Chiseled Stone Village Bricks"), _doc_items_longdesc = doc.sub.items.temp.build, tiles = {"mcl_core_stonebrick_carved.png"}, stack_max = 64, @@ -36,73 +348,59 @@ minetest.register_node("mcl_villages:stonebrickcarved", { -- -- on map generation, try to build a settlement -- -local function build_a_settlement(minp, maxp, blockseed) - minetest.log("action","[mcl_villages] Building village at mapchunk " .. minetest.pos_to_string(minp) .. "..." .. minetest.pos_to_string(maxp) .. ", blockseed = " .. tostring(blockseed)) - local pr = PseudoRandom(blockseed) +local function build_a_settlement(minp, maxp, pr) + minetest.log("action","[mcl_villages] Building village at mapchunk " .. minetest.pos_to_string(minp) .. "..." .. minetest.pos_to_string(maxp)) + local pr = pr or PseudoRandom(mcl_mapgen.get_block_seed3(minp)) + local plan = create_site_plan(minp, maxp, pr) + if not plan then return end + terraform(plan, minp, maxp, pr) + paths(plan) + place_schematics(plan, pr) - -- fill settlement_info with buildings and their data - local settlement_info = settlements.create_site_plan(minp, maxp, pr) - if not settlement_info then return end + local center = vector.add(minp, mcl_mapgen.HALF_CS_NODES) + local center_surface = settlements.find_surface(center) + table.insert(villages, center_surface) + storage:set_string("villages", minetest.serialize(villages)) - -- evaluate settlement_info and prepair terrain - settlements.terraform(settlement_info, pr) + -- save list to file + settlements.save() - -- evaluate settlement_info and build paths between buildings - settlements.paths(settlement_info) - - -- evaluate settlement_info and place schematics - settlements.place_schematics(settlement_info, pr) end -- Disable natural generation in singlenode. -local mg_name = minetest.get_mapgen_setting("mg_name") -local scan_last_node = (mcl_mapgen.CS - 2) * mcl_mapgen.BS - 1 -local scan_offset = mcl_mapgen.BS if mg_name ~= "singlenode" then - mcl_mapgen.register_mapgen(function(minp, maxp, blockseed) - -- local str1 = (maxp.y >= 0 and blockseed % 77 == 17) and "YES" or "no" - -- minetest.log("action","[mcl_villages] " .. str1 .. ": minp=" .. minetest.pos_to_string(minp) .. ", maxp=" .. minetest.pos_to_string(maxp) .. ", blockseed=" .. tostring(blockseed)) - -- don't build settlement underground - local y_max = maxp.y - if y_max < -30 then return end - -- randomly try to build settlements - -- if blockseed % 77 ~= 17 then return end - - -- don't build settlements on (too) uneven terrain - - -- lame and quick replacement of `heightmap` by kay27 - we maybe need to restore `heightmap` analysis if there will be a way for the engine to avoid cavegen conflicts: - -------------------------------------------------------------------------- + local mg_name = minetest.get_mapgen_setting("mg_name") + local scan_last_node = mcl_mapgen.LAST_BLOCK * mcl_mapgen.BS - 1 + local scan_offset = mcl_mapgen.BS + mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed) + if minp.y < minp_min then return end + local pr = PseudoRandom(chunkseed * random_multiply + random_offset) + local noise = mcl_structures_get_perlin_noise_level(minp) * noise_multiplier + if (random_number + noise) < struct_threshold then return end local min, max = 9999999, -9999999 - local pr = PseudoRandom(blockseed) for i = 1, pr:next(5,10) do - local pos = vector.add(vector.new(pr:next(0, scan_last_node) + scan_offset, 0, pr:next(0, scan_last_node) + scan_offset), minp) - local surface_point = settlements.find_surface(pos) + local surface_point = settlements.find_surface( + vector.add( + vector.new( + pr:next(scan_offset, scan_last_node) + , + 0, + pr:next(0, scan_last_node) + scan_offset + ), + minp + ) + ) if not surface_point then return end local y = surface_point.y min = math.min(y, min) max = math.max(y, max) end local height_difference = max - min - -------------------------------------------------------------------------- - minetest.chat_send_all("height diff="..height_difference) - if height_difference > 10 then return end - - build_a_settlement(minp, maxp, blockseed) + if height_difference > max_height_difference then return end + build_a_settlement(minp, maxp, chunkkseed) end, mcl_mapgen.order.VILLAGES) end --- manually place villages -if minetest.is_creative_enabled("") then - minetest.register_craftitem("mcl_villages:tool", { - description = "mcl_villages build tool", - inventory_image = "default_tool_woodshovel.png", - -- build ssettlement - on_place = function(itemstack, placer, pointed_thing) - if not pointed_thing.under then return 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)) - end - }) - mcl_wip.register_experimental_item("mcl_villages:tool") + +function mcl_villages.get_villages() + return villages end diff --git a/mods/MAPGEN/mcl_villages/locale/template.txt b/mods/MAPGEN/mcl_villages/locale/template.txt new file mode 100644 index 000000000..e396a8b08 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/locale/template.txt @@ -0,0 +1,2 @@ +# textdomain: mcl_villages +Chiseled Stone Village Bricks= diff --git a/mods/MAPGEN/mcl_villages/mod.conf b/mods/MAPGEN/mcl_villages/mod.conf index c8e0d8149..3f9a67bb3 100644 --- a/mods/MAPGEN/mcl_villages/mod.conf +++ b/mods/MAPGEN/mcl_villages/mod.conf @@ -1,5 +1,5 @@ name = mcl_villages author = Rochambeau, MysticTempest, kay27 description = This mod adds settlements on world generation. -depends = mcl_util, mcl_mapgen_core, mcl_structures, mcl_core, mcl_loot, mcl_mapgen +depends = mcl_util, mcl_structures, mcl_core, mcl_loot, mcl_mapgen optional_depends = mcl_farming, mobs_mc diff --git a/mods/MAPGEN/mcl_villages/paths.lua b/mods/MAPGEN/mcl_villages/paths.lua deleted file mode 100644 index 63f2ba146..000000000 --- a/mods/MAPGEN/mcl_villages/paths.lua +++ /dev/null @@ -1,91 +0,0 @@ -------------------------------------------------------------------------------- --- generate paths between buildings -------------------------------------------------------------------------------- -function settlements.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 - - -- 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 - - 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 = settlements.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 -end diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua deleted file mode 100644 index 589b04403..000000000 --- a/mods/MAPGEN/mcl_villages/utils.lua +++ /dev/null @@ -1,198 +0,0 @@ -local get_node = minetest.get_node - -------------------------------------------------------------------------------- --- function to copy tables -------------------------------------------------------------------------------- -function settlements.shallowCopy(original) - local copy = {} - for key, value in pairs(original) do - copy[key] = value - end - return copy -end --- --- --- -function settlements.round(num, numDecimalPlaces) - local mult = 10^(numDecimalPlaces or 0) - return math.floor(num * mult + 0.5) / mult -end - -------------------------------------------------------------------------------- --- function to find surface block y coordinate --- returns surface postion -------------------------------------------------------------------------------- -function settlements.find_surface(pos) - local p6 = vector.new(pos) - p6.y = mcl_mapgen.get_chunk_ending(p6.y) - local ymin = mcl_mapgen.get_chunk_beginning(p6.y) - local node = get_node(p6) - minetest.chat_send_all(node.name) - if node.name ~= "air" then return end - while true do - p6.y = p6.y - 1 - if p6.y < ymin then return end - node = get_node(p6) - if settlements.surface_mat[node.name] then - break - end - end - minetest.chat_send_all(node.name) - - local prev_node = minetest.get_node(vector.new(p6.x, p6.y + 1, p6.z)) - local name = prev_node.name - if (string.find(name, "air") - or string.find(name, "snow") - or string.find(name, "fern") - or string.find(name, "flower") - or string.find(name, "bush") - or string.find(name, "tree") - or string.find(name, "grass") - ) then - minetest.chat_send_all("found! "..node.name..", "..minetest.pos_to_string(p6)) - return p6, node.name - end -end -------------------------------------------------------------------------------- --- check distance for new building -------------------------------------------------------------------------------- -function settlements.check_distance(settlement_info, building_pos, building_size) - local distance - for i, built_house in ipairs(settlement_info) do - distance = math.sqrt( - ((building_pos.x - built_house["pos"].x)*(building_pos.x - built_house["pos"].x))+ - ((building_pos.z - built_house["pos"].z)*(building_pos.z - built_house["pos"].z))) - if distance < building_size or distance < built_house["hsize"] then - return false - end - end - return true -end -------------------------------------------------------------------------------- --- save list of generated settlements -------------------------------------------------------------------------------- -function settlements.save() - local file = io.open(minetest.get_worldpath().."/settlements.txt", "w") - if file then - file:write(minetest.serialize(settlements_in_world)) - file:close() - end -end -------------------------------------------------------------------------------- --- load list of generated settlements -------------------------------------------------------------------------------- -function settlements.load() - local file = io.open(minetest.get_worldpath().."/settlements.txt", "r") - if file then - local table = minetest.deserialize(file:read("*all")) - if type(table) == "table" then - return table - end - end - return {} -end -------------------------------------------------------------------------------- --- fill chests -------------------------------------------------------------------------------- -function settlements.fill_chest(pos, pr) - -- initialize chest (mts chests don't have meta) - local meta = minetest.get_meta(pos) - if meta:get_string("infotext") ~= "Chest" then - -- For MineClone2 0.70 or before - -- minetest.registered_nodes["mcl_chests:chest"].on_construct(pos) - -- - -- For MineClone2 after commit 09ab1482b5 (the new entity chests) - minetest.registered_nodes["mcl_chests:chest_small"].on_construct(pos) - end - -- fill chest - local inv = minetest.get_inventory( {type="node", pos=pos} ) - - local function get_treasures(prand) - local loottable = {{ - stacks_min = 3, - stacks_max = 8, - items = { - { itemstring = "mcl_core:diamond", weight = 3, amount_min = 1, amount_max = 3 }, - { itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 }, - { itemstring = "mcl_core:gold_ingot", weight = 5, amount_min = 1, amount_max = 3 }, - { itemstring = "mcl_farming:bread", weight = 15, amount_min = 1, amount_max = 3 }, - { itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 }, - { itemstring = "mcl_tools:pick_iron", weight = 5 }, - { itemstring = "mcl_tools:sword_iron", weight = 5 }, - { itemstring = "mcl_armor:chestplate_iron", weight = 5 }, - { itemstring = "mcl_armor:helmet_iron", weight = 5 }, - { itemstring = "mcl_armor:leggings_iron", weight = 5 }, - { itemstring = "mcl_armor:boots_iron", weight = 5 }, - { itemstring = "mcl_core:obsidian", weight = 5, amount_min = 3, amount_max = 7 }, - { itemstring = "mcl_core:sapling", weight = 5, amount_min = 3, amount_max = 7 }, - { itemstring = "mcl_mobitems:saddle", weight = 3 }, - { itemstring = "mobs_mc:iron_horse_armor", weight = 1 }, - { itemstring = "mobs_mc:gold_horse_armor", weight = 1 }, - { itemstring = "mobs_mc:diamond_horse_armor", weight = 1 }, - } - }} - local items = mcl_loot.get_multi_loot(loottable, prand) - return items - end - - local items = get_treasures(pr) - mcl_loot.fill_inventory(inv, "main", items, pr) -end - -------------------------------------------------------------------------------- --- initialize furnace -------------------------------------------------------------------------------- -function settlements.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 settlements.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 -------------------------------------------------------------------------------- -function shuffle(tbl, pr) - local table = settlements.shallowCopy(tbl) - local size = #table - for i = size, 1, -1 do - local rand = pr:next(1, size) - table[i], table[rand] = table[rand], table[i] - end - return table -end -------------------------------------------------------------------------------- --- Set array to list --- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list -------------------------------------------------------------------------------- -function settlements.Set (list) - local set = {} - for _, l in ipairs(list) do set[l] = true end - return set -end