diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index cc1b474c49..4304af77f3 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -184,7 +184,7 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define BODY_ZONE_PRECISE_R_FOOT "r_foot"
//We will round to this value in damage calculations.
-#define DAMAGE_PRECISION 0.1
+#define DAMAGE_PRECISION 0.01
//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25)
#define TOTAL_MASS_TINY_ITEM 1.25
@@ -202,4 +202,4 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
-#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
\ No newline at end of file
+#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm
index 3034876e36..6b70eb1e0f 100644
--- a/code/__DEFINES/configuration.dm
+++ b/code/__DEFINES/configuration.dm
@@ -1,6 +1,7 @@
//config files
#define CONFIG_GET(X) global.config.Get(/datum/config_entry/##X)
#define CONFIG_SET(X, Y) global.config.Set(/datum/config_entry/##X, ##Y)
+#define CONFIG_GET_ENTRY(X) global.config.GetEntryDatum(/datum/config_entry/##X)
#define CONFIG_MAPS_FILE "maps.txt"
diff --git a/code/__HELPERS/areas.dm b/code/__HELPERS/areas.dm
index 4b52187e13..3b1496bae0 100644
--- a/code/__HELPERS/areas.dm
+++ b/code/__HELPERS/areas.dm
@@ -1,5 +1,81 @@
#define BP_MAX_ROOM_SIZE 300
+//Repopulates sortedAreas list
+/proc/repopulate_sorted_areas()
+ GLOB.sortedAreas = list()
+
+ for(var/area/A in world)
+ GLOB.sortedAreas.Add(A)
+
+ sortTim(GLOB.sortedAreas, /proc/cmp_name_asc)
+
+/area/proc/addSorted()
+ 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
+
// Gets an atmos isolated contained space
// Returns an associative list of turf|dirs pairs
// The dirs are connected turfs in the same space
@@ -103,4 +179,60 @@
to_chat(creator, "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
+
+/**
+ * Returns the base area the target is located in if there is one.
+ * Alternatively, returns the area as is.
+ */
+/proc/get_base_area(atom/target)
+ var/area/A = get_area(target)
+ if(A?.base_area)
+ return A.base_area
+ return A
+
+/**
+ * Returns either null, or a list containing every sub area associated with our base area.
+ * If include_base is TRUE, the base area will also be added to the return list.
+ */
+/proc/get_sub_areas(atom/target, include_base = TRUE)
+ var/area/A = get_area(target)
+ if(!A)
+ return
+ . = list()
+ if(A.base_area)
+ A = A.base_area
+ if(include_base)
+ . += A
+ if(A.sub_areas)
+ . += A.sub_areas
+
+/**
+ * Proc used for purposes similar to get_areas_turfs(), but aimed to include associated areas.
+ * Only accepts area instances and paths for the first arg, no text strings.
+ * Returns a list of all turfs found in the sub areas (including the base's if include_base is TRUE)
+ * and located in a z level matching target_z, or anywhere if target_z is 0
+ */
+
+/proc/get_sub_areas_turfs(area/A, target_z = 0, include_base = TRUE)
+ var/list/contents = get_sub_areas_contents(A, include_base)
+ . = list()
+ for(var/turf/T in contents)
+ if(target_z == 0 || target_z == T.z)
+ . += T
+/**
+ * Simple proc that returns a sum of all contents from every sub area,
+ * Think of the above but for all contents, not just turfs, and without target z.
+ */
+
+/proc/get_sub_areas_contents(area/A, include_base = TRUE)
+ if(ispath(A))
+ A = GLOB.areas_by_type[A]
+ if(!A)
+ return
+ if(A.base_area)
+ A = A.base_area
+ . = list(A.contents)
+ for(var/i in A.sub_areas)
+ . += A.sub_areas[i].contents
+
#undef BP_MAX_ROOM_SIZE
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index ca4b913369..59bf210853 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -8,8 +8,8 @@
#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL))
#define CULT_POLL_WAIT 2400
-/proc/get_area_name(atom/X, format_text = FALSE)
- var/area/A = isarea(X) ? X : get_area(X)
+/proc/get_area_name(atom/X, format_text = FALSE, get_base_area = FALSE)
+ var/area/A = get_base_area ? get_base_area(X) : get_area(X)
if(!A)
return null
return format_text ? format_text(A.name) : A.name
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index e916b2532a..8b86ce691d 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -566,82 +566,6 @@ Turf and target are separate in case you want to teleport some distance from a t
else
return 0
-//Repopulates sortedAreas list
-/proc/repopulate_sorted_areas()
- GLOB.sortedAreas = list()
-
- for(var/area/A in world)
- GLOB.sortedAreas.Add(A)
-
- sortTim(GLOB.sortedAreas, /proc/cmp_name_asc)
-
-/area/proc/addSorted()
- 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)
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 4ce0ccf361..ad1f869057 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -193,6 +193,13 @@
stat("[name]:", statclick)
/datum/controller/configuration/proc/Get(entry_type)
+ var/datum/config_entry/E = GetEntryDatum(entry_type)
+ if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
+ log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
+ return
+ return E.config_entry_value
+
+/datum/controller/configuration/proc/GetEntryDatum(entry_type)
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
@@ -200,10 +207,7 @@
E = entries_by_type[entry_type]
if(!E)
CRASH("Missing config entry for [entry_type]!")
- if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
- log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
- return
- return E.config_entry_value
+ return E
/datum/controller/configuration/proc/Set(entry_type, new_val)
var/datum/config_entry/E = entry_type
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 6efc9eab12..a3bc47ac91 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -249,6 +249,18 @@
/datum/config_entry/number/movedelay/walk_delay
+/datum/config_entry/number/movedelay/sprint_speed_increase
+ config_entry_value = 1
+
+/datum/config_entry/number/movedelay/sprint_buffer_max
+ config_entry_value = 42
+
+/datum/config_entry/number/movedelay/sprint_stamina_cost
+ config_entry_value = 0.7
+
+/datum/config_entry/number/movedelay/sprint_buffer_regen_per_ds
+ config_entry_value = 0.3
+
/////////////////////////////////////////////////Outdated move delay
/datum/config_entry/number/outdated_movedelay
deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index 2f0405af2e..63e4f4f092 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -229,7 +229,7 @@
UnregisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER)
mastermind = null
if(cleanup)
- var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
+ var/obj/effect/vr_clean_master/cleanbot = locate() in get_base_area(M)
if(cleanbot)
LAZYOR(cleanbot.corpse_party, M)
qdel(src)
diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm
index 230a074bb1..71a5bcb99e 100644
--- a/code/datums/diseases/wizarditis.dm
+++ b/code/datums/diseases/wizarditis.dm
@@ -94,7 +94,7 @@ STI KALY - blind
var/area/thearea = pick(theareas)
var/list/L = list()
- for(var/turf/T in get_area_turfs(thearea.type))
+ for(var/turf/T in thearea)
if(T.z != affected_mob.z)
continue
if(T.name == "space")
diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm
index a3b666dcc6..01b9facbb8 100644
--- a/code/datums/weather/weather.dm
+++ b/code/datums/weather/weather.dm
@@ -50,7 +50,10 @@
stage = STARTUP_STAGE
var/list/affectareas = list()
for(var/V in get_areas(area_type))
- affectareas += V
+ var/area/A = V
+ affectareas |= A
+ if(A.sub_areas)
+ affectareas |= A.sub_areas
for(var/V in protected_areas)
affectareas -= get_areas(V)
for(var/V in affectareas)
diff --git a/code/datums/wires/airalarm.dm b/code/datums/wires/airalarm.dm
index 6eb4dc04db..0c4715e27e 100644
--- a/code/datums/wires/airalarm.dm
+++ b/code/datums/wires/airalarm.dm
@@ -46,7 +46,7 @@
A.mode = 1 // AALARM_MODE_SCRUB
A.apply_mode()
if(WIRE_ALARM) // Clear alarms.
- var/area/AA = get_area(A)
+ var/area/AA = get_base_area(A)
if(AA.atmosalert(0, holder))
A.post_alert(0)
A.update_icon()
@@ -68,7 +68,7 @@
A.mode = 3 // AALARM_MODE_PANIC
A.apply_mode()
if(WIRE_ALARM) // Post alarm.
- var/area/AA = get_area(A)
+ var/area/AA = get_base_area(A)
if(AA.atmosalert(2, holder))
A.post_alert(2)
A.update_icon()
\ No newline at end of file
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 2d256aad27..99b8165306 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -63,6 +63,15 @@
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with
+/**
+ * These two vars allow for multiple unique areas to be linked to a master area
+ * and share some functionalities such as APC powernet nodes, fire alarms etc, without sacrificing
+ * their own flags, statuses, variables and more snowflakes.
+ * Friendly reminder: no map edited areas.
+ */
+ var/list/area/sub_areas //list of typepaths of the areas you wish to link here, will be replaced with a list of references on mapload.
+ var/area/base_area //The area we wish to use in place of src for certain actions such as APC area linking.
+
var/nightshift_public_area = NIGHTSHIFT_AREA_NONE //considered a public area for nightshift
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
@@ -123,10 +132,35 @@ GLOBAL_LIST_EMPTY(teleportlocs)
reg_in_areas_in_z()
+ //so far I'm only implementing it on mapped unique areas, it's easier this way.
+ if(unique && sub_areas)
+ if(type in sub_areas)
+ WARNING("\"[src]\" typepath found inside its own sub-areas list, please make sure it doesn't share its parent type initial sub-areas value.")
+ sub_areas = null
+ else
+ var/paths = sub_areas.Copy()
+ sub_areas = null
+ for(var/type in paths)
+ var/area/A = GLOB.areas_by_type[type]
+ if(!A) //By chance an area not loaded in the current world, no warning report.
+ continue
+ if(A == src)
+ WARNING("\"[src]\" area a attempted to link with itself.")
+ continue
+ if(A.base_area)
+ WARNING("[src] attempted to link with [A] while the latter is already linked to another area ([A.base_area]).")
+ continue
+ LAZYADD(sub_areas, A)
+ A.base_area = src
+ else if(LAZYLEN(sub_areas))
+ WARNING("sub-areas are currently not supported for non-unique areas such as [src].")
+ sub_areas = null
+
return INITIALIZE_HINT_LATELOAD
/area/LateInitialize()
- power_change() // all machines set to current power level, also updates icon
+ if(!base_area) //we don't want to run it twice.
+ power_change() // all machines set to current power level, also updates icon
/area/proc/reg_in_areas_in_z()
if(contents.len)
@@ -149,6 +183,19 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/Destroy()
if(GLOB.areas_by_type[type] == src)
GLOB.areas_by_type[type] = null
+ if(base_area)
+ LAZYREMOVE(base_area, src)
+ base_area = null
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.base_area = null
+ sub_areas -= A
+ if(A.requires_power)
+ A.power_light = FALSE
+ A.power_equip = FALSE
+ A.power_environ = FALSE
+ INVOKE_ASYNC(A, .proc/power_change)
STOP_PROCESSING(SSobj, src)
return ..()
@@ -214,9 +261,12 @@ GLOBAL_LIST_EMPTY(teleportlocs)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Atmosphere", src, source)
- src.atmosalm = danger_level
- return 1
- return 0
+ atmosalm = danger_level
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.atmosalm = danger_level
+ return TRUE
+ return FALSE
/area/proc/ModifyFiredoors(opening)
if(firedoors)
@@ -241,11 +291,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return
if (!fire)
- set_fire_alarm_effect()
+ set_fire_alarm_effects(TRUE)
ModifyFiredoors(FALSE)
- for(var/item in firealarms)
- var/obj/machinery/firealarm/F = item
- F.update_icon()
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
@@ -264,11 +311,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/firereset(obj/source)
if (fire)
- unset_fire_alarm_effects()
+ set_fire_alarm_effects(FALSE)
ModifyFiredoors(TRUE)
- for(var/item in firealarms)
- var/obj/machinery/firealarm/F = item
- F.update_icon()
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
@@ -300,9 +344,9 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return
//Trigger alarm effect
- set_fire_alarm_effect()
+ set_fire_alarm_effects(TRUE)
//Lockdown airlocks
- for(var/obj/machinery/door/DOOR in src)
+ for(var/obj/machinery/door/DOOR in get_sub_areas_contents(src))
close_and_lock_door(DOOR)
for (var/i in GLOB.silicon_mobs)
@@ -311,23 +355,20 @@ GLOBAL_LIST_EMPTY(teleportlocs)
//Cancel silicon alert after 1 minute
addtimer(CALLBACK(SILICON, /mob/living/silicon.proc/cancelAlarm,"Burglar",src,trigger), 600)
-/area/proc/set_fire_alarm_effect()
- fire = TRUE
+/area/proc/set_fire_alarm_effects(boolean)
+ fire = boolean
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.fire = boolean
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
for(var/alarm in firealarms)
var/obj/machinery/firealarm/F = alarm
F.update_fire_light(fire)
- for(var/obj/machinery/light/L in src)
+ F.update_icon()
+ for(var/obj/machinery/light/L in get_sub_areas_contents(src))
L.update()
-/area/proc/unset_fire_alarm_effects()
- fire = FALSE
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- for(var/alarm in firealarms)
- var/obj/machinery/firealarm/F = alarm
- F.update_fire_light(fire)
- for(var/obj/machinery/light/L in src)
- L.update()
+/area/proc/updateicon()
/**
* Update the icon state of the area
*
@@ -380,26 +421,35 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/power_change()
for(var/obj/machinery/M in src) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.power_light = power_light
+ A.power_equip = power_equip
+ A.power_environ = power_environ
+ INVOKE_ASYNC(A, .proc/power_change)
update_icon()
/area/proc/usage(chan)
- var/used = 0
switch(chan)
if(LIGHT)
- used += used_light
+ . += used_light
if(EQUIP)
- used += used_equip
+ . += used_equip
if(ENVIRON)
- used += used_environ
+ . += used_environ
if(TOTAL)
- used += used_light + used_equip + used_environ
+ . += used_light + used_equip + used_environ
if(STATIC_EQUIP)
- used += static_equip
+ . += static_equip
if(STATIC_LIGHT)
- used += static_light
+ . += static_light
if(STATIC_ENVIRON)
- used += static_environ
- return used
+ . += static_environ
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ . += A.usage(chan)
/area/proc/addStaticPower(value, powerchannel)
switch(powerchannel)
@@ -414,6 +464,10 @@ GLOBAL_LIST_EMPTY(teleportlocs)
used_equip = 0
used_light = 0
used_environ = 0
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.clear_usage()
/area/proc/use_power(amount, chan)
diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm
index d010da465e..b296ba7273 100644
--- a/code/game/gamemodes/events.dm
+++ b/code/game/gamemodes/events.dm
@@ -12,7 +12,7 @@
var/list/skipped_areas = list(/area/engine/engineering, /area/engine/supermatter, /area/engine/atmospherics_engine, /area/ai_monitored/turret_protected/ai)
for(var/area/A in world)
- if( !A.requires_power || A.always_unpowered )
+ if( !A.requires_power || A.always_unpowered || A.base_area)
continue
var/skip = 0
@@ -61,8 +61,9 @@
S.output_attempt = 1
S.update_icon()
S.power_change()
+
for(var/area/A in world)
- if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A, /area/arrival))
+ if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A, /area/arrival) && !A.always_unpowered && !A.base_area)
A.power_light = TRUE
A.power_equip = TRUE
A.power_environ = TRUE
diff --git a/code/game/gamemodes/gangs/dominator.dm b/code/game/gamemodes/gangs/dominator.dm
index 8c1272dcbd..db060a6539 100644
--- a/code/game/gamemodes/gangs/dominator.dm
+++ b/code/game/gamemodes/gangs/dominator.dm
@@ -101,7 +101,7 @@
playsound(loc, 'sound/items/timer.ogg', 10, 0)
if(!warned && (time_remaining < 180))
warned = TRUE
- var/area/domloc = get_area(loc)
+ var/area/domloc = get_base_area(loc)
gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!")
for(var/G in GLOB.gangs)
var/datum/team/gang/tempgang = G
@@ -179,7 +179,7 @@
if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
return 0
- var/area/A = get_area(loc)
+ var/area/A = get_base_area(loc)
var/locname = A.map_name
gang = tempgang
diff --git a/code/game/gamemodes/gangs/gang_decals.dm b/code/game/gamemodes/gangs/gang_decals.dm
index 6e5cb58891..7aaed769d9 100644
--- a/code/game/gamemodes/gangs/gang_decals.dm
+++ b/code/game/gamemodes/gangs/gang_decals.dm
@@ -19,7 +19,7 @@
return INITIALIZE_HINT_QDEL
gang = G
var/newcolor = G.color
- var/area/territory = get_area(src)
+ var/area/territory = get_base_area(src)
icon_state = G.name
G.new_territories |= list(territory.type = territory.name)
//If this isn't tagged by a specific gangster there's no bonus income.
@@ -27,7 +27,7 @@
/obj/effect/decal/cleanable/crayon/gang/Destroy()
if(gang)
- var/area/territory = get_area(src)
+ var/area/territory = get_base_area(src)
gang.territories -= territory.type
gang.new_territories -= territory.type
gang.lost_territories |= list(territory.type = territory.name)
diff --git a/code/game/gamemodes/gangs/gang_items.dm b/code/game/gamemodes/gangs/gang_items.dm
index 2e9ca4dcc0..688af3beea 100644
--- a/code/game/gamemodes/gangs/gang_items.dm
+++ b/code/game/gamemodes/gangs/gang_items.dm
@@ -401,7 +401,7 @@ datum/gang_item/equipment/gangsheild
return "This device requires a 5x5 area clear of walls to FUNCTION. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)"
/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
- var/area/userarea = get_area(user)
+ var/area/userarea = get_base_area(user)
if(!(userarea.type in gang.territories|gang.new_territories))
to_chat(user,"The dominator can be spawned only on territory controlled by your gang!")
return FALSE
diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm
index fc6cac785a..991577e123 100644
--- a/code/game/machinery/cell_charger.dm
+++ b/code/game/machinery/cell_charger.dm
@@ -46,7 +46,7 @@
var/area/a = loc.loc // Gets our locations location, like a dream within a dream
if(!isarea(a))
return
- if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power!
+ if(!a.powered(EQUIP)) // There's no APC in this area, don't try to cheat power!
to_chat(user, "[src] blinks red as you try to insert the cell!")
return
if(!user.transferItemToLoc(W,src))
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index f5bf8c8a1b..cb133978b2 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -44,7 +44,7 @@
/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
remove_from_areas()
- affecting_areas = get_adjacent_open_areas(src) | get_area(src)
+ affecting_areas = get_adjacent_open_areas(src) | get_base_area(src)
for(var/I in affecting_areas)
var/area/A = I
LAZYADD(A.firedoors, src)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 1ef3fc0a74..0e2363cb6a 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -44,7 +44,7 @@
pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
update_icon()
- myarea = get_area(src)
+ myarea = get_base_area(src)
LAZYADD(myarea.firealarms, src)
/obj/machinery/firealarm/Destroy()
@@ -124,7 +124,7 @@
if(!is_operational() || (last_alarm+FIREALARM_COOLDOWN > world.time))
return
last_alarm = world.time
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
A.firealert(src)
playsound(loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
if(user)
@@ -133,7 +133,7 @@
/obj/machinery/firealarm/proc/reset(mob/user)
if(!is_operational())
return
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
A.firereset(src)
if(user)
log_game("[user] reset a fire alarm at [COORD(src)]")
@@ -142,7 +142,7 @@
if(buildstage != 2)
return ..()
add_fingerprint(user)
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if(A.fire)
reset(user)
else
@@ -322,7 +322,7 @@
/obj/machinery/firealarm/partyalarm/reset()
if (stat & (NOPOWER|BROKEN))
return
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if (!A || !A.party)
return
A.party = FALSE
@@ -331,7 +331,7 @@
/obj/machinery/firealarm/partyalarm/alarm()
if (stat & (NOPOWER|BROKEN))
return
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if (!A || A.party || A.name == "Space")
return
A.party = TRUE
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 2211397f3e..f777c893f8 100755
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -70,7 +70,7 @@
//Checks to make sure he's not in space doing it, and that the area got proper power.
var/area/a = get_area(src)
- if(!isarea(a) || a.power_equip == 0)
+ if(!a || !a.powered(EQUIP))
to_chat(user, "[src] blinks red as you try to insert [G].")
return 1
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index fdb620cc67..5a907804ce 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -283,7 +283,7 @@
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_charge()
if(equip_ready) //disabled
return
- var/area/A = get_area(chassis)
+ var/area/A = get_base_area(chassis)
var/pow_chan = get_power_channel(A)
if(pow_chan)
return 1000 //making magic
@@ -328,7 +328,7 @@
occupant_message("No powercell detected.")
return
if(cur_charge < chassis.cell.maxcharge)
- var/area/A = get_area(chassis)
+ var/area/A = get_base_area(chassis)
if(A)
var/pow_chan
for(var/c in list(EQUIP,ENVIRON,LIGHT))
diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm
index aabf9acafa..38a1a6089e 100644
--- a/code/game/objects/effects/effect_system/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/effects_foam.dm
@@ -126,7 +126,7 @@
T.PlaceOnTop(/turf/open/floor/plating/foam, flags = CHANGETURF_INHERIT_AIR)
for(var/direction in GLOB.cardinals)
var/turf/cardinal_turf = get_step(T, direction)
- if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf!
+ if(get_base_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf!
new/obj/structure/foamedmetal(T)
break
flick("[icon_state]-disolve", src)
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 8f09827bd2..3d63f4a553 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -456,7 +456,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
if(!SSmapping.station_room_templates[t])
log_world("Station room spawner placed at ([T.x], [T.y], [T.z]) has invalid ruin name of \"[t]\" in its list")
templates -= t
- template_name = pickweight(templates)
+ template_name = pickweightAllowZero(templates)
if(!template_name)
GLOB.stationroom_landmarks -= src
qdel(src)
@@ -485,4 +485,4 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
// Landmark for the mining station
/obj/effect/landmark/stationroom/lavaland/station
templates = list("Public Mining Base" = 3)
- icon = 'icons/rooms/Lavaland/Mining.dmi'
\ No newline at end of file
+ icon = 'icons/rooms/Lavaland/Mining.dmi'
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 143354b496..cb77a58769 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -427,7 +427,7 @@
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
// Check area validity.
// Reject space, player-created areas, and non-station z-levels.
- var/area/A = get_area(target)
+ var/area/A = get_base_area(target)
if(!A || (!is_station_level(A.z)) || !A.valid_territory)
to_chat(user, "[A] is unsuitable for tagging.")
return FALSE
@@ -461,7 +461,7 @@
qdel(old_marking)
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
- var/area/territory = get_area(target)
+ var/area/territory = get_base_area(target)
new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) // Heres the gang tag.
to_chat(user, "You tagged [territory] for your gang!")
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index 5b9a764925..fcd1f80a4f 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -52,7 +52,7 @@
var/area/A = get_area(src)
if(!A.blob_allowed)
return FALSE
- if(!A.power_equip)
+ if(!A.powered(EQUIP))
return FALSE
if(!SSmapping.level_trait(T.z,ZTRAIT_STATION))
return FALSE
diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm
index 053512a256..b72a4b816e 100644
--- a/code/game/objects/structures/barsigns.dm
+++ b/code/game/objects/structures/barsigns.dm
@@ -305,7 +305,7 @@
/datum/barsign/meow_mix
name = "Meow Mix"
- icon = "meow_mix"
+ icon = "Meow Mix"
desc = "No, we don't serve catnip, officer!"
/datum/barsign/hiddensigns
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 8c25a2ca5c..70533df0ad 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -80,7 +80,7 @@
/obj/structure/displaycase/proc/trigger_alarm()
//Activate Anti-theft
if(alert)
- var/area/alarmed = get_area(src)
+ var/area/alarmed = get_base_area(src)
alarmed.burglaralert(src)
playsound(src, 'sound/effects/alert.ogg', 50, 1)
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 7cda24d98b..b62c17832a 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -222,18 +222,23 @@
/obj/effect/vr_clean_master/Initialize()
. = ..()
- vr_area = get_area(src)
- addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
+ vr_area = get_base_area(src)
+ if(!vr_area)
+ return INITIALIZE_HINT_QDEL
+ addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES, TIMER_LOOP)
/obj/effect/vr_clean_master/proc/clean_up()
- if (vr_area)
- for (var/obj/item/ammo_casing/casing in vr_area)
- qdel(casing)
- for(var/obj/effect/decal/cleanable/C in vr_area)
- qdel(C)
- for (var/A in corpse_party)
- var/mob/M = A
- if(M && M.stat == DEAD && get_area(M) == vr_area)
- qdel(M)
- corpse_party -= M
- addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
+ if (!vr_area)
+ qdel(src)
+ return
+ var/list/contents = get_sub_areas_contents(src)
+ for (var/obj/item/ammo_casing/casing in contents)
+ qdel(casing)
+ for(var/obj/effect/decal/cleanable/C in contents)
+ qdel(C)
+ for (var/A in corpse_party)
+ var/mob/M = A
+ if(!QDELETED(M) && (M in contents) && M.stat == DEAD)
+ qdel(M)
+ corpse_party -= M
+ addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index f08112939a..ad2eeb7289 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -541,7 +541,9 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
var/list/areas_all = list()
var/list/areas_with_APC = list()
var/list/areas_with_multiple_APCs = list()
+ var/list/sub_areas_APC = list()
var/list/areas_with_air_alarm = list()
+ var/list/sub_areas_air_alarm = list()
var/list/areas_with_RC = list()
var/list/areas_with_light = list()
var/list/areas_with_LS = list()
@@ -578,6 +580,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(!A)
dat += "Skipped over [APC] in invalid location, [APC.loc]."
continue
+ LAZYSET(sub_areas_APC, A.type, get_sub_areas(A, FALSE))
if(!(A.type in areas_with_APC))
areas_with_APC.Add(A.type)
else if(A.type in areas_all)
@@ -585,10 +588,11 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
CHECK_TICK
for(var/obj/machinery/airalarm/AA in GLOB.machines)
- var/area/A = get_area(AA)
+ var/area/A = get_base_area(AA)
if(!A) //Make sure the target isn't inside an object, which results in runtimes.
dat += "Skipped over [AA] in invalid location, [AA.loc].
"
continue
+ LAZYSET(sub_areas_air_alarm, A.type, get_sub_areas(A, FALSE))
if(!(A.type in areas_with_air_alarm))
areas_with_air_alarm.Add(A.type)
CHECK_TICK
@@ -638,8 +642,8 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
areas_with_camera.Add(A.type)
CHECK_TICK
- var/list/areas_without_APC = areas_all - areas_with_APC
- var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm
+ var/list/areas_without_APC = areas_all - (areas_with_APC + flatten_list(sub_areas_APC))
+ var/list/areas_without_air_alarm = areas_all - (areas_with_air_alarm + flatten_list(sub_areas_air_alarm))
var/list/areas_without_RC = areas_all - areas_with_RC
var/list/areas_without_light = areas_all - areas_with_light
var/list/areas_without_LS = areas_all - areas_with_LS
@@ -656,12 +660,18 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
dat += "