This repository has been archived by the owner on Sep 12, 2021. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 66
/
overmap.dm
432 lines (386 loc) · 16.9 KB
/
overmap.dm
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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
SUBSYSTEM_DEF(overmap)
name = "Overmap"
wait = 10
init_order = INIT_ORDER_OVERMAP
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME
// Defines which generator to use for the overmap
var/generator_type = OVERMAP_GENERATOR_RANDOM
///List of all overmap objects
var/list/overmap_objects
///List of all active, simulated ships
var/list/simulated_ships
///List of all helms, to be adjusted
var/list/helms
///List of all nav computers to initialize
var/list/navs
///List of all events
var/list/events
/**
* Used for generating "orbits" via the solar generator
* Has tiles between index 1 and OVERMAP_DIMENSIONS,
* and a special "unsorted" key for tiles that don't fit in an orbit
* the latter being used primarily for oddities like wormholes.
*/
///Map of tiles at each radius around the sun
var/list/radius_tiles = list()
///The main station or ship
var/obj/structure/overmap/main
///Width/height of the overmap "zlevel"
var/size = 20
///Should events be processed
var/events_enabled = TRUE
///Cooldown on dynamically loading encounters
var/encounter_cooldown = 0
/**
* Creates an overmap object for shuttles, triggers initialization procs for ships and helms
*/
/datum/controller/subsystem/overmap/Initialize(start_timeofday)
generator_type = CONFIG_GET(string/overmap_generator_type)
if (!generator_type || generator_type == "")
generator_type = OVERMAP_GENERATOR_RANDOM
if (generator_type == OVERMAP_GENERATOR_SOLAR)
var/obj/structure/overmap/star/center = locate(/obj/structure/overmap/star)
if (!istype(center))
WARNING("No center tile found in overmap, falling back to default generation method.")
generator_type = OVERMAP_GENERATOR_RANDOM
else
var/list/unsorted_turfs = get_area_turfs(/area/overmap)
// SSovermap.size - 2 = area of the overmap w/o borders
for(var/i in 3 to (size - 2) / 2)
radius_tiles["[i]"] = list()
for(var/turf/T in unsorted_turfs)
var/dist = round(sqrt((T.x - center.x) ** 2 + (T.y - center.y) ** 2))
if (dist == i)
radius_tiles["[i]"] += T
unsorted_turfs = unsorted_turfs - radius_tiles["[i]"]
radius_tiles["unsorted"] = unsorted_turfs.Copy()
create_map()
for(var/shuttle in SSshuttle.mobile)
var/obj/docking_port/mobile/M = shuttle
if(istype(M, /obj/docking_port/mobile/arrivals))
continue
setup_shuttle_ship(M)
for(var/ship in simulated_ships)
var/obj/structure/overmap/ship/simulated/S = ship
S.initial_load()
for(var/helm in helms)
var/obj/machinery/computer/helm/H = helm
H.set_ship()
for(var/nav in navs)
var/obj/machinery/computer/camera_advanced/shuttle_docker/nav/N = nav
N.link_shuttle()
return ..()
/datum/controller/subsystem/overmap/fire()
if(events_enabled)
for(var/event in events)
var/obj/structure/overmap/event/E = event
if(E?.affect_multiple_times && E?.close_overmap_objects)
E.apply_effect()
/**
* Creates an overmap ship object for the provided mobile docking port if one does not already exist.
* * Shuttle: The docking port to create an overmap object for
*/
/datum/controller/subsystem/overmap/proc/setup_shuttle_ship(obj/docking_port/mobile/shuttle)
var/docked_object = get_overmap_object_by_z(shuttle.z)
if(docked_object)
var/obj/structure/overmap/ship/simulated/S = new (docked_object, shuttle.id, shuttle)
S.docked = docked_object
shuttle.current_ship = S
else if(SSmapping.level_trait(shuttle.z, ZTRAIT_RESERVED))
shuttle.current_ship = new /obj/structure/overmap/ship/simulated(get_unused_overmap_square(), shuttle.id, shuttle)
shuttle.current_ship.state = OVERMAP_SHIP_FLYING
else if(is_centcom_level(shuttle.z))
shuttle.current_ship = new /obj/structure/overmap/ship/simulated(null, shuttle.id, shuttle)
else
WARNING("Shuttle created in unknown location, unable to create overmap ship!")
/**
* The proc that creates all the objects on the overmap, split into seperate procs for redundancy.
*/
/datum/controller/subsystem/overmap/proc/create_map()
if (generator_type == OVERMAP_GENERATOR_SOLAR)
spawn_events_in_orbits()
spawn_ruin_levels_in_orbits()
spawn_station()
else
spawn_events()
spawn_ruin_levels()
spawn_station()
/**
* VERY Simple random generation for overmap events, spawns the event in a random turf and sometimes spreads it out similar to ores
*/
/datum/controller/subsystem/overmap/proc/spawn_events()
var/max_clusters = CONFIG_GET(number/max_overmap_event_clusters)
for(var/i=1, i<=max_clusters, i++)
spawn_event_cluster(pick(subtypesof(/obj/structure/overmap/event)), get_unused_overmap_square())
/datum/controller/subsystem/overmap/proc/spawn_events_in_orbits()
var/orbits = list_keys(SSovermap.radius_tiles.Copy())
var/full_orbits = list("unsorted")
orbits = orbits - full_orbits
var/max_clusters = CONFIG_GET(number/max_overmap_event_clusters)
for(var/i = 1, i <= max_clusters, i++)
if(CONFIG_GET(number/max_overmap_events) <= LAZYLEN(SSovermap.events))
return
var/event_type = pickweight(GLOB.overmap_event_pick_list)
var/selected_orbit = pick(orbits)
orbits = orbits - selected_orbit
if (LAZYLEN(orbits) == 0)
// We've hit every orbit with some events, reset the list
orbits = list_keys(SSovermap.radius_tiles.Copy())
orbits = orbits - full_orbits
var/turf/T = get_unused_overmap_square_in_radius(selected_orbit)
if (!T || !istype(T))
// This orbit is full, move onto the next
full_orbits += selected_orbit
continue
var/obj/structure/overmap/event/E = new event_type(T)
var/chain_rate = E.chain_rate
for(var/j = 0, j < chain_rate, j++)
T = get_nearest_unused_square_in_radius(T, selected_orbit, 3)
if (!T || !istype(T))
break
E = new event_type(T)
/**
* See [/datum/controller/subsystem/overmap/proc/spawn_events], spawns "veins" (like ores) of events
*/
/datum/controller/subsystem/overmap/proc/spawn_event_cluster(obj/structure/overmap/event/type, turf/location, chance)
if(CONFIG_GET(number/max_overmap_events) <= LAZYLEN(SSovermap.events))
return
var/obj/structure/overmap/event/E = new type(location)
if(!chance)
chance = E.spread_chance
for(var/dir in GLOB.cardinals)
if(prob(chance))
var/turf/T = get_step(E, dir)
if(!istype(get_area(T), /area/overmap))
continue
if(locate(/obj/structure/overmap/event) in T)
continue
spawn_event_cluster(type, T, chance / 2)
/**
* Creates a station and lavaland overmap object randomly on the overmap.
* * attempt - Used for the failsafe respawning of the station. Don't set unless you want it to only try to spawn it once.
*/
/datum/controller/subsystem/overmap/proc/spawn_station(attempt = 1)
if(main)
qdel(main)
var/obj/structure/overmap/level/mining/mining_level = /obj/structure/overmap/level/mining/lavaland
var/radius = "3"
switch(GLOB.current_mining_map)
if("lavaland")
mining_level = /obj/structure/overmap/level/mining/lavaland
radius = "3"
if("icemoon")
mining_level = /obj/structure/overmap/level/mining/icemoon
radius = "8"
if("whitesands")
mining_level = /obj/structure/overmap/level/mining/whitesands
radius = "5"
var/turf/T
if (generator_type == OVERMAP_GENERATOR_SOLAR)
T = get_unused_overmap_square_in_radius(radius)
else
T = get_unused_overmap_square()
var/obj/structure/overmap/level/mining/instance = new mining_level(T, null, SSmapping.levels_by_trait(ZTRAIT_MINING))
for(var/dir in shuffle(GLOB.alldirs))
var/turf/possible_tile = get_step(instance, dir)
if(!istype(get_area(possible_tile), /area/overmap))
continue
if(locate(/obj/structure/overmap) in possible_tile)
continue
new /obj/structure/overmap/level/main(possible_tile, null, SSmapping.levels_by_trait(ZTRAIT_STATION))
return
if(attempt <= MAX_OVERMAP_PLACEMENT_ATTEMPTS)
spawn_station(++attempt) //Try to spawn the whole thing again
else
if (generator_type == OVERMAP_GENERATOR_SOLAR)
T = get_unused_overmap_square_in_radius(radius)
else
T = get_unused_overmap_square()
new mining_level(T, null, SSmapping.levels_by_trait(ZTRAIT_MINING))
/**
* Creates an overmap object for each ruin level, making them accessible.
*/
/datum/controller/subsystem/overmap/proc/spawn_ruin_levels()
for(var/level in SSmapping.z_list)
var/datum/space_level/L = level
if(ZTRAIT_SPACE_RUINS in L.traits)
var/obj/structure/overmap/level/ruin/new_level = new(get_unused_overmap_square(), null, L.z_value)
new_level.id = "z[L.z_value]"
/datum/controller/subsystem/overmap/proc/spawn_ruin_levels_in_orbits()
var/list/orbits = list_keys(SSovermap.radius_tiles)
orbits = orbits - list("3", "5", "8") // try to not have planets overlap
for(var/level in SSmapping.z_list)
var/datum/space_level/L = level
if(ZTRAIT_SPACE_RUINS in L.traits)
var/turf/T = get_unused_overmap_square_in_radius(orbits)
var/obj/structure/overmap/level/ruin/new_level = new(T, null, L.z_value)
new_level.id = "z[L.z_value]"
for(var/i in 1 to CONFIG_GET(number/max_overmap_dynamic_events))
var/turf/T = get_unused_overmap_square_in_radius(pick(orbits))
new /obj/structure/overmap/dynamic(T)
/**
* Reserves a square dynamic encounter area, and spawns a ruin in it if one is supplied.
* * on_planet - If the encounter should be on a generated planet. Required, as it will be otherwise inaccessible.
* * target - The ruin to spawn, if any
* * dock_id - The id of the stationary docking port that will be spawned in the encounter. The primary and secondary prefixes will be applied, so do not include them.
* * size - Size of the encounter, defaults to 1/3 total world size
* * visiting_shuttle - The shuttle that is going to go to the encounter. Allows ruins to scale.
*/
/datum/controller/subsystem/overmap/proc/spawn_dynamic_encounter(planet_type, ruin = TRUE, dock_id, size = world.maxx / 4, obj/docking_port/mobile/visiting_shuttle, ignore_cooldown = FALSE)
if(!ignore_cooldown && !COOLDOWN_FINISHED(SSovermap, encounter_cooldown))
return FALSE
COOLDOWN_START(src, encounter_cooldown, 90 SECONDS) //Cooldown starts even if ignore_cooldown is set.
if(!dock_id)
CRASH("Encounter spawner tried spawning an encounter without a docking port ID!")
var/total_size = size
var/ruin_size = CEILING(size / 2, 1)
var/dock_size = FLOOR(size / 2, 1)
if(visiting_shuttle)
dock_size = max(visiting_shuttle.width, visiting_shuttle.height) + 3 //a little bit of wiggle room
var/list/ruin_list = SSmapping.space_ruins_templates
var/datum/map_template/ruin/ruin_type
var/datum/map_generator/mapgen
var/area/target_area
if(planet_type)
switch(planet_type)
if(DYNAMIC_WORLD_LAVA)
ruin_list = SSmapping.lava_ruins_templates
mapgen = new /datum/map_generator/cave_generator/lavaland
target_area = /area/ruin/unpowered/planetoid/lava
if(DYNAMIC_WORLD_ICE)
ruin_list = SSmapping.ice_ruins_templates
mapgen = new /datum/map_generator/cave_generator/icemoon/surface
target_area = /area/ruin/unpowered/planetoid/ice
if(DYNAMIC_WORLD_SAND)
ruin_list = SSmapping.sand_ruins_templates
mapgen = new /datum/map_generator/cave_generator/whitesands
target_area = /area/ruin/unpowered/planetoid/sand
if(DYNAMIC_WORLD_JUNGLE)
ruin_list = SSmapping.jungle_ruins_templates
mapgen = new /datum/map_generator/jungle_generator
target_area = /area/ruin/unpowered/planetoid/jungle
if(ruin && ruin_list) //Done BEFORE the turfs are reserved so that it allocates the right size box
ruin_type = ruin_list[pick(ruin_list)]
if(ispath(ruin_type))
ruin_type = new ruin_type
ruin_size = max(ruin_type.width, ruin_type.height) + 4
total_size = dock_size + ruin_size
var/datum/turf_reservation/encounter_reservation = SSmapping.RequestBlockReservation(total_size, total_size, border_turf_override = /turf/closed/indestructible/blank, area_override = target_area)
if(mapgen)
var/list/same_area_turfs = list()
for(var/turf in encounter_reservation.non_border_turfs)
var/turf/T = turf
var/area/A = T.loc
if(A?.type != target_area)
continue
same_area_turfs += T
mapgen.generate_terrain(same_area_turfs)
if(ruin_type) //Does AFTER the turfs are reserved so it can find where the allocation is
//gets a turf vaguely in the middle of the reserve
var/turf/ruin_turf = locate(encounter_reservation.bottom_left_coords[1] + dock_size + 2, encounter_reservation.bottom_left_coords[2] + dock_size, encounter_reservation.bottom_left_coords[3])
ruin_type.load(ruin_turf)
//gets the turf with an X in the middle of the reservation, and a Y that's 1/4ths up in the reservation.
var/turf/docking_turf = locate(encounter_reservation.bottom_left_coords[1] + dock_size, encounter_reservation.bottom_left_coords[2] + FLOOR(dock_size / 2, 1), encounter_reservation.bottom_left_coords[3])
var/obj/docking_port/stationary/dock = SSshuttle.getDock("[PRIMARY_OVERMAP_DOCK_PREFIX]_[dock_id]")
if(!dock)
dock = new(docking_turf)
else
dock.forceMove(docking_turf)
dock.dir = WEST
dock.name = "\improper Uncharted Space"
dock.id = "[PRIMARY_OVERMAP_DOCK_PREFIX]_[dock_id]"
dock.height = dock_size
dock.width = dock_size
if(visiting_shuttle)
dock.dheight = min(visiting_shuttle.dheight, dock_size)
dock.dwidth = min(visiting_shuttle.dwidth, dock_size)
else
dock.dwidth = FLOOR(dock_size / 2, 1)
//gets the turf with an X in the middle of the reservation, and a Y that's 3/4ths up in the reservation.
var/turf/secondary_docking_turf = locate(encounter_reservation.bottom_left_coords[1] + dock_size, encounter_reservation.bottom_left_coords[2] + CEILING(dock_size * 1.5, 1), encounter_reservation.bottom_left_coords[3])
var/obj/docking_port/stationary/secondary_dock = SSshuttle.getDock("[SECONDARY_OVERMAP_DOCK_PREFIX]_[dock_id]")
if(!secondary_dock)
secondary_dock = new(secondary_docking_turf)
else
secondary_dock.forceMove(secondary_docking_turf)
secondary_dock.dir = WEST
secondary_dock.name = "\improper Uncharted Space"
secondary_dock.id = "[SECONDARY_OVERMAP_DOCK_PREFIX]_[dock_id]"
secondary_dock.height = dock_size
secondary_dock.width = dock_size
secondary_dock.dwidth = FLOOR(dock_size / 2, 1)
return encounter_reservation
/**
* Returns a random, usually empty turf in the overmap
* * thing_to_not_have - The thing you don't want to be in the found tile, for example, an overmap event [/obj/structure/overmap/event].
* * tries - How many attempts it will try before giving up finding an unused tile..
*/
/datum/controller/subsystem/overmap/proc/get_unused_overmap_square(thing_to_not_have = /obj/structure/overmap, tries = MAX_OVERMAP_PLACEMENT_ATTEMPTS)
var/turf/picked_turf
for(var/i in 1 to tries)
picked_turf = pick(pick(get_area_turfs(/area/overmap)))
if(locate(thing_to_not_have) in picked_turf)
continue
break
return picked_turf
/**
* Returns a random turf in a radius from the star, or a random empty turf if OVERMAP_GENERATOR_RANDOM is the active generator.
* * thing_to_not_have - The thing you don't want to be in the found tile, for example, an overmap event [/obj/structure/overmap/event].
* * tries - How many attempts it will try before giving up finding an unused tile..
* * radius - The distance from the star to search for an empty tile.
*/
/datum/controller/subsystem/overmap/proc/get_unused_overmap_square_in_radius(radius, thing_to_not_have = /obj/structure/overmap, tries = MAX_OVERMAP_PLACEMENT_ATTEMPTS)
if (generator_type == OVERMAP_GENERATOR_RANDOM)
return get_unused_overmap_square(thing_to_not_have, tries)
if(!radius)
var/list/K = list_keys(SSovermap.radius_tiles)
K -= "unsorted"
radius = pick(K)
var/turf/picked_turf
for(var/i in 1 to tries)
picked_turf = pick(radius_tiles[radius])
if (locate(thing_to_not_have) in picked_turf)
continue
break
return picked_turf
/datum/controller/subsystem/overmap/proc/get_nearest_unused_square_in_radius(adjacent, radius, max_range, thing_to_not_have = /obj/structure/overmap)
var/turf/target = adjacent
var/list/turfs_in_orbit = SSovermap.radius_tiles[radius]
var/turf/ret
var/ret_dist = INFINITY
for(var/turf/T in turfs_in_orbit)
if (locate(thing_to_not_have) in T)
continue
var/dist = round(sqrt((T.x - target.x) ** 2 + (T.y - target.y) ** 2))
if (dist < max_range && dist < ret_dist)
ret = T
ret_dist = dist
return ret
/**
* Gets the corresponding overmap object that shares the provided ID
* * id - ID of the overmap object you want to find
*/
/datum/controller/subsystem/overmap/proc/get_overmap_object_by_id(id)
for(var/obj/structure/overmap/object in overmap_objects)
if(object.id == id)
return object
/**
* Gets the corresponding overmap object that shares the provided z level
* * zlevel - The Z-level of the overmap object you want to find
*/
/datum/controller/subsystem/overmap/proc/get_overmap_object_by_z(zlevel)
for(var/obj/structure/overmap/level/object in overmap_objects)
if(zlevel in object.linked_levels)
return object
/datum/controller/subsystem/overmap/Recover()
if(istype(SSovermap.simulated_ships))
simulated_ships = SSovermap.simulated_ships
if(istype(SSovermap.helms))
helms = SSovermap.helms
if(istype(SSovermap.events))
events = SSovermap.events
if(istype(SSovermap.main))
main = SSovermap.main
if(istype(SSovermap.radius_tiles))
radius_tiles = SSovermap.radius_tiles