Backports several SSmapping improvements (#17208)

* Optimizes SSmapping

* Turfs inside area are stored

https://github.com/tgstation/tgstation/pull/70966

* Add async reserving of turfs

* Fix bug

* Fix shuttle init
This commit is contained in:
Ling
2022-12-31 16:41:47 +01:00
committed by GitHub
parent 5a5be9ef89
commit 17c5a68c33
48 changed files with 1390 additions and 644 deletions

View File

@@ -28,6 +28,8 @@
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
#define ROUND_UP(x) ( -round(-(x)))
// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) )

View File

@@ -1,6 +0,0 @@
#define CHANGETURF_DEFER_CHANGE 1
#define CHANGETURF_IGNORE_AIR 2 // This flag prevents changeturf from gathering air from nearby turfs to fill the new turf with an approximation of local air
#define CHANGETURF_FORCEOP 4
#define CHANGETURF_SKIP 8 // A flag for PlaceOnTop to just instance the new turf instead of calling ChangeTurf. Used for uninitialized turfs NOTHING ELSE
#define CHANGETURF_INHERIT_AIR 16 // Inherit air from previous turf. Implies CHANGETURF_IGNORE_AIR
#define CHANGETURF_RECALC_ADJACENT 32 //Immediately recalc adjacent atmos turfs instead of queuing.

10
code/__DEFINES/turfs.dm Normal file
View File

@@ -0,0 +1,10 @@
#define CHANGETURF_DEFER_CHANGE (1<<0)
#define CHANGETURF_IGNORE_AIR (1<<1) // This flag prevents changeturf from gathering air from nearby turfs to fill the new turf with an approximation of local air
#define CHANGETURF_FORCEOP (1<<2)
#define CHANGETURF_SKIP (1<<3) // A flag for PlaceOnTop to just instance the new turf instead of calling ChangeTurf. Used for uninitialized turfs NOTHING ELSE
#define CHANGETURF_INHERIT_AIR (1<<4) // Inherit air from previous turf. Implies CHANGETURF_IGNORE_AIR
#define CHANGETURF_RECALC_ADJACENT (1<<5) //Immediately recalc adjacent atmos turfs instead of queuing.
#define CHANGETURF_TRAPDOOR_INDUCED (1<<6) // Caused by a trapdoor, for trapdoor to know that this changeturf was caused by itself
///Returns all currently loaded turfs
#define ALL_TURFS(...) block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz))

View File

@@ -346,6 +346,41 @@
return null
/// Takes a weighted list (see above) and expands it into raw entries
/// This eats more memory, but saves time when actually picking from it
/proc/expand_weights(list/list_to_pick)
var/list/values = list()
for(var/item in list_to_pick)
var/value = list_to_pick[item]
if(!value)
continue
values += value
var/gcf = greatest_common_factor(values)
var/list/output = list()
for(var/item in list_to_pick)
var/value = list_to_pick[item]
if(!value)
continue
for(var/i in 1 to value / gcf)
output += item
return output
/// Takes a list of numbers as input, returns the highest value that is cleanly divides them all
/// Note: this implementation is expensive as heck for large numbers, I only use it because most of my usecase
/// Is < 10 ints
/proc/greatest_common_factor(list/values)
var/smallest = min(arglist(values))
for(var/i in smallest to 1 step -1)
var/safe = TRUE
for(var/entry in values)
if(entry % i != 0)
safe = FALSE
break
if(safe)
return i
/// Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
RETURN_TYPE(L[_].type)

View File

@@ -92,7 +92,9 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(list(/area/engin
for(var/i in 1 to turfs.len)
var/turf/thing = turfs[i]
var/area/old_area = thing.loc
old_area.turfs_to_uncontain += thing
newA.contents += thing
newA.contained_turfs += thing
thing.change_area(old_area, newA)
newA.reg_in_areas_in_z()
@@ -105,4 +107,101 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(list(/area/engin
to_chat(creator, span_notice("You have created a new area, named [newA.name]. It is now weather proof, and constructing an APC will allow it to be powered."))
return TRUE
/proc/require_area_resort()
GLOB.sortedAreas = null
/// Returns a sorted version of GLOB.areas, by name
/proc/get_sorted_areas()
if(!GLOB.sortedAreas)
GLOB.sortedAreas = sortTim(GLOB.areas.Copy(), /proc/cmp_name_asc)
return GLOB.sortedAreas
//Takes: Area type as a text string from a variable.
//Returns: Instance for the area in the world.
/proc/get_area_instance_from_text(areatext)
if(istext(areatext))
areatext = text2path(areatext)
return GLOB.areas_by_type[areatext]
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all areas of that type in the world.
/proc/get_areas(areatype, subtypes=TRUE)
if(istext(areatype))
areatype = text2path(areatype)
else if(isarea(areatype))
var/area/areatemp = areatype
areatype = areatemp.type
else if(!ispath(areatype))
return null
var/list/areas = list()
if(subtypes)
var/list/cache = typecacheof(areatype)
for(var/area/area_to_check as anything in GLOB.areas)
if(cache[area_to_check.type])
areas += area_to_check
else
for(var/area/area_to_check as anything in GLOB.areas)
if(area_to_check.type == areatype)
areas += area_to_check
return areas
/// Iterates over all turfs in the target area and returns the first non-dense one
/proc/get_first_open_turf_in_area(area/target)
if(!target)
return
for(var/turf/turf in target)
if(!turf.density)
return turf
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all turfs in areas of that type of that type in the world.
/proc/get_area_turfs(areatype, target_z = 0, subtypes=FALSE)
if(istext(areatype))
areatype = text2path(areatype)
else if(isarea(areatype))
var/area/areatemp = areatype
areatype = areatemp.type
else if(!ispath(areatype))
return null
// Pull out the areas
var/list/areas_to_pull = list()
if(subtypes)
var/list/cache = typecacheof(areatype)
for(var/area/area_to_check as anything in GLOB.areas)
if(!cache[area_to_check.type])
continue
areas_to_pull += area_to_check
else
for(var/area/area_to_check as anything in GLOB.areas)
if(area_to_check.type != areatype)
continue
areas_to_pull += area_to_check
// Now their turfs
var/list/turfs = list()
for(var/area/pull_from as anything in areas_to_pull)
var/list/our_turfs = pull_from.get_contained_turfs()
if(target_z == 0)
turfs += our_turfs
else
for(var/turf/turf_in_area as anything in our_turfs)
if(target_z == turf_in_area.z)
turfs += turf_in_area
return turfs
///Takes: list of area types
///Returns: all mobs that are in an area type
/proc/mobs_in_area_type(list/area/checked_areas)
var/list/mobs_in_area = list()
for(var/mob/living/mob as anything in GLOB.mob_living_list)
if(QDELETED(mob))
continue
for(var/area in checked_areas)
if(istype(get_area(mob), area))
mobs_in_area += mob
break
return mobs_in_area
#undef BP_MAX_ROOM_SIZE

View File

@@ -264,11 +264,29 @@
return ""
//Returns a string with reserved characters and spaces after the first and last letters removed
//Like trim(), but very slightly faster. worth it for niche usecases
/proc/trim_reduced(text)
var/starting_coord = 1
var/text_len = length(text)
for (var/i in 1 to text_len)
if (text2ascii(text, i) > 32)
starting_coord = i
break
for (var/i = text_len, i >= starting_coord, i--)
if (text2ascii(text, i) > 32)
return copytext(text, starting_coord, i + 1)
if(starting_coord > 1)
return copytext(text, starting_coord)
return ""
//Returns a string with reserved characters and spaces before the first word and after the last word removed.
/proc/trim(text, max_length)
if(max_length)
text = copytext_char(text, 1, max_length)
return trim_left(trim_right(text))
return trim_reduced(text)
//Returns a string with the first element of the string capitalized.
/proc/capitalize(t as text)

View File

@@ -583,69 +583,6 @@ Turf and target are separate in case you want to teleport some distance from a t
GLOB.sortedAreas.Add(src)
sortTim(GLOB.sortedAreas, /proc/cmp_name_asc)
//Takes: Area type as a text string from a variable.
//Returns: Instance for the area in the world.
/proc/get_area_instance_from_text(areatext)
if(istext(areatext))
areatext = text2path(areatext)
return GLOB.areas_by_type[areatext]
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all areas of that type in the world.
/proc/get_areas(areatype, subtypes=TRUE)
if(istext(areatype))
areatype = text2path(areatype)
else if(isarea(areatype))
var/area/areatemp = areatype
areatype = areatemp.type
else if(!ispath(areatype))
return null
var/list/areas = list()
if(subtypes)
var/list/cache = typecacheof(areatype)
for(var/V in GLOB.sortedAreas)
var/area/A = V
if(cache[A.type])
areas += V
else
for(var/V in GLOB.sortedAreas)
var/area/A = V
if(A.type == areatype)
areas += V
return areas
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all turfs in areas of that type of that type in the world.
/proc/get_area_turfs(areatype, target_z = 0, subtypes=FALSE)
if(istext(areatype))
areatype = text2path(areatype)
else if(isarea(areatype))
var/area/areatemp = areatype
areatype = areatemp.type
else if(!ispath(areatype))
return null
var/list/turfs = list()
if(subtypes)
var/list/cache = typecacheof(areatype)
for(var/V in GLOB.sortedAreas)
var/area/A = V
if(!cache[A.type])
continue
for(var/turf/T in A)
if(target_z == 0 || target_z == T.z)
turfs += T
else
for(var/V in GLOB.sortedAreas)
var/area/A = V
if(A.type != areatype)
continue
for(var/turf/T in A)
if(target_z == 0 || target_z == T.z)
turfs += T
return turfs
/proc/get_cardinal_dir(atom/A, atom/B)
var/dx = abs(B.x - A.x)
var/dy = abs(B.y - A.y)

View File

@@ -45,11 +45,15 @@ GLOBAL_LIST_EMPTY(bar_areas)
// IF YOU ARE MAKING A NEW BAR TEMPLATE AND WANT IT ROUNDSTART ADD IT TO THIS LIST!
GLOBAL_LIST_INIT(potential_box_bars, list("Bar Trek", "Bar Spacious", "Bar Box", "Bar Casino", "Bar Citadel", "Bar Conveyor", "Bar Diner", "Bar Disco", "Bar Purple", "Bar Cheese", "Bar Grassy", "Bar Clock", "Bar Arcade"))
//away missions
/// Away missions
GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can take you to
GLOBAL_LIST_EMPTY(vr_spawnpoints)
//used by jump-to-area etc. Updated by area/updateName()
/// Just a list of all the area objects in the game
/// Note, areas can have duplicate types
GLOBAL_LIST_EMPTY(areas)
/// Used by jump-to-area etc. Updated by area/updateName()
/// If this is null, it needs to be recalculated. Use get_sorted_areas() as a getter please
GLOBAL_LIST_EMPTY(sortedAreas)
/// An association from typepath to area instance. Only includes areas with `unique` set.
GLOBAL_LIST_EMPTY_TYPED(areas_by_type, /area)

View File

@@ -351,7 +351,6 @@ SUBSYSTEM_DEF(air)
queued_for_activation.Cut()
/datum/controller/subsystem/air/proc/setup_allturfs()
var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz))
var/list/active_turfs = src.active_turfs
var/times_fired = ++src.times_fired
@@ -359,7 +358,7 @@ SUBSYSTEM_DEF(air)
// one-by-one, and Initalize_Atmos only ever adds `src` back in.
active_turfs.Cut()
for(var/thing in turfs_to_init)
for(var/thing in ALL_TURFS())
var/turf/T = thing
if (T.blocks_air)
continue

View File

@@ -0,0 +1,55 @@
#define ALLOWED_LOOSE_TURFS 500
/**
* Responsible for managing the sizes of area.contained_turfs and area.turfs_to_uncontain
* These lists do not check for duplicates, which is fine, but it also means they can balloon in size over time
* as a consequence of repeated changes in area in a space
* They additionally may not always resolve often enough to avoid memory leaks
* This is annoying, so lets keep an eye on them and cut them down to size if needed
*/
SUBSYSTEM_DEF(area_contents)
name = "Area Contents"
flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
var/list/currentrun
var/list/area/marked_for_clearing = list()
/datum/controller/subsystem/area_contents/stat_entry(msg)
var/total_clearing_from = 0
var/total_to_clear = 0
for(var/area/to_clear as anything in marked_for_clearing)
total_to_clear += length(to_clear.turfs_to_uncontain)
total_clearing_from += length(to_clear.contained_turfs)
msg = "A:[length(currentrun)] MR:[length(marked_for_clearing)] TC:[total_to_clear] CF:[total_clearing_from]"
return ..()
/datum/controller/subsystem/area_contents/fire(resumed)
if(!resumed)
currentrun = GLOB.areas.Copy()
while(length(currentrun))
var/area/test = currentrun[length(currentrun)]
if(length(test.turfs_to_uncontain) > ALLOWED_LOOSE_TURFS)
marked_for_clearing |= test
currentrun.len--
if(MC_TICK_CHECK)
return
// Alright, if we've done a scan on all our areas, it's time to knock the existing ones down to size
while(length(marked_for_clearing))
var/area/clear = marked_for_clearing[length(marked_for_clearing)]
// The operation of cutting large lists can be expensive
// It scales almost directly with the size of the list we're cutting with
// Because of this, we're gonna stick to cutting 1 entry at a time
// There's no reason to batch it I promise, this is faster. No overtime too
var/amount_cut = 0
var/list/cut_from = clear.turfs_to_uncontain
for(amount_cut in 1 to length(cut_from))
clear.contained_turfs -= cut_from[amount_cut]
if(MC_TICK_CHECK)
cut_from.Cut(1, amount_cut + 1)
return
clear.turfs_to_uncontain = list()
marked_for_clearing.len--

View File

@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
runlevels = ALL
loading_points = 11 SECONDS // Yogs -- loading times
@@ -32,6 +32,8 @@ SUBSYSTEM_DEF(mapping)
var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
var/list/datum/turf_reservations //list of turf reservations
var/list/used_turfs = list() //list of turf = datum/turf_reservation
/// List of lists of turfs to reserve
var/list/lists_to_reserve = list()
var/clearing_reserved_turfs = FALSE
@@ -63,7 +65,7 @@ SUBSYSTEM_DEF(mapping)
to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to Box Station"))
config = old_config
loadWorld()
repopulate_sorted_areas()
require_area_resort()
process_teleport_locs() //Sets up the wizard teleport locations
preloadTemplates()
run_map_generation()
@@ -91,20 +93,20 @@ SUBSYSTEM_DEF(mapping)
loading_ruins = TRUE
var/list/lava_ruins = levels_by_trait(ZTRAIT_LAVA_RUINS)
if (lava_ruins.len)
seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), lava_ruins_templates)
seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), lava_ruins_templates, clear_below = TRUE)
for (var/lava_z in lava_ruins)
spawn_rivers(lava_z)
var/list/ice_ruins = levels_by_trait(ZTRAIT_ICE_RUINS)
if (ice_ruins.len)
// needs to be whitelisted for underground too so place_below ruins work
seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), ice_ruins_templates)
seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), ice_ruins_templates, clear_below = TRUE)
for(var/ice_z in ice_ruins)
spawn_rivers(ice_z, 4, /turf/open/openspace/icemoon, /area/icemoon/surface/outdoors/unexplored)
var/list/ice_ruins_underground = levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND)
if (ice_ruins_underground.len)
seedRuins(ice_ruins_underground, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/underground/unexplored), ice_ruins_underground_templates)
seedRuins(ice_ruins_underground, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/underground/unexplored), ice_ruins_underground_templates, clear_below = TRUE)
for(var/ice_z in ice_ruins_underground)
spawn_rivers(ice_z, 4, /turf/open/lava/plasma/ice_moon, /area/icemoon/underground/unexplored)
@@ -134,7 +136,7 @@ SUBSYSTEM_DEF(mapping)
PM.initTemplateBounds()
// Add the transit level
transit = add_new_zlevel("Transit/Reserved", list(ZTRAIT_RESERVED = TRUE))
repopulate_sorted_areas()
require_area_resort()
// Set up Z-level transitions.
setup_map_transitions()
generate_station_area_list()
@@ -143,6 +145,35 @@ SUBSYSTEM_DEF(mapping)
build_minimaps()
return SS_INIT_SUCCESS
/datum/controller/subsystem/mapping/fire(resumed)
// Cache for sonic speed
var/list/unused_turfs = src.unused_turfs
var/list/world_contents = GLOB.areas_by_type[world.area].contents
var/list/world_turf_contents = GLOB.areas_by_type[world.area].contained_turfs
var/list/lists_to_reserve = src.lists_to_reserve
var/index = 0
while(index < length(lists_to_reserve))
var/list/packet = lists_to_reserve[index + 1]
var/packetlen = length(packet)
while(packetlen)
if(MC_TICK_CHECK)
lists_to_reserve.Cut(1, index)
return
var/turf/T = packet[packetlen]
T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
var/area/old_area = T.loc
old_area.turfs_to_uncontain += T
T.flags_1 |= UNUSED_RESERVATION_TURF_1
world_contents += T
world_turf_contents += T
packet.len--
packetlen = length(packet)
index++
lists_to_reserve.Cut(1, index)
/datum/controller/subsystem/mapping/proc/wipe_reservations(wipe_safety_delay = 100)
if(clearing_reserved_turfs || !initialized) //in either case this is just not needed.
return
@@ -250,13 +281,13 @@ SUBSYSTEM_DEF(mapping)
var/start_z = world.maxz + 1
var/i = 0
for (var/level in traits)
add_new_zlevel("[name][i ? " [i + 1]" : ""]", level)
add_new_zlevel("[name][i ? " [i + 1]" : ""]", level, contain_turfs = FALSE)
++i
// load the maps
for (var/P in parsed_maps)
var/datum/parsed_map/pm = P
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE))
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE, new_z = TRUE))
errorList |= pm.original_path
if(!silent)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
@@ -310,7 +341,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
/datum/controller/subsystem/mapping/proc/generate_station_area_list()
var/list/station_areas_blacklist = typecacheof(list(/area/space, /area/mine, /area/ruin, /area/asteroid/nearstation))
for(var/area/A in world)
for(var/area/A in GLOB.areas)
if (is_type_in_typecache(A, station_areas_blacklist))
continue
if (!A.contents.len || !A.unique)
@@ -323,7 +354,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
log_world("ERROR: Station areas list failed to generate!")
/datum/controller/subsystem/mapping/proc/run_map_generation()
for(var/area/A in world)
for(var/area/A as anything in GLOB.areas)
A.RunGeneration()
/datum/controller/subsystem/mapping/proc/maprotate()
@@ -549,15 +580,12 @@ GLOBAL_LIST_EMPTY(the_station_areas)
unused_turfs["[i]"] = block
clearing_reserved_turfs = FALSE
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs)
for(var/i in turfs)
var/turf/T = i
T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
T.flags_1 |= UNUSED_RESERVATION_TURF_1
GLOB.areas_by_type[world.area].contents += T
CHECK_TICK
/// Schedules a group of turfs to be handed back to the reservation system's control
/// If await is true, will sleep until the turfs are finished work
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs, await = FALSE)
lists_to_reserve += list(turfs)
if(await)
UNTIL(!length(turfs))
//DO NOT CALL THIS PROC DIRECTLY, CALL wipe_reservations().
/datum/controller/subsystem/mapping/proc/do_wipe_turf_reservations()
@@ -575,7 +603,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
clearing |= used_turfs //used turfs is an associative list, BUT, reserve_turfs() can still handle it. If the code above works properly, this won't even be needed as the turfs would be freed already.
unused_turfs.Cut()
used_turfs.Cut()
reserve_turfs(clearing)
reserve_turfs(clearing, await = TRUE)
/datum/controller/subsystem/mapping/proc/build_minimaps()
to_chat(world, span_boldannounce("Building minimaps..."))
@@ -586,3 +614,15 @@ GLOBAL_LIST_EMPTY(the_station_areas)
for(var/B in areas)
var/area/A = B
A.reg_in_areas_in_z()
/datum/controller/subsystem/mapping/proc/build_area_turfs(z_level, space_guaranteed)
// If we know this is filled with default tiles, we can use the default area
// Faster
if(space_guaranteed)
var/area/global_area = GLOB.areas_by_type[world.area]
global_area.contained_turfs += Z_TURFS(z_level)
return
for(var/turf/to_contain as anything in Z_TURFS(z_level))
var/area/our_area = to_contain.loc
our_area.contained_turfs += to_contain

View File

@@ -516,9 +516,12 @@ SUBSYSTEM_DEF(shuttle)
var/turf/midpoint = locate(transit_x, transit_y, bottomleft.z)
if(!midpoint)
return FALSE
var/area/old_area = midpoint.loc
old_area.turfs_to_uncontain += proposal.reserved_turfs
var/area/shuttle/transit/A = new()
A.parallax_movedir = travel_dir
A.contents = proposal.reserved_turfs
A.contained_turfs = proposal.reserved_turfs
var/obj/docking_port/stationary/transit/new_transit_dock = new(midpoint)
new_transit_dock.reserved_area = proposal
new_transit_dock.name = "Transit for [M.id]/[M.name]"

View File

@@ -1,19 +1,33 @@
/datum/map_generator/cave_generator
var/name = "Cave Generator"
///Weighted list of the types that spawns if the turf is open
var/open_turf_types = list(/turf/open/floor/plating/asteroid/airless = 1)
var/weighted_open_turf_types = list(/turf/open/floor/plating/asteroid/airless = 1)
///Expanded list of the types that spawns if the turf is open
var/open_turf_types
///Weighted list of the types that spawns if the turf is closed
var/closed_turf_types = list(/turf/closed/mineral/random = 1)
var/weighted_closed_turf_types = list(/turf/closed/mineral/random = 1)
///Expanded list of the types that spawns if the turf is closed
var/closed_turf_types
///If this is set to TRUE then it will only change turfs that are /turf/open/genturf, for more flexability in design
var/gen_gurf_only = TRUE
///Weighted list of mobs that can spawn in the area.
var/list/weighted_mob_spawn_list
///Expanded list of mobs that can spawn in the area. Reads from the weighted list
var/list/mob_spawn_list
// Weighted list of Megafauna that can spawn in the caves
///The mob spawn list but with no megafauna markers. autogenerated
var/list/mob_spawn_no_mega_list
// Weighted list of Megafauna that can spawn in the area
var/list/weighted_megafauna_spawn_list
///Expanded list of Megafauna that can spawn in the area. Reads from the weighted list
var/list/megafauna_spawn_list
///Weighted list of flora that can spawn in the area.
var/list/weighted_flora_spawn_list
///Expanded list of flora that can spawn in the area. Reads from the weighted list
var/list/flora_spawn_list
///Weighted list of extra features that can spawn in the area, such as geysers.
var/list/weighted_feature_spawn_list
///Expanded list of extra features that can spawn in the area. Reads from the weighted list
var/list/feature_spawn_list
@@ -37,113 +51,119 @@
/datum/map_generator/cave_generator/New()
. = ..()
if(!mob_spawn_list)
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goldgrub = 1, /mob/living/simple_animal/hostile/asteroid/goliath = 5, /mob/living/simple_animal/hostile/asteroid/basilisk = 4, /mob/living/simple_animal/hostile/asteroid/hivelord = 3)
if(!megafauna_spawn_list)
megafauna_spawn_list = GLOB.megafauna_spawn_list
if(!flora_spawn_list)
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
if(!feature_spawn_list)
feature_spawn_list = list() //yogs no geysers in asteroid caves
if(!weighted_mob_spawn_list)
weighted_mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goldgrub = 1, /mob/living/simple_animal/hostile/asteroid/goliath = 5, /mob/living/simple_animal/hostile/asteroid/basilisk = 4, /mob/living/simple_animal/hostile/asteroid/hivelord = 3)
mob_spawn_list = expand_weights(weighted_mob_spawn_list)
mob_spawn_no_mega_list = expand_weights(weighted_mob_spawn_list - SPAWN_MEGAFAUNA)
if(!weighted_megafauna_spawn_list)
weighted_megafauna_spawn_list = GLOB.megafauna_spawn_list
megafauna_spawn_list = expand_weights(weighted_megafauna_spawn_list)
if(!weighted_flora_spawn_list)
weighted_flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2, /obj/structure/flora/ash/seraka = 2)
flora_spawn_list = expand_weights(weighted_flora_spawn_list)
if(!weighted_feature_spawn_list)
weighted_feature_spawn_list = list() // yogs - no geysers in asteroid caves
feature_spawn_list = expand_weights(weighted_feature_spawn_list)
open_turf_types = expand_weights(weighted_open_turf_types)
closed_turf_types = expand_weights(weighted_closed_turf_types)
/datum/map_generator/cave_generator/generate_terrain(list/turfs)
/datum/map_generator/cave_generator/generate_terrain(list/turfs, area/generate_in)
. = ..()
if(!(generate_in.area_flags & CAVES_ALLOWED))
return
var/start_time = REALTIMEOFDAY
string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data
// Area var pullouts to make accessing in the loop faster
var/flora_allowed = (generate_in.area_flags & FLORA_ALLOWED) && length(flora_spawn_list)
var/feature_allowed = (generate_in.area_flags & FLORA_ALLOWED) && length(feature_spawn_list)
var/mobs_allowed = (generate_in.area_flags & MOB_SPAWN_ALLOWED) && length(mob_spawn_list)
var/megas_allowed = (generate_in.area_flags & MEGAFAUNA_SPAWN_ALLOWED) && length(megafauna_spawn_list)
for(var/i in turfs) //Go through all the turfs and generate them
var/turf/gen_turf = i
if(gen_gurf_only && !istype(gen_turf,/turf/open/genturf))
if(gen_gurf_only && !istype(gen_turf, /turf/open/genturf))
continue
if(istype(gen_turf,/turf/closed/mineral))
if(istype(gen_turf, /turf/closed/mineral))
continue
var/area/A = gen_turf.loc
if(!(A.area_flags & CAVES_ALLOWED))
continue
var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "0"
var/turf/new_turf = pick(closed ? closed_turf_types : open_turf_types)
var/closed = text2num(string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x])
// The assumption is this will be faster then changeturf, and changeturf isn't required since by this point
// The old tile hasn't got the chance to init yet
new_turf = new new_turf(gen_turf)
var/stored_flags
if(gen_turf.flags_1 & NO_RUINS_1)
stored_flags |= NO_RUINS_1
new_turf.flags_1 |= NO_RUINS_1
var/turf/new_turf = pickweight(closed ? closed_turf_types : open_turf_types)
if(closed) //Open turfs have some special behavior related to spawning flora and mobs.
CHECK_TICK
continue
new_turf = gen_turf.ChangeTurf(new_turf, initial(new_turf.baseturfs), CHANGETURF_DEFER_CHANGE)
// If we've spawned something yet
var/spawned_something = FALSE
new_turf.flags_1 |= stored_flags
///Spawning isn't done in procs to save on overhead on the 60k turfs we're going through.
//FLORA SPAWNING HERE
if(flora_allowed && prob(flora_spawn_chance))
var/flora_type = pick(flora_spawn_list)
new flora_type(new_turf)
spawned_something = TRUE
if(!closed)//Open turfs have some special behavior related to spawning flora and mobs.
//FEATURE SPAWNING HERE
if(feature_allowed && prob(feature_spawn_chance))
var/can_spawn = TRUE
var/turf/open/new_open_turf = new_turf
var/atom/picked_feature = pick(feature_spawn_list)
///Spawning isn't done in procs to save on overhead on the 60k turfs we're going through.
//FLORA SPAWNING HERE
var/atom/spawned_flora
if(flora_spawn_list && !isemptylist(flora_spawn_list) && prob(flora_spawn_chance))
var/can_spawn = TRUE
if(!(A.area_flags & FLORA_ALLOWED))
for(var/obj/structure/existing_feature in range(7, new_turf))
if(istype(existing_feature, picked_feature))
can_spawn = FALSE
if(can_spawn)
spawned_flora = pickweight(flora_spawn_list)
spawned_flora = new spawned_flora(new_open_turf)
break
if(can_spawn)
new picked_feature(new_turf)
spawned_something = TRUE
//FEATURE SPAWNING HERE
var/atom/spawned_feature
if(feature_spawn_list && !isemptylist(feature_spawn_list) && prob(feature_spawn_chance))
var/can_spawn = TRUE
//MOB SPAWNING HERE
if(mobs_allowed && !spawned_something && prob(mob_spawn_chance))
var/atom/picked_mob = pick(mob_spawn_list)
if(!(A.area_flags & FLORA_ALLOWED)) //checks the same flag because lol dunno
if(picked_mob == SPAWN_MEGAFAUNA)
if(megas_allowed) //this is danger. it's boss time.
picked_mob = pick(megafauna_spawn_list)
else //this is not danger, don't spawn a boss, spawn something else
picked_mob = pick(mob_spawn_no_mega_list) //What if we used 100% of the brain...and did something (slightly) less shit than a while loop?
var/can_spawn = TRUE
// prevents tendrils spawning in each other's collapse range
if(ispath(picked_mob, /obj/structure/spawner/lavaland))
for(var/obj/structure/spawner/lavaland/spawn_blocker in range(2, new_turf))
can_spawn = FALSE
var/atom/picked_feature = pickweight(feature_spawn_list)
for(var/obj/structure/F in range(7, new_open_turf))
if(istype(F, picked_feature))
can_spawn = FALSE
if(can_spawn)
spawned_feature = new picked_feature(new_open_turf)
//MOB SPAWNING HERE
if(mob_spawn_list && !isemptylist(mob_spawn_list) && !spawned_flora && !spawned_feature && prob(mob_spawn_chance))
var/can_spawn = TRUE
if(!(A.area_flags & MOB_SPAWN_ALLOWED))
break
//if the random is a standard mob, avoid spawning if there's another one within 12 tiles
else if(ispath(picked_mob, /mob/living/simple_animal/hostile/asteroid))
for(var/mob/living/simple_animal/hostile/asteroid/mob_blocker in range(12, new_turf))
can_spawn = FALSE
break
//if there's a megafauna within standard view don't spawn anything at all (This isn't really consistent, I don't know why we do this. you do you tho)
if(can_spawn)
for(var/mob/living/simple_animal/hostile/megafauna/found_fauna in range(7, new_turf))
can_spawn = FALSE
break
var/atom/picked_mob = pickweight(mob_spawn_list)
if(picked_mob == SPAWN_MEGAFAUNA) //
if((A.area_flags & MEGAFAUNA_SPAWN_ALLOWED) && megafauna_spawn_list?.len) //this is danger. it's boss time.
picked_mob = pickweight(megafauna_spawn_list)
else //this is not danger, don't spawn a boss, spawn something else
picked_mob = pickweight(mob_spawn_list - SPAWN_MEGAFAUNA) //What if we used 100% of the brain...and did something (slightly) less shit than a while loop?
for(var/thing in urange(12, new_open_turf)) //prevents mob clumps
if(!ishostile(thing) && !istype(thing, /obj/structure/spawner))
continue
if((ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna) || ismegafauna(thing)) && get_dist(new_open_turf, thing) <= 7)
can_spawn = FALSE //if there's a megafauna within standard view don't spawn anything at all
break
if(ispath(picked_mob, /mob/living/simple_animal/hostile/asteroid) || istype(thing, /mob/living/simple_animal/hostile/asteroid))
can_spawn = FALSE //if the random is a standard mob, avoid spawning if there's another one within 12 tiles
break
if((ispath(picked_mob, /obj/structure/spawner/lavaland) || istype(thing, /obj/structure/spawner/lavaland)) && get_dist(new_open_turf, thing) <= 2)
can_spawn = FALSE //prevents tendrils spawning in each other's collapse range
break
if(can_spawn)
if(ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna/bubblegum)) //there can be only one bubblegum, so don't waste spawns on it
megafauna_spawn_list.Remove(picked_mob)
new picked_mob(new_open_turf)
if(can_spawn)
if(ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna/bubblegum)) //there can be only one bubblegum, so don't waste spawns on it
weighted_megafauna_spawn_list.Remove(picked_mob)
megafauna_spawn_list = expand_weights(weighted_megafauna_spawn_list)
megas_allowed = megas_allowed && length(megafauna_spawn_list)
new picked_mob(new_turf)
spawned_something = TRUE
CHECK_TICK
var/message = "[name] finished in [(REALTIMEOFDAY - start_time)/10]s!"

View File

@@ -1,20 +1,20 @@
/datum/map_generator/cave_generator/icemoon
open_turf_types = list(/turf/open/floor/plating/asteroid/snow/icemoon = 19, /turf/open/floor/plating/ice/icemoon = 1)
closed_turf_types = list(/turf/closed/mineral/random/snow = 1)
weighted_open_turf_types = list(/turf/open/floor/plating/asteroid/snow/icemoon = 19, /turf/open/floor/plating/ice/icemoon = 1)
weighted_closed_turf_types = list(/turf/closed/mineral/random/snow = 1)
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \
weighted_mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \
/mob/living/simple_animal/hostile/asteroid/polarbear = 30, /obj/structure/spawner/ice_moon/polarbear = 3, \
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50,
/mob/living/simple_animal/hostile/asteroid/marrowweaver/ice = 30,
/mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/rock/icy = 2, /obj/structure/flora/rock/pile/icy = 2, /obj/structure/flora/grass/both = 6)
weighted_flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/rock/icy = 2, /obj/structure/flora/rock/pile/icy = 2, /obj/structure/flora/grass/both = 6)
///Note that this spawn list is also in the lavaland generator
feature_spawn_list = list()
weighted_feature_spawn_list = null
/datum/map_generator/cave_generator/icemoon/surface
flora_spawn_chance = 4
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \
weighted_mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \
/mob/living/simple_animal/hostile/asteroid/polarbear = 30, /obj/structure/spawner/ice_moon/polarbear = 3, \
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50,
/mob/living/simple_animal/hostile/asteroid/marrowweaver/ice = 30,

View File

@@ -1,18 +1,18 @@
/datum/map_generator/cave_generator/lavaland
name = "Lavaland Base"
open_turf_types = list(/turf/open/floor/plating/asteroid/basalt/lava_land_surface = 1)
closed_turf_types = list(/turf/closed/mineral/random/volcanic = 1)
weighted_open_turf_types = list(/turf/open/floor/plating/asteroid/basalt/lava_land_surface = 1)
weighted_closed_turf_types = list(/turf/closed/mineral/random/volcanic = 1)
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /obj/structure/spawner/lavaland/goliath = 3, \
weighted_mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /obj/structure/spawner/lavaland/goliath = 3, \
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /obj/structure/spawner/lavaland = 2, \
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /obj/structure/spawner/lavaland/legion = 3, \
/mob/living/simple_animal/hostile/asteroid/marrowweaver = 30,
SPAWN_MEGAFAUNA = 4, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10
)
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
weighted_flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
///Note that this spawn list is also in the icemoon generator
feature_spawn_list = list(/obj/structure/geyser/ash = 10, /obj/structure/geyser/random = 2, /obj/structure/geyser/stable_plasma = 6, /obj/structure/geyser/oil = 8,/obj/structure/geyser/protozine = 10,/obj/structure/geyser/holywater = 2) //yogs, yes geysers
weighted_feature_spawn_list = list(/obj/structure/geyser/ash = 10, /obj/structure/geyser/random = 2, /obj/structure/geyser/stable_plasma = 6, /obj/structure/geyser/oil = 8,/obj/structure/geyser/protozine = 10,/obj/structure/geyser/holywater = 2) //yogs, yes geysers
initial_closed_chance = 45
smoothing_iterations = 50
@@ -37,7 +37,7 @@
var/min_offset = 0
var/max_offset = 5
/datum/map_generator/cave_generator/lavaland/generate_terrain(list/turfs)
/datum/map_generator/cave_generator/lavaland/generate_terrain(list/turfs, area/generate_in)
. = ..()
var/start_time = REALTIMEOFDAY
var/node_amount = rand(6,10)

View File

@@ -2,7 +2,7 @@
/datum/map_generator
///This proc will be ran by areas on Initialize, and provides the areas turfs as argument to allow for generation.
/datum/map_generator/proc/generate_terrain(list/turfs)
/datum/map_generator/proc/generate_terrain(list/turfs, area/generate_in)
return
/turf/open/genturf

View File

@@ -14,6 +14,15 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
invisibility = INVISIBILITY_LIGHTING
/// List of all turfs currently inside this area. Acts as a filtered bersion of area.contents
/// For faster lookup (area.contents is actually a filtered loop over world)
/// Semi fragile, but it prevents stupid so I think it's worth it
var/list/turf/contained_turfs = list()
/// Contained turfs is a MASSIVE list, so rather then adding/removing from it each time we have a problem turf
/// We should instead store a list of turfs to REMOVE from it, then hook into a getter for it
/// There is a risk of this and contained_turfs leaking, so a subsystem will run it down to 0 incrementally if it gets too large
var/list/turf/turfs_to_uncontain = list()
var/area_flags = 0
var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints.
@@ -109,20 +118,16 @@ GLOBAL_LIST_EMPTY(teleportlocs)
* The returned list of turfs is sorted by name
*/
/proc/process_teleport_locs()
for(var/V in GLOB.sortedAreas)
var/area/AR = V
for(var/area/AR as anything in get_sorted_areas())
if(istype(AR, /area/shuttle) || AR.noteleport)
continue
if(GLOB.teleportlocs[AR.name])
continue
if (!AR.contents.len)
if (!AR.has_contained_turfs())
continue
var/turf/picked = AR.contents[1]
if (picked && is_station_level(picked.z))
if (is_station_level(AR.z))
GLOB.teleportlocs[AR.name] = AR
sortTim(GLOB.teleportlocs, /proc/cmp_text_dsc)
/**
* Called when an area loads
*
@@ -142,6 +147,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
// rather than waiting for atoms to initialize.
if (unique)
GLOB.areas_by_type[type] = src
GLOB.areas += src
return ..()
/**
@@ -197,14 +203,33 @@ GLOBAL_LIST_EMPTY(teleportlocs)
var/list/turfs = list()
for(var/turf/T in contents)
turfs += T
map_generator.generate_terrain(turfs)
map_generator.generate_terrain(turfs, src)
/area/proc/test_gen()
if(map_generator)
var/list/turfs = list()
for(var/turf/T in contents)
turfs += T
map_generator.generate_terrain(turfs)
map_generator.generate_terrain(turfs, src)
/area/proc/get_contained_turfs()
if(length(turfs_to_uncontain))
cannonize_contained_turfs()
return contained_turfs
/// Ensures that the contained_turfs list properly represents the turfs actually inside us
/area/proc/cannonize_contained_turfs()
// This is massively suboptimal for LARGE removal lists
// Try and keep the mass removal as low as you can. We'll do this by ensuring
// We only actually add to contained turfs after large changes (Also the management subsystem)
// Do your damndest to keep turfs out of /area/space as a stepping stone
// That sucker gets HUGE and will make this take actual tens of seconds if you stuff turfs_to_uncontain
contained_turfs -= turfs_to_uncontain
turfs_to_uncontain = list()
/// Returns TRUE if we have contained turfs, FALSE otherwise
/area/proc/has_contained_turfs()
return length(contained_turfs) - length(turfs_to_uncontain) > 0
/**
* Register this area as belonging to a z level
@@ -216,22 +241,16 @@ GLOBAL_LIST_EMPTY(teleportlocs)
* areas don't have a valid z themself or something
*/
/area/proc/reg_in_areas_in_z()
if(contents.len)
var/list/areas_in_z = SSmapping.areas_in_z
var/z
update_areasize()
for(var/i in 1 to contents.len)
var/atom/thing = contents[i]
if(!thing)
continue
z = thing.z
break
if(!z)
WARNING("No z found for [src]")
return
if(!areas_in_z["[z]"])
areas_in_z["[z]"] = list()
areas_in_z["[z]"] += src
if(!has_contained_turfs())
return
var/list/areas_in_z = SSmapping.areas_in_z
update_areasize()
if(!z)
WARNING("No z found for [src]")
return
if(!areas_in_z["[z]"])
areas_in_z["[z]"] = list()
areas_in_z["[z]"] += src
/**
* Destroy an area and clean it up
@@ -244,6 +263,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/Destroy()
if(GLOB.areas_by_type[type] == src)
GLOB.areas_by_type[type] = null
GLOB.areas -= src
STOP_PROCESSING(SSobj, src)
return ..()
@@ -734,7 +754,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
always_unpowered = FALSE
valid_territory = FALSE
blob_allowed = FALSE
addSorted()
require_area_resort()
/**
* Set the area size of the area
*
@@ -745,7 +765,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if(outdoors)
return FALSE
areasize = 0
for(var/turf/open/T in contents)
for(var/turf/open/T in get_contained_turfs())
areasize++
/**

View File

@@ -94,7 +94,7 @@
*/
/atom/New(loc, ...)
//atom creation method that preloads variables at creation
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
if(GLOB.use_preloader && (src.type == GLOB._preloader_path))//in case the instanciated atom is creating other atoms in New()
world.preloader_load(src)
if(datum_flags & DF_USE_TAG)

View File

@@ -1480,7 +1480,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
/datum/objective/contract/proc/generate_dropoff()
var/found = FALSE
while (!found)
var/area/dropoff_area = pick(GLOB.sortedAreas)
var/area/dropoff_area = pick(GLOB.areas)
if(dropoff_area && is_station_level(dropoff_area.z) && !dropoff_area.outdoors)
dropoff = dropoff_area
found = TRUE

View File

@@ -22,22 +22,23 @@
var/obj/effect/landmark/river_waypoint/W = A
if (W.z != target_z || W.connected)
continue
W.connected = 1
W.connected = TRUE
// Workaround around ChangeTurf that's safe because of when this proc is called
var/turf/cur_turf = get_turf(W)
cur_turf.ChangeTurf(turf_type, null, CHANGETURF_IGNORE_AIR)
cur_turf = new turf_type(cur_turf)
var/turf/target_turf = get_turf(pick(river_nodes - W))
if(!target_turf)
break
var/detouring = 0
var/detouring = FALSE
var/cur_dir = get_dir(cur_turf, target_turf)
while(cur_turf != target_turf)
if(detouring) //randomly snake around a bit
if(prob(20))
detouring = 0
detouring = FALSE
cur_dir = get_dir(cur_turf, target_turf)
else if(prob(20))
detouring = 1
detouring = TRUE
if(prob(50))
cur_dir = turn(cur_dir, 45)
else
@@ -48,12 +49,13 @@
cur_turf = get_step(cur_turf, cur_dir)
var/area/new_area = get_area(cur_turf)
if(!istype(new_area, whitelist_area) || (cur_turf.flags_1 & NO_LAVA_GEN_1)) //Rivers will skip ruins
detouring = 0
detouring = FALSE
cur_dir = get_dir(cur_turf, target_turf)
cur_turf = get_step(cur_turf, cur_dir)
continue
else
var/turf/river_turf = cur_turf.ChangeTurf(turf_type, null, CHANGETURF_IGNORE_AIR)
// Workaround around ChangeTurf that's safe because of when this proc is called
var/turf/river_turf = new turf_type(cur_turf)
river_turf.Spread(25, 11, whitelist_area)
for(var/WP in river_nodes)
@@ -72,33 +74,42 @@
var/list/cardinal_turfs = list()
var/list/diagonal_turfs = list()
var/logged_turf_type
for(var/F in RANGE_TURFS(1, src) - src)
var/turf/T = F
var/area/new_area = get_area(T)
if(!T || (T.density && !ismineralturf(T)) || istype(T, /turf/open/indestructible) || (whitelisted_area && !istype(new_area, whitelisted_area)) || (T.flags_1 & NO_LAVA_GEN_1) )
for(var/turf/candidate as anything in RANGE_TURFS(1, src) - src)
if(!candidate || (candidate.density && !ismineralturf(candidate)) || isindestructiblefloor(candidate))
continue
if(!logged_turf_type && ismineralturf(T))
var/turf/closed/mineral/M = T
logged_turf_type = M.turf_type
var/area/new_area = get_area(candidate)
if((!istype(new_area, whitelisted_area) && whitelisted_area) || (candidate.flags_1 & NO_LAVA_GEN_1))
continue
if(get_dir(src, F) in GLOB.cardinals)
cardinal_turfs += F
if(!logged_turf_type && ismineralturf(candidate))
var/turf/closed/mineral/mineral_candidate = candidate
logged_turf_type = mineral_candidate.turf_type
if(get_dir(src, candidate) in GLOB.cardinals)
cardinal_turfs += candidate
else
diagonal_turfs += F
diagonal_turfs += candidate
for(var/F in cardinal_turfs) //cardinal turfs are always changed but don't always spread
var/turf/T = F
if(!istype(T, logged_turf_type) && T.ChangeTurf(type, null, CHANGETURF_IGNORE_AIR) && prob(probability))
T.Spread(probability - prob_loss, prob_loss, whitelisted_area)
for(var/turf/cardinal_candidate as anything in cardinal_turfs) //cardinal turfs are always changed but don't always spread
// NOTE: WE ARE SKIPPING CHANGETURF HERE
// The calls in this proc only serve to provide a satisfactory (if it's not ALREADY this) check. They do not actually call changeturf
// This is safe because this proc can only be run during mapload, and nothing has initialized by now so there's nothing to inherit or delete
if(!istype(cardinal_candidate, logged_turf_type) && cardinal_candidate.ChangeTurf(type, baseturfs, CHANGETURF_SKIP) && prob(probability))
if(baseturfs)
cardinal_candidate.baseturfs = baseturfs
cardinal_candidate.Spread(probability - prob_loss, prob_loss, whitelisted_area)
for(var/F in diagonal_turfs) //diagonal turfs only sometimes change, but will always spread if changed
var/turf/T = F
if(!istype(T, logged_turf_type) && prob(probability) && T.ChangeTurf(type, null, CHANGETURF_IGNORE_AIR))
T.Spread(probability - prob_loss, prob_loss, whitelisted_area)
else if(ismineralturf(T))
var/turf/closed/mineral/M = T
M.ChangeTurf(M.turf_type, null, CHANGETURF_IGNORE_AIR)
for(var/turf/diagonal_candidate as anything in diagonal_turfs) //diagonal turfs only sometimes change, but will always spread if changed
// Important NOTE: SEE ABOVE
if(!istype(diagonal_candidate, logged_turf_type) && prob(probability) && diagonal_candidate.ChangeTurf(type, baseturfs, CHANGETURF_SKIP))
if(baseturfs)
diagonal_candidate.baseturfs = baseturfs
diagonal_candidate.Spread(probability - prob_loss, prob_loss, whitelisted_area)
else if(ismineralturf(diagonal_candidate))
var/turf/closed/mineral/diagonal_mineral = diagonal_candidate
// SEE ABOVE, THIS IS ONLY VERY RARELY SAFE
new diagonal_mineral.turf_type(diagonal_mineral)
#undef RANDOM_UPPER_X

View File

@@ -1,4 +1,4 @@
/client/proc/jumptoarea(area/A in GLOB.sortedAreas)
/client/proc/jumptoarea(area/A in get_sorted_areas())
set name = "Jump to Area"
set desc = "Area to jump to"
set category = "Admin"
@@ -138,21 +138,28 @@
usr.forceMove(M.loc)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/sendmob(mob/M in sortmobs())
/client/proc/sendmob(mob/jumper in sortmobs())
set category = "Admin.Player Interaction"
set name = "Send Mob"
if(!src.holder)
to_chat(src, "Only administrators may use this command.", confidential=TRUE)
return
var/area/A = input(usr, "Pick an area.", "Pick an area") in GLOB.sortedAreas|null
if(A && istype(A))
if(M.forceMove(safepick(get_area_turfs(A))))
var/list/sorted_areas = get_sorted_areas()
if(!length(sorted_areas))
to_chat(src, "No areas found.", confidential = TRUE)
return
var/area/target_area = input(src, "Pick an area", "Send Mob", sorted_areas)
if(isnull(target_area))
return
if(!istype(target_area))
return
var/list/turfs = get_area_turfs(target_area)
if(length(turfs) && jumper.forceMove(pick(turfs)))
message_admins("[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(jumper)] to [AREACOORD(jumper)]")
message_admins("[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(A)]")
var/log_msg = "[key_name(usr)] teleported [key_name(M)] to [AREACOORD(A)]"
log_admin(log_msg)
admin_ticket_log(M, log_msg, TRUE)
else
to_chat(src, "Failed to move mob to a valid location.", confidential=TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
var/log_msg = "[key_name(usr)] teleported [key_name(jumper)] to [AREACOORD(jumper)]"
log_admin(log_msg)
admin_ticket_log(jumper, log_msg, TRUE)
else
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -582,7 +582,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
message_admins(span_adminnotice("[key_name_admin(usr)] used the Test Areas debug command checking [log_message]."))
log_admin("[key_name(usr)] used the Test Areas debug command checking [log_message].")
for(var/area/A in world)
for(var/area/A as anything in GLOB.areas)
if(on_station)
var/turf/picked = safepick(get_area_turfs(A.type))
if(picked && is_station_level(picked.z))

View File

@@ -153,7 +153,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
else
L.fully_heal()
for(var/area/A in GLOB.sortedAreas)
for(var/area/A in GLOB.areas)
if(!(A.type in GLOB.the_station_areas))
continue
if(!A.blob_allowed)

View File

@@ -461,7 +461,7 @@
..()
var/sanity = 0
while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
var/area/summon = pick(GLOB.sortedAreas - summon_spots)
var/area/summon = pick(GLOB.areas - summon_spots)
if(summon && is_station_level(summon.z) && summon.valid_territory)
summon_spots += summon
sanity++

View File

@@ -71,9 +71,9 @@
else
var/mob/user_mob = user
holder = user_mob.client //if its a mob, assign the mob's client to holder
bay = locate(/area/centcom/supplypod/loading/one) in GLOB.sortedAreas //Locate the default bay (one) from the centcom map
bay = locate(/area/centcom/supplypod/loading/one) in GLOB.areas //Locate the default bay (one) from the centcom map
bayNumber = bay.loading_id //Used as quick reference to what bay we're taking items from
var/area/pod_storage_area = locate(/area/centcom/supplypod/pod_storage) in GLOB.sortedAreas
var/area/pod_storage_area = locate(/area/centcom/supplypod/pod_storage) in GLOB.areas
temp_pod = new(pick(get_area_turfs(pod_storage_area))) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed)
orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1)
selector = new(null, holder.mob)

View File

@@ -179,7 +179,7 @@
if (!landingzone)
WARNING("[src] couldnt find a Quartermaster/Storage (aka cargobay) area on the station, and as such it has set the supplypod landingzone to the area it resides in.")
landingzone = get_area(src)
for(var/turf/open/floor/T in landingzone.contents)//uses default landing zone
for(var/turf/open/floor/T in landingzone.get_contained_turfs())//uses default landing zone
if(is_blocked_turf(T))
continue
LAZYADD(empty_turfs, T)
@@ -196,7 +196,7 @@
else
if(SO.pack.get_cost() * (0.72*MAX_EMAG_ROCKETS) <= points_to_check) // bulk discount :^)
landingzone = GLOB.areas_by_type[pick(GLOB.the_station_areas)] //override default landing zone
for(var/turf/open/floor/T in landingzone.contents)
for(var/turf/open/floor/T in landingzone.get_contained_turfs())
if(is_blocked_turf(T))
continue
LAZYADD(empty_turfs, T)

View File

@@ -103,9 +103,8 @@
if(!port)
return
var/list/shuttle_areas = port.shuttle_areas
for(var/r in shuttle_areas)
var/area/region = r
for(var/turf/place in region.contents)
for(var/area/region as anything in shuttle_areas)
for(var/turf/place as anything in region.get_contained_turfs())
if(get_dist(user, place) > 7)
continue
var/image/pic

View File

@@ -30,7 +30,7 @@
allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes
return safepick(typecache_filter_list(GLOB.sortedAreas,allowed_areas))
return safepick(typecache_filter_list(GLOB.areas,allowed_areas))
/datum/round_event/anomaly/setup()
impact_area = findEventArea()
@@ -49,4 +49,4 @@
if(T)
newAnomaly = new anomaly_path(T)
if (newAnomaly)
announce_to_ghosts(newAnomaly)
announce_to_ghosts(newAnomaly)

View File

@@ -28,27 +28,24 @@
M.playsound_local(M, 'sound/ambience/aurora_caelus.ogg', 20, FALSE, pressure_affected = FALSE)
/datum/round_event/aurora_caelus/start()
for(var/area in GLOB.sortedAreas)
var/area/A = area
for(var/area/A as anything in GLOB.areas)
if(initial(A.dynamic_lighting) == DYNAMIC_LIGHTING_IFSTARLIGHT)
for(var/turf/open/space/S in A)
for(var/turf/open/space/S in A.get_contained_turfs())
S.set_light(S.light_range * 3, S.light_power * 2)
/datum/round_event/aurora_caelus/tick()
if(activeFor % 5 == 0)
aurora_progress++
var/aurora_color = aurora_colors[aurora_progress]
for(var/area in GLOB.sortedAreas)
var/area/A = area
for(var/area/A as anything in GLOB.areas)
if(initial(A.dynamic_lighting) == DYNAMIC_LIGHTING_IFSTARLIGHT)
for(var/turf/open/space/S in A)
for(var/turf/open/space/S in A.get_contained_turfs())
S.set_light(l_color = aurora_color)
/datum/round_event/aurora_caelus/end()
for(var/area in GLOB.sortedAreas)
var/area/A = area
for(var/area/A as anything in GLOB.areas)
if(initial(A.dynamic_lighting) == DYNAMIC_LIGHTING_IFSTARLIGHT)
for(var/turf/open/space/S in A)
for(var/turf/open/space/S in A.get_contained_turfs())
fade_to_black(S)
priority_announce("The aurora caelus event is now ending. Starlight conditions will slowly return to normal. When this has concluded, please return to your workplace and continue work as normal. Have a pleasant shift, [station_name()], and thank you for watching with us.",
sound = 'sound/misc/notice2.ogg',

View File

@@ -24,7 +24,7 @@
severity = rand(1,3)
for(var/i in 1 to severity)
var/picked_area = pick_n_take(potential_areas)
for(var/area/A in world)
for(var/area/A as anything in GLOB.areas)
if(istype(A, picked_area))
areasToOpen += A

View File

@@ -13,8 +13,8 @@
var/obj/structure/spacevine/SV = new()
for(var/area/maintenance/A in world)
for(var/turf/F in A)
for(var/area/maintenance/A in GLOB.areas)
for(var/turf/F as anything in A.get_contained_turfs())
if(F.Enter(SV))
turfs += F

View File

@@ -76,6 +76,6 @@
///Subtypes from the above that actually should explode.
var/list/unsafe_area_subtypes = typecacheof(list(/area/engine/break_room))
allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes
var/list/possible_areas = typecache_filter_list(GLOB.sortedAreas,allowed_areas)
var/list/possible_areas = typecache_filter_list(GLOB.areas, allowed_areas)
if (length(possible_areas))
return pick(possible_areas)

View File

@@ -1,10 +1,9 @@
/proc/create_all_lighting_objects()
for(var/area/A in world)
for(var/area/A as anything in GLOB.areas)
if(!IS_DYNAMIC_LIGHTING(A))
continue
for(var/turf/T in A)
for(var/turf/T as anything in A.get_contained_turfs())
if(!IS_DYNAMIC_LIGHTING(T))
continue

View File

@@ -59,14 +59,13 @@
var/x = round((world.maxx - width)/2)
var/y = round((world.maxy - height)/2)
var/datum/space_level/level = SSmapping.add_new_zlevel(name, secret ? ZTRAITS_AWAY_SECRET : ZTRAITS_AWAY)
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
var/datum/space_level/level = SSmapping.add_new_zlevel(name, secret ? ZTRAITS_AWAY_SECRET : ZTRAITS_AWAY, contain_turfs = FALSE)
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE, new_z = TRUE)
var/list/bounds = parsed.bounds
if(!bounds)
return FALSE
repopulate_sorted_areas()
require_area_resort()
//initialize things that are normally initialized after map load
parsed.initTemplateBounds()
smooth_zlevel(world.maxz)
@@ -79,9 +78,9 @@
T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
if(!T)
return
if(T.x+width > world.maxx)
if((T.x+width) - 1 > world.maxx)
return
if(T.y+height > world.maxy)
if((T.y+height) - 1 > world.maxy)
return
// Accept cached maps, but don't save them automatically - we don't want
@@ -94,8 +93,7 @@
if(!bounds)
return
if(!SSmapping.loading_ruins) //Will be done manually during mapping ss init
repopulate_sorted_areas()
require_area_resort()
//initialize things that are normally initialized after map load
parsed.initTemplateBounds()

View File

@@ -1,25 +1,24 @@
// global datum that will preload variables on atoms instanciation
GLOBAL_VAR_INIT(use_preloader, FALSE)
GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new)
GLOBAL_LIST_INIT(_preloader_attributes, null)
GLOBAL_LIST_INIT(_preloader_path, null)
/// Preloader datum
/datum/map_preloader
parent_type = /datum
var/list/attributes
var/target_path
/world/proc/preloader_setup(list/the_attributes, path)
if(the_attributes.len)
GLOB.use_preloader = TRUE
var/datum/map_preloader/preloader_local = GLOB._preloader
preloader_local.attributes = the_attributes
preloader_local.target_path = path
GLOB._preloader_attributes = the_attributes
GLOB._preloader_path = path
/world/proc/preloader_load(atom/what)
GLOB.use_preloader = FALSE
var/datum/map_preloader/preloader_local = GLOB._preloader
for(var/attribute in preloader_local.attributes)
var/value = preloader_local.attributes[attribute]
var/list/attributes = GLOB._preloader_attributes
for(var/attribute in attributes)
var/value = attributes[attribute]
if(islist(value))
value = deepCopyList(value)
#ifdef TESTING

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/datum/map_template/ruin/proc/try_to_place(z,allowed_areas,turf/forced_turf)
/datum/map_template/ruin/proc/try_to_place(z, list/allowed_areas_typecache, turf/forced_turf, clear_below)
var/sanity = forced_turf ? 1 : PLACEMENT_TRIES
while(sanity > 0)
sanity--
@@ -6,18 +6,22 @@
var/height_border = TRANSITIONEDGE + SPACERUIN_MAP_EDGE_PAD + round(height / 2)
var/turf/central_turf = locate(rand(width_border, world.maxx - width_border), rand(height_border, world.maxy - height_border), z)
var/valid = TRUE
var/list/affected_turfs = get_affected_turfs(central_turf,1)
var/list/affected_areas = list()
for(var/turf/check in get_affected_turfs(central_turf,1))
var/area/new_area = get_area(check)
for(var/turf/check in affected_turfs)
// Use assoc lists to move this out, it's easier that way
if(check.flags_1 & NO_RUINS_1)
valid = FALSE
else
valid = FALSE // set to false before we check
for(var/type in allowed_areas)
if(istype(new_area, type)) // it's at least one of our types so it's whitelisted
valid = TRUE
break
if(!valid)
break
var/area/new_area = get_area(check)
affected_areas[new_area] = TRUE
// This is faster yes. Only BARELY but it is faster
for(var/area/affct_area as anything in affected_areas)
if(!allowed_areas_typecache[affct_area.type])
valid = FALSE
break
if(!valid)
@@ -25,29 +29,32 @@
testing("Ruin \"[name]\" placed at ([central_turf.x], [central_turf.y], [central_turf.z])")
for(var/i in get_affected_turfs(central_turf, 1))
var/turf/T = i
for(var/obj/structure/spawner/nest in T)
qdel(nest)
for(var/mob/living/simple_animal/monster in T)
qdel(monster)
for(var/obj/structure/flora/ash/plant in T)
qdel(plant)
if(clear_below)
var/list/static/clear_below_typecache = typecacheof(list(
/obj/structure/spawner,
/mob/living/simple_animal,
/obj/structure/flora
))
for(var/turf/T as anything in affected_turfs)
for(var/atom/thing as anything in T)
if(clear_below_typecache[thing.type])
qdel(thing)
load(central_turf,centered = TRUE)
loaded++
for(var/turf/T in get_affected_turfs(central_turf, 1))
for(var/turf/T in affected_turfs)
T.flags_1 |= NO_RUINS_1
new /obj/effect/landmark/ruin(central_turf, src)
return central_turf
/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = list(/area/space), list/potentialRuins)
/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = list(/area/space), list/potentialRuins, clear_below = FALSE)
if(!z_levels || !z_levels.len)
WARNING("No Z levels provided - Not generating ruins")
return
var/list/whitelist_typecache = typecacheof(whitelist)
for(var/zl in z_levels)
var/turf/T = locate(1, 1, zl)
@@ -111,7 +118,7 @@
else
break outer
placed_turf = current_pick.try_to_place(target_z,whitelist,forced_turf)
placed_turf = current_pick.try_to_place(target_z,whitelist_typecache,forced_turf,clear_below)
if(!placed_turf)
continue
else

View File

@@ -14,11 +14,11 @@
turf_type = /turf/open/space/transit
/datum/turf_reservation/proc/Release()
var/v = reserved_turfs.Copy()
for(var/i in reserved_turfs)
reserved_turfs -= i
SSmapping.used_turfs -= i
SSmapping.reserve_turfs(v)
var/list/reserved_copy = reserved_turfs.Copy()
SSmapping.used_turfs -= reserved_turfs
reserved_turfs = list()
// Makes the linter happy, even tho we don't await this
INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/proc/reserve_turfs, reserved_copy)
/datum/turf_reservation/proc/Reserve(width, height, zlevel)
if(width > world.maxx || height > world.maxy || width < 1 || height < 1)

View File

@@ -22,122 +22,141 @@
neigbours[TEXT_WEST] = P.spl
P.spl.neigbours[TEXT_EAST] = src
#define CHORDS_TO_1D(x, y, grid_diameter) ((x) + ((y) - 1) * (grid_diameter))
/datum/space_transition_point //this is explicitly utilitarian datum type made specially for the space map generation and are absolutely unusable for anything else
var/list/neigbours = list()
var/x
var/y
var/datum/space_level/spl
/datum/space_transition_point/New(nx, ny, list/point_grid)
if(!point_grid)
/datum/space_transition_point/New(nx, ny, list/grid)
if(!grid)
qdel(src)
return
var/list/L = point_grid[1]
if(nx > point_grid.len || ny > L.len)
var/grid_diameter = sqrt(length(grid))
if(nx > grid_diameter || ny > grid_diameter)
stack_trace("Attempted to set a position outside the size of [grid_diameter]")
qdel(src)
return
x = nx
y = ny
if(point_grid[x][y])
var/position = CHORDS_TO_1D(x, y, grid_diameter)
if(grid[position])
return
point_grid[x][y] = src
grid[position] = src
/datum/space_transition_point/proc/set_neigbours(list/grid)
var/max_X = grid.len
var/list/max_Y = grid[1]
max_Y = max_Y.len
/datum/space_transition_point/proc/set_neigbours(list/grid, size)
neigbours.Cut()
if(x+1 <= max_X)
neigbours |= grid[x+1][y]
if(x+1 <= size)
neigbours |= grid[CHORDS_TO_1D(x+1, y, size)]
if(x-1 >= 1)
neigbours |= grid[x-1][y]
if(y+1 <= max_Y)
neigbours |= grid[x][y+1]
neigbours |= grid[CHORDS_TO_1D(x-1, y, size)]
if(y+1 <= size)
neigbours |= grid[CHORDS_TO_1D(x, y + 1, size)]
if(y-1 >= 1)
neigbours |= grid[x][y-1]
neigbours |= grid[CHORDS_TO_1D(x, y - 1, size)]
/datum/controller/subsystem/mapping/proc/setup_map_transitions() //listamania
var/list/SLS = list()
var/list/transition_levels = list()
var/list/cached_z_list = z_list
var/conf_set_len = 0
for(var/A in cached_z_list)
var/datum/space_level/D = A
if (D.linkage == CROSSLINKED)
SLS.Add(D)
conf_set_len++
var/list/point_grid[conf_set_len*2+1][conf_set_len*2+1]
var/list/grid = list()
var/datum/space_transition_point/P
for(var/i = 1, i<=conf_set_len*2+1, i++)
for(var/j = 1, j<=conf_set_len*2+1, j++)
P = new/datum/space_transition_point(i,j, point_grid)
point_grid[i][j] = P
grid.Add(P)
for(var/datum/space_transition_point/pnt in grid)
pnt.set_neigbours(point_grid)
P = point_grid[conf_set_len+1][conf_set_len+1]
for(var/datum/space_level/level as anything in cached_z_list)
if (level.linkage == CROSSLINKED)
transition_levels.Add(level)
var/grid_diameter = (length(transition_levels) * 2) + 1
var/list/grid = new /list(grid_diameter ** 2)
var/datum/space_transition_point/point
for(var/x in 1 to grid_diameter)
for(var/y in 1 to grid_diameter)
point = new/datum/space_transition_point(x, y, grid)
grid[CHORDS_TO_1D(x, y, grid_diameter)] = point
for(point as anything in grid)
point.set_neigbours(grid, grid_diameter)
var/center = round(grid_diameter / 2)
point = grid[CHORDS_TO_1D(grid_diameter, center, center)]
grid.Cut()
var/list/transition_pick = transition_levels.Copy()
var/list/possible_points = list()
var/list/used_points = list()
grid.Cut()
while(SLS.len)
var/datum/space_level/D = pick_n_take(SLS)
D.xi = P.x
D.yi = P.y
P.spl = D
possible_points |= P.neigbours
used_points |= P
while(transition_pick.len)
var/datum/space_level/level = pick_n_take(transition_pick)
level.xi = point.x
level.yi = point.y
point.spl = level
possible_points |= point.neigbours
used_points |= point
possible_points.Remove(used_points)
D.set_neigbours(used_points)
P = pick(possible_points)
level.set_neigbours(used_points)
point = pick(possible_points)
CHECK_TICK
// Now that we've handed out neighbors, we're gonna handle an edge case
// Need to check if all our levels have neighbors in all directions
// If they don't, we'll make them wrap all the way around to the other side of the grid
for(var/direction in GLOB.cardinals)
var/dir = "[direction]"
var/inverse = "[turn(direction, 180)]"
for(var/datum/space_level/level as anything in transition_levels)
// If we have something in this dir that isn't just us, continue on
if(level.neigbours[dir] && level.neigbours[dir] != level)
continue
var/datum/space_level/head = level
while(head.neigbours[inverse] && head.neigbours[inverse] != head)
head = head.neigbours[inverse]
// Alllright we've landed on someone who we can wrap around onto safely, let's make that connection yeah?
head.neigbours[inverse] = level
level.neigbours[dir] = head
//Lists below are pre-calculated values arranged in the list in such a way to be easily accessable in the loop by the counter
//Its either this or madness with lotsa math
var/list/x_pos_beginning = list(1, 1, world.maxx - TRANSITIONEDGE, 1) //x values of the lowest-leftest turfs of the respective 4 blocks on each side of zlevel
var/list/y_pos_beginning = list(world.maxy - TRANSITIONEDGE, 1, 1 + TRANSITIONEDGE, 1 + TRANSITIONEDGE) //y values respectively
var/inner_max_x = world.maxx - TRANSITIONEDGE
var/inner_max_y = world.maxy - TRANSITIONEDGE
var/list/x_pos_beginning = list(1, 1, inner_max_x, 1) //x values of the lowest-leftest turfs of the respective 4 blocks on each side of zlevel
var/list/y_pos_beginning = list(inner_max_y, 1, 1 + TRANSITIONEDGE, 1 + TRANSITIONEDGE) //y values respectively
var/list/x_pos_ending = list(world.maxx, world.maxx, world.maxx, 1 + TRANSITIONEDGE) //x values of the highest-rightest turfs of the respective 4 blocks on each side of zlevel
var/list/y_pos_ending = list(world.maxy, 1 + TRANSITIONEDGE, world.maxy - TRANSITIONEDGE, world.maxy - TRANSITIONEDGE) //y values respectively
var/list/x_pos_transition = list(1, 1, TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 1) //values of x for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective x value later in the code
var/list/y_pos_transition = list(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 1, 1, 1) //values of y for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective y value later in the code
var/list/y_pos_ending = list(world.maxy, 1 + TRANSITIONEDGE, inner_max_y, inner_max_y) //y values respectively
var/list/x_pos_transition = list(1, 1, TRANSITIONEDGE + 2, inner_max_x - 1) //values of x for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective x value later in the code
var/list/y_pos_transition = list(TRANSITIONEDGE + 2, inner_max_y - 1, 1, 1) //values of y for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective y value later in the code
for(var/I in cached_z_list)
var/datum/space_level/D = I
if(!D.neigbours.len)
for(var/datum/space_level/level as anything in cached_z_list)
if(!level.neigbours.len)
continue
var/zlevelnumber = D.z_value
var/zlevelnumber = level.z_value
for(var/side in 1 to 4)
var/turf/beginning = locate(x_pos_beginning[side], y_pos_beginning[side], zlevelnumber)
var/turf/ending = locate(x_pos_ending[side], y_pos_ending[side], zlevelnumber)
var/list/turfblock = block(beginning, ending)
var/dirside = 2**(side-1)
var/zdestination = zlevelnumber
if(D.neigbours["[dirside]"] && D.neigbours["[dirside]"] != D)
D = D.neigbours["[dirside]"]
zdestination = D.z_value
else
dirside = turn(dirside, 180)
while(D.neigbours["[dirside]"] && D.neigbours["[dirside]"] != D)
D = D.neigbours["[dirside]"]
zdestination = D.z_value
D = I
var/x_target = x_pos_transition[side] == 1 ? 0 : x_pos_transition[side]
var/y_target = y_pos_transition[side] == 1 ? 0 : y_pos_transition[side]
var/datum/space_level/neighbor = level.neigbours["[dirside]"]
var/zdestination = neighbor.z_value
for(var/turf/open/space/S in turfblock)
S.destination_x = x_pos_transition[side] == 1 ? S.x : x_pos_transition[side]
S.destination_y = y_pos_transition[side] == 1 ? S.y : y_pos_transition[side]
S.destination_x = x_target || S.x
S.destination_y = y_target || S.y
S.destination_z = zdestination
// Mirage border code
var/mirage_dir
if(S.x == 1 + TRANSITIONEDGE)
mirage_dir |= WEST
else if(S.x == world.maxx - TRANSITIONEDGE)
else if(S.x == inner_max_x)
mirage_dir |= EAST
if(S.y == 1 + TRANSITIONEDGE)
mirage_dir |= SOUTH
else if(S.y == world.maxy - TRANSITIONEDGE)
else if(S.y == inner_max_y)
mirage_dir |= NORTH
if(!mirage_dir)
continue
var/turf/place = locate(S.destination_x, S.destination_y, S.destination_z)
var/turf/place = locate(S.destination_x, S.destination_y, zdestination)
S.AddComponent(/datum/component/mirage_border, place, mirage_dir)
#undef CHORDS_TO_1D

View File

@@ -14,9 +14,10 @@
for (var/I in 1 to default_map_traits.len)
var/list/features = default_map_traits[I]
var/datum/space_level/S = new(I, features[DL_NAME], features[DL_TRAITS])
build_area_turfs(I, FALSE)
z_list += S
/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level)
/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level, filled_with_space = TRUE, contain_turfs = TRUE)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, args)
var/new_z = z_list.len + 1
if (world.maxz < new_z)
@@ -24,6 +25,8 @@
CHECK_TICK
// TODO: sleep here if the Z level needs to be cleared
var/datum/space_level/S = new z_type(new_z, name, traits)
if(contain_turfs)
build_area_turfs(new_z, filled_with_space)
z_list += S
return S

View File

@@ -404,8 +404,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(usr, "Not when you're not dead!")
return
var/list/filtered = list()
for(var/V in GLOB.sortedAreas)
var/area/A = V
for(var/area/A as anything in get_sorted_areas())
if(!A.hidden)
filtered += A
var/area/thearea = input("Area to jump to", "BOOYEA") as null|anything in filtered

View File

@@ -34,7 +34,7 @@
var/list/obj/structure/cable/cables = list()
var/list/atom/atoms = list()
repopulate_sorted_areas()
require_area_resort()
for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], SSmapping.station_start),
locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1)))

View File

@@ -133,19 +133,21 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new)
GLOBAL_VAR_INIT(emergency_access, FALSE)
/proc/make_maint_all_access()
for(var/area/maintenance/A in world)
for(var/obj/machinery/door/airlock/D in A)
D.emergency = TRUE
D.update_icon(0)
for(var/area/maintenance/A in GLOB.areas)
for(var/turf/in_area as anything in A.get_contained_turfs())
for(var/obj/machinery/door/airlock/D in in_area)
D.emergency = TRUE
D.update_icon(ALL, 0)
minor_announce("Access restrictions on maintenance and external airlocks have been lifted.", "Attention! Station-wide emergency declared!",1)
GLOB.emergency_access = TRUE
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency maintenance access", "enabled"))
/proc/revoke_maint_all_access()
for(var/area/maintenance/A in world)
for(var/obj/machinery/door/airlock/D in A)
D.emergency = FALSE
D.update_icon(0)
for(var/area/maintenance/A in GLOB.areas)
for(var/turf/in_area as anything in A.get_contained_turfs())
for(var/obj/machinery/door/airlock/D in in_area)
D.emergency = FALSE
D.update_icon(ALL, 0)
minor_announce("Access restrictions in maintenance areas have been restored.", "Attention! Station-wide emergency rescinded:")
GLOB.emergency_access = FALSE
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency maintenance access", "disabled"))
@@ -157,4 +159,4 @@ GLOBAL_VAR_INIT(emergency_access, FALSE)
#undef KEYCARD_RED_ALERT
#undef KEYCARD_EMERGENCY_MAINTENANCE_ACCESS
#undef KEYCARD_BSA_UNLOCK
#undef KEYCARD_BSA_UNLOCK

View File

@@ -37,7 +37,7 @@
areas = list()
var/list/new_latejoin = list()
for(var/area/shuttle/arrival/A in GLOB.sortedAreas)
for(var/area/shuttle/arrival/A in GLOB.areas)
for(var/obj/structure/chair/C in A)
new_latejoin += C
if(!console)

View File

@@ -388,7 +388,7 @@
if(time_left <= 50 && !sound_played) //4 seconds left:REV UP THOSE ENGINES BOYS. - should sync up with the launch
sound_played = 1 //Only rev them up once.
var/list/areas = list()
for(var/area/shuttle/escape/E in GLOB.sortedAreas)
for(var/area/shuttle/escape/E in GLOB.areas)
areas += E
hyperspace_sound(HYPERSPACE_WARMUP, areas)
@@ -400,7 +400,7 @@
//now move the actual emergency shuttle to its transit dock
var/list/areas = list()
for(var/area/shuttle/escape/E in GLOB.sortedAreas)
for(var/area/shuttle/escape/E in GLOB.areas)
areas += E
hyperspace_sound(HYPERSPACE_LAUNCH, areas)
enterTransit()
@@ -415,7 +415,7 @@
if(SHUTTLE_ESCAPE)
if(sound_played && time_left <= HYPERSPACE_END_TIME)
var/list/areas = list()
for(var/area/shuttle/escape/E in GLOB.sortedAreas)
for(var/area/shuttle/escape/E in GLOB.areas)
areas += E
hyperspace_sound(HYPERSPACE_END, areas)
if(time_left <= PARALLAX_LOOP_TIME)

View File

@@ -151,7 +151,9 @@ All ShuttleMove procs go here
return TRUE
contents -= oldT
turfs_to_uncontain += oldT
underlying_old_area.contents += oldT
underlying_old_area.contained_turfs += oldT
oldT.change_area(src, underlying_old_area)
//The old turf has now been given back to the area that turf originaly belonged to
@@ -159,7 +161,9 @@ All ShuttleMove procs go here
parallax_movedir = old_dest_area.parallax_movedir
old_dest_area.contents -= newT
old_dest_area.turfs_to_uncontain += newT
contents += newT
contained_turfs += newT
newT.change_area(old_dest_area, src)
return TRUE

View File

@@ -482,7 +482,9 @@
if(!oldT || !istype(oldT.loc, area_type))
continue
var/area/old_area = oldT.loc
old_area.turfs_to_uncontain += oldT
underlying_area.contents += oldT
underlying_area.contained_turfs += oldT
oldT.change_area(old_area, underlying_area)
oldT.empty(FALSE)

View File

@@ -119,7 +119,7 @@
#include "code\__DEFINES\time.dm"
#include "code\__DEFINES\tools.dm"
#include "code\__DEFINES\traits.dm"
#include "code\__DEFINES\turf_flags.dm"
#include "code\__DEFINES\turfs.dm"
#include "code\__DEFINES\typeids.dm"
#include "code\__DEFINES\vehicles.dm"
#include "code\__DEFINES\vv.dm"
@@ -275,6 +275,7 @@
#include "code\controllers\subsystem\acid.dm"
#include "code\controllers\subsystem\adjacent_air.dm"
#include "code\controllers\subsystem\air.dm"
#include "code\controllers\subsystem\area_contents.dm"
#include "code\controllers\subsystem\assets.dm"
#include "code\controllers\subsystem\atoms.dm"
#include "code\controllers\subsystem\augury.dm"