-
Notifications
You must be signed in to change notification settings - Fork 2
/
init.lua
277 lines (244 loc) · 9.09 KB
/
init.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
settlements = {}
settlements.half_map_chunk_size = 40
settlements.surface_materials = {}
settlements.settlement_defs = {}
-- Minimum distance between settlements
settlements.min_dist_settlements = tonumber(minetest.settings:get("settlements_minimum_distance_between_settlements")) or 500
-- maximum allowed difference in height for building a settlement
settlements.max_height_difference = tonumber(minetest.settings:get("settlements_maximum_height_difference")) or 10
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath.."/buildings.lua")
dofile(modpath.."/hud.lua")
settlements.register_settlement = function(settlement_type_name, settlement_def)
assert(not settlements.settlement_defs[settlement_type_name])
settlement_def.name = settlement_type_name
settlements.settlement_defs[settlement_type_name] = settlement_def
for _, material in ipairs(settlement_def.surface_materials) do
local c_mat = minetest.get_content_id(material)
local material_list = settlements.surface_materials[c_mat] or {}
settlements.surface_materials[c_mat] = material_list
table.insert(material_list, settlement_def)
end
end
-- Interconverting lua and mts formatted schematics
-- Useful for modders adding existing schematics that are in mts format
function settlements.convert_mts_to_lua(schem_path)
local str = minetest.serialize_schematic(schem_path, "lua", {lua_use_comments = true})
local file = io.open(schem_path:sub(1,-4).."lua", "w")
file:write(str.."\nreturn schematic")
file:close()
end
dofile(modpath.."/default_settlements.lua")
----------------------------------------------------------------------------
local worldpath = minetest.get_worldpath()
local areastore_filename = worldpath.."/settlements_areastore.txt"
-- load list of generated settlements
local function settlements_load()
local area_file = io.open(areastore_filename, "r")
-- Compatibility with old versions
local old_file = io.open(worldpath.."/settlements.txt", "r")
if old_file and not area_file then
local settlements = minetest.deserialize(old_file:read("*all"))
if type(settlements) == "table" then
local areastore = AreaStore()
for _, pos in ipairs(settlements) do
pos = vector.add(pos, {x=5, y=0, z=5}) -- Shift it over to put it in the center of town hall
local name = "Town"
if minetest.get_modpath("namegen") then
name = namegen.generate("settlement_towns")
end
local discovered_by = {}
local settlement_type = "medieval"
areastore:insert_area(pos, pos, minetest.serialize({name=name, discovered_by=discovered_by,settlement_type=settlement_type}))
end
return areastore
end
end
------------------------------------
local areastore = AreaStore()
if not area_file then
return areastore
end
areastore:from_file(areastore_filename)
return areastore
end
settlements.settlements_in_world = settlements_load()
-- save list of generated settlements
function settlements.settlements_save()
settlements.settlements_in_world:to_file(areastore_filename)
end
-------------------------------------------------------------------------------
-- register block for npc spawn
local function deep_copy(table_in)
local table_out = {}
for index, value in pairs(table_in) do
if type(value) == "table" then
table_out[index] = deep_copy(value)
else
table_out[index] = value
end
end
return table_out
end
local junglewood_def = deep_copy(minetest.registered_nodes["default:junglewood"])
minetest.register_node("settlements:junglewood", junglewood_def)
-- register inhabitants
if minetest.get_modpath("mobs_npc") ~= nil then
mobs:register_spawn("mobs_npc:npc", --name
{"settlements:junglewood"}, --nodes
20, --max_light
0, --min_light
20, --chance
2, --active_object_count
31000, --max_height
nil) --day_toggle
mobs:register_spawn("mobs_npc:trader", --name
{"settlements:junglewood"}, --nodes
20, --max_light
0, --min_light
20, --chance
2, --active_object_count
31000, --max_height
nil)--day_toggle
end
-------------------------------------------------------------------------------
-- check distance to other settlements
-------------------------------------------------------------------------------
local function check_distance_other_settlements(center_new_chunk)
local min_edge = vector.subtract(center_new_chunk, settlements.min_dist_settlements)
local max_edge = vector.add(center_new_chunk, settlements.min_dist_settlements)
-- This gets all neighbors within a cube-shaped volume
local neighbors = settlements.settlements_in_world:get_areas_in_area(min_edge, max_edge, true, true)
-- Search through those to find any that are within a spherical volume
for i, settlement in pairs(neighbors) do
local distance = vector.distance(center_new_chunk, settlement.min)
-- minetest.chat_send_all("dist ".. distance)
if distance < settlements.min_dist_settlements then
return false
end
end
return true
end
-------------------------------------------------------------------------------
-- evaluate heightmap
-------------------------------------------------------------------------------
local function evaluate_heightmap(heightmap)
-- max height and min height, initialize with impossible values for easier first time setting
local max_y = -50000
local min_y = 50000
-- only evaluate the center square of heightmap 40 x 40
local square_start = 1621
local square_end = 1661
for j = 1 , 40, 1 do
for i = square_start, square_end, 1 do
-- skip buggy heightmaps, return high value
if heightmap[i] == -31000 or
heightmap[i] == 31000
then
return settlements.max_height_difference + 1
end
if heightmap[i] < min_y
then
min_y = heightmap[i]
end
if heightmap[i] > max_y
then
max_y = heightmap[i]
end
end
-- set next line
square_start = square_start + 80
square_end = square_end + 80
end
-- return the difference between highest and lowest pos in chunk
local height_diff = max_y - min_y
-- filter buggy heightmaps
if height_diff <= 1
then
return settlements.max_height_difference + 1
end
return height_diff
end
local half_map_chunk_size = settlements.half_map_chunk_size
minetest.register_on_generated(function(minp, maxp)
-- don't build settlement underground
if maxp.y < -100 then
return
end
-- don't build settlements too close to each other
local center_of_chunk = vector.subtract(maxp, half_map_chunk_size)
local dist_ok = check_distance_other_settlements(center_of_chunk)
if dist_ok == false
then
return
end
-- don't build settlements on (too) uneven terrain
local heightmap = minetest.get_mapgen_object("heightmap")
local height_difference = evaluate_heightmap(heightmap)
if height_difference > settlements.max_height_difference
then
return
end
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local va = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
settlements.generate_settlement_vm(vm, va, minp, maxp)
end)
local debug_building_index = 0
local c_dirt_with_grass = minetest.get_content_id("default:dirt_with_grass")
local all_schematics
local function get_next_debug_building()
if not all_schematics then
all_schematics = {}
for _, settlement_def in pairs(settlements.settlement_defs) do
for _, building_info in ipairs(settlement_def.schematics) do
table.insert(all_schematics, building_info)
end
end
end
debug_building_index = debug_building_index + 1
if debug_building_index > #all_schematics then
debug_building_index = 1
end
return all_schematics[debug_building_index]
end
-- manually place buildings, for debugging only
minetest.register_craftitem("settlements:tool", {
description = "settlements build tool",
inventory_image = "default_tool_woodshovel.png",
-- build single house
on_use = function(itemstack, placer, pointed_thing)
if not minetest.check_player_privs(placer, "server") then
minetest.chat_send_player(placer:get_player_name(), "You need the server privilege to use this tool.")
return
end
local center_surface = pointed_thing.under
if center_surface then
local selected_building = get_next_debug_building()
local built_house = {}
built_house.schematic_info = selected_building
built_house.center_pos = center_surface -- we're not terraforming so this doesn't matter
built_house.build_pos_min = center_surface
built_house.rotation = "0"
built_house.surface_mat = c_dirt_with_grass
local vm = minetest.get_voxel_manip()
local maxp = vector.add(center_surface, selected_building.schematic.size)
local emin, emax = vm:read_from_map(center_surface, maxp)
settlements.place_building(vm, built_house, {def={}})
minetest.chat_send_player(placer:get_player_name(), "Built " .. selected_building.name)
vm:write_to_map()
end
end,
-- build settlement
on_place = function(itemstack, placer, pointed_thing)
if not minetest.check_player_privs(placer, "server") then
minetest.chat_send_player(placer:get_player_name(), "You need the server privilege to use this tool.")
return
end
local center_surface = pointed_thing.under
if center_surface then
local minp = vector.subtract(center_surface, half_map_chunk_size)
local maxp = vector.add(center_surface, half_map_chunk_size)
settlements.generate_settlement(minp, maxp)
end
end
})