Easy multi-z support for alarm console (+spawn changes) (#93198)

## About The Pull Request

Station alarm consoles now register to all Station Z levels+Areas if
they were created on a station Z level (this doesn't include mining, but
station ones will see mining areas), otherwise will be a "local" console
to that Z level only.

Mostly the same applies for Cyborg/AI, but if they were built on a
non-station Z level then they have their Z level added on top of Station
Z levels, so example Nukie saboteur borgs will see alarms they might
need, while a Golem/Oldstation AI will also be able to see their own
alarms.

Because of how alarms and spawning in worked, Cyborgs/AIs had CentCom's
Z level as something it would detect, so we simply create the mob at the
spawn location rather than on the title screen during Initialization,
fixing that issue as well.

## Why It's Good For The Game

Adds easy multi-z support for station alarms and removes the previous
bandaid of having to deconstruct your board and multitool it or map in
every single station alarm console to allow station alerts (which is
quite silly) which was done in
https://github.com/tgstation/tgstation/pull/88343.

Closes https://github.com/tgstation/tgstation/issues/61592
Closes https://github.com/tgstation/tgstation/issues/83042
Closes https://github.com/tgstation/tgstation/issues/69314

I find this solution better since it catches pretty much all use cases
without the need for special subtypes and needing players to actually
know the difference between these things.

## Changelog

🆑
fix: Cyborgs and AIs will now see alerts of the whole station rather
than solely the Z level they were spawned on.
fix: Cyborgs and AIs will no longer get alerts about Thunderdome.
/🆑
This commit is contained in:
John Willard
2025-10-02 18:12:57 +02:00
committed by GitHub
parent c4efcc91de
commit fd752f043d
15 changed files with 71 additions and 95 deletions
@@ -7090,7 +7090,7 @@
/turf/open/floor/wood,
/area/station/service/theater)
"bKo" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/machinery/firealarm/directional/south,
@@ -8774,7 +8774,7 @@
/area/station/ai_monitored/turret_protected/ai_upload)
"cfx" = (
/obj/structure/cable,
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/effect/turf_decal/bot,
@@ -38738,7 +38738,7 @@
/turf/open/floor/iron,
/area/station/hallway/primary/fore)
"jAY" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/effect/turf_decal/bot/left,
@@ -45717,7 +45717,7 @@
},
/obj/machinery/light/directional/south,
/obj/effect/turf_decal/bot,
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/turf/open/floor/iron/dark/textured_large,
@@ -57143,7 +57143,7 @@
/turf/open/floor/iron/dark,
/area/station/security/detectives_office)
"oiO" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/obj/effect/turf_decal/bot,
@@ -89693,7 +89693,7 @@
/area/station/commons/dorms)
"wgF" = (
/obj/structure/cable,
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/machinery/status_display/ai/directional/south,
@@ -27114,7 +27114,7 @@
/turf/open/floor/iron/cafeteria,
/area/station/security/prison/work)
"hFJ" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/tile/neutral/fourcorners,
/obj/machinery/computer/security/telescreen/engine/directional/north,
/turf/open/floor/iron/dark,
@@ -59111,7 +59111,7 @@
/area/station/science/explab)
"qFu" = (
/obj/machinery/airalarm/directional/west,
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/effect/turf_decal/tile/neutral/fourcorners,
@@ -60495,7 +60495,7 @@
/turf/open/floor/iron,
/area/station/engineering/lobby)
"qXF" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/tile/yellow/half/contrasted,
/turf/open/floor/iron,
/area/station/command/bridge)
@@ -72157,7 +72157,7 @@
/turf/open/floor/iron/showroomfloor,
/area/station/security/processing)
"unG" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/effect/turf_decal/tile/neutral/fourcorners,
@@ -75453,7 +75453,7 @@
/turf/open/floor/iron/dark,
/area/station/service/chapel)
"vnt" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/obj/machinery/button/door/directional/south{
+6 -6
View File
@@ -14104,7 +14104,7 @@
/turf/open/floor/engine,
/area/station/science/xenobiology)
"eSy" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/structure/cable,
@@ -19444,7 +19444,7 @@
/obj/machinery/camera/directional/north{
c_tag = "Engineering - Power Monitoring"
},
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/tile/neutral/fourcorners,
/turf/open/floor/iron/dark,
/area/station/engineering/main)
@@ -28123,7 +28123,7 @@
/turf/open/floor/iron,
/area/station/hallway/primary/port)
"jGN" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/machinery/light/directional/south,
@@ -37612,7 +37612,7 @@
/turf/open/floor/iron,
/area/station/ai_monitored/command/storage/eva)
"mXj" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/tile/brown/half/contrasted{
dir = 1
},
@@ -45635,7 +45635,7 @@
/turf/open/floor/iron,
/area/station/security/warden)
"pMH" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/structure/plaque/static_plaque/atmos{
@@ -48330,7 +48330,7 @@
/area/station/security/detectives_office)
"qJd" = (
/obj/machinery/status_display/ai/directional/north,
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/tile/neutral/fourcorners,
/turf/open/floor/iron/dark,
/area/station/command/heads_quarters/ce)
+10 -10
View File
@@ -22802,7 +22802,7 @@
/turf/open/floor/iron/white,
/area/station/science/lower)
"dsQ" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/effect/turf_decal/bot,
@@ -54296,7 +54296,7 @@
/area/station/security/prison)
"hZu" = (
/obj/effect/turf_decal/bot,
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/turf/open/floor/iron/dark/textured_large,
@@ -59766,7 +59766,7 @@
/turf/open/floor/plating,
/area/station/maintenance/port/fore)
"iPp" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/obj/effect/turf_decal/tile/yellow/half/contrasted{
@@ -76959,7 +76959,7 @@
/turf/open/floor/iron/dark/herringbone,
/area/station/commons/dorms)
"lrn" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/effect/turf_decal/bot,
@@ -82435,7 +82435,7 @@
/turf/open/floor/iron/dark/textured,
/area/station/command)
"mkJ" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/machinery/button/door/directional/north{
pixel_x = 6;
id = "atmoslock";
@@ -92499,7 +92499,7 @@
/turf/open/floor/iron/dark,
/area/station/service/hydroponics)
"nNh" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/siding/thinplating_new/dark{
dir = 5
},
@@ -96727,7 +96727,7 @@
/turf/open/floor/iron/dark,
/area/station/maintenance/starboard/central)
"ork" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/effect/turf_decal/bot{
@@ -100471,7 +100471,7 @@
/turf/open/floor/iron/dark/textured,
/area/station/service/library/lounge)
"oSD" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/structure/sign/poster/official/random/directional/south,
@@ -114507,7 +114507,7 @@
/obj/effect/turf_decal/trimline/dark/corner{
dir = 8
},
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/turf/open/floor/iron/dark/textured_corner{
dir = 8
},
@@ -142730,7 +142730,7 @@
/area/station/science/ordnance/storage)
"vgI" = (
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible,
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 4
},
/obj/effect/turf_decal/bot,
+5 -5
View File
@@ -7805,7 +7805,7 @@
/turf/open/floor/iron,
/area/station/cargo/office)
"bHP" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/obj/effect/turf_decal/trimline/yellow/filled/line{
@@ -48740,7 +48740,7 @@
/turf/open/floor/iron,
/area/station/security/courtroom)
"qcw" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/item/radio/intercom/directional/south,
@@ -60686,7 +60686,7 @@
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
"ukE" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/structure/window/reinforced/spawner/directional/south,
@@ -68218,7 +68218,7 @@
/turf/open/floor/iron/checker,
/area/station/commons/lounge)
"wLx" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/turf/open/floor/iron/dark,
@@ -68332,7 +68332,7 @@
/turf/open/floor/iron/white/side,
/area/station/science/lobby)
"wNY" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 8
},
/obj/machinery/requests_console/directional/east{
+3 -3
View File
@@ -2207,7 +2207,7 @@
/turf/open/floor/catwalk_floor/iron_dark,
/area/station/ai_monitored/turret_protected/aisat/uppersouth)
"aKj" = (
/obj/machinery/computer/station_alert/station_only{
/obj/machinery/computer/station_alert{
dir = 1
},
/obj/effect/turf_decal/trimline/yellow/filled/line,
@@ -35349,7 +35349,7 @@
/turf/open/floor/iron/dark,
/area/station/security/warden)
"mio" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/turf/open/floor/iron,
/area/station/engineering/atmos/upper)
"mit" = (
@@ -46231,7 +46231,7 @@
/turf/open/misc/asteroid,
/area/station/hallway/primary/starboard)
"pVC" = (
/obj/machinery/computer/station_alert/station_only,
/obj/machinery/computer/station_alert,
/obj/effect/turf_decal/tile/yellow/half/contrasted,
/obj/machinery/light/directional/north,
/turf/open/floor/iron/dark,
+6 -1
View File
@@ -734,7 +734,12 @@ SUBSYSTEM_DEF(job)
/atom/proc/JoinPlayerHere(mob/joining_mob, buckle)
// By default, just place the mob on the same turf as the marker or whatever.
joining_mob.forceMove(get_turf(src))
// Set joining_mob as the new mob so subtypes can use it as a proper mob.
if(ispath(joining_mob))
joining_mob = new joining_mob(get_turf(src))
else
joining_mob.forceMove(get_turf(src))
return joining_mob
/obj/structure/chair/JoinPlayerHere(mob/joining_mob, buckle)
. = ..()
+9 -34
View File
@@ -8,19 +8,18 @@
/// Station alert datum for showing alerts UI
var/datum/station_alert/alert_control
/obj/machinery/computer/station_alert/examine(mob/user)
. = ..()
var/obj/item/circuitboard/computer/station_alert/my_circuit = circuit
. += span_info("The console is set to [my_circuit.station_only ? "track all station and mining alarms" : "track alarms on the same z-level"].")
/obj/machinery/computer/station_alert/Initialize(mapload)
link_alerts()
if(is_station_level(z))
var/static/list/alert_areas
if(isnull(alert_areas))
alert_areas = (GLOB.the_station_areas + typesof(/area/mine))
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), SSmapping.levels_by_trait(ZTRAIT_STATION), alert_areas)
else
name = "local alert console"
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), list(z), title = name)
RegisterSignals(alert_control.listener, list(COMSIG_ALARM_LISTENER_TRIGGERED, COMSIG_ALARM_LISTENER_CLEARED), PROC_REF(update_alarm_display))
return ..()
/obj/machinery/computer/station_alert/on_construction(mob/user)
. = ..()
link_alerts()
/obj/machinery/computer/station_alert/Destroy()
QDEL_NULL(alert_control)
return ..()
@@ -42,26 +41,6 @@
if(length(alert_control.listener.alarms))
. += "alert:2"
/**
* Clears out any active alert_control listeners, then sets up a new one based on the circuit settings
*/
/obj/machinery/computer/station_alert/proc/link_alerts()
//Start from scratch, clear out the existing alert listeners
QDEL_NULL(alert_control)
//Then we check the circuit to determine if it should show alarms from Station & Mining areas,
//or Local (z-level) areas
var/obj/item/circuitboard/computer/station_alert/my_circuit = circuit
if(my_circuit.station_only)
name = "station alert console"
var/list/alert_areas
alert_areas = (GLOB.the_station_areas + typesof(/area/mine))
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), listener_areas = alert_areas, title = name)
else
name = "local alert console"
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), list(z), title = name)
RegisterSignals(alert_control.listener, list(COMSIG_ALARM_LISTENER_TRIGGERED, COMSIG_ALARM_LISTENER_CLEARED), PROC_REF(update_alarm_display))
/**
* Signal handler for calling an icon update in case an alarm is added or cleared
*
@@ -71,7 +50,3 @@
/obj/machinery/computer/station_alert/proc/update_alarm_display(datum/source)
SIGNAL_HANDLER
update_icon()
// Subtype which only checks station areas and the mining station
/obj/machinery/computer/station_alert/station_only
circuit = /obj/item/circuitboard/computer/station_alert/station_only
+1 -3
View File
@@ -563,15 +563,13 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
icon_state = "hangover_spawn_closet"
/obj/effect/landmark/start/hangover/closet/JoinPlayerHere(mob/joining_mob, buckle)
. = ..()
for(var/obj/structure/closet/closet in get_turf(src))
if(closet.opened)
continue
joining_mob.forceMove(closet)
make_hungover(joining_mob)
return
return ..() //Call parent as fallback
//Landmark that creates destinations for the navigate verb to path to
/obj/effect/landmark/navigate_destination
name = "navigate verb destination"
@@ -247,20 +247,6 @@
name = "Station Alerts"
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/computer/station_alert
var/station_only = FALSE
/obj/item/circuitboard/computer/station_alert/station_only
station_only = TRUE
/obj/item/circuitboard/computer/station_alert/examine(mob/user)
. = ..()
. += span_info("The board is configured to [station_only ? "track all station and mining alarms" : "track alarms on the same z-level"].")
. += span_notice("The board mode can be changed with a [EXAMINE_HINT("multitool")].")
/obj/item/circuitboard/computer/station_alert/multitool_act(mob/living/user)
station_only = !station_only
balloon_alert(user, "tracking set to [station_only ? "station" : "z-level"]")
return TRUE
/obj/item/circuitboard/computer/turbine_computer
name = "Turbine Computer"
+1 -2
View File
@@ -510,8 +510,7 @@
// This is unfortunately necessary because of snowflake AI init code. To be refactored.
spawn_instance = new spawn_type(get_turf(spawn_point), null, player_client.mob)
else
spawn_instance = new spawn_type(player_client.mob.loc)
spawn_point.JoinPlayerHere(spawn_instance, TRUE)
spawn_instance = spawn_point.JoinPlayerHere(spawn_type, TRUE)
spawn_instance.apply_prefs_job(player_client, src)
if(!player_client)
qdel(spawn_instance)
@@ -30,7 +30,7 @@
return FALSE
/// Get a list of all z which have the specified trait
/datum/controller/subsystem/mapping/proc/levels_by_trait(trait)
/datum/controller/subsystem/mapping/proc/levels_by_trait(trait) as /list
return z_trait_levels[trait] || list()
/// Get a list of all z which have any of the specified traits
+10 -4
View File
@@ -81,16 +81,22 @@
//They aren't given a c_tag so they don't show up in camera consoles
builtInCamera = new(src)
var/static/list/alert_areas
if(isnull(alert_areas))
alert_areas = (GLOB.the_station_areas + typesof(/area/mine))
if(is_station_level(z))
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), SSmapping.levels_by_trait(ZTRAIT_STATION), alert_areas, camera_view = TRUE)
else
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), (SSmapping.levels_by_trait(ZTRAIT_STATION) + z), alert_areas, camera_view = TRUE)
RegisterSignal(alert_control.listener, COMSIG_ALARM_LISTENER_TRIGGERED, PROC_REF(alarm_triggered))
RegisterSignal(alert_control.listener, COMSIG_ALARM_LISTENER_CLEARED, PROC_REF(alarm_cleared))
ai_tracking_tool = new(src)
RegisterSignal(ai_tracking_tool, COMSIG_TRACKABLE_TRACKING_TARGET, PROC_REF(on_track_target))
RegisterSignal(ai_tracking_tool, COMSIG_TRACKABLE_GLIDE_CHANGED, PROC_REF(tracked_glidesize_changed))
add_traits(list(TRAIT_PULL_BLOCKED, TRAIT_AI_ACCESS, TRAIT_HANDS_BLOCKED, TRAIT_CAN_GET_AI_TRACKING_MESSAGE, TRAIT_LOUD_BINARY), INNATE_TRAIT)
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), list(z), camera_view = TRUE)
RegisterSignal(alert_control.listener, COMSIG_ALARM_LISTENER_TRIGGERED, PROC_REF(alarm_triggered))
RegisterSignal(alert_control.listener, COMSIG_ALARM_LISTENER_CLEARED, PROC_REF(alarm_cleared))
//Heads up to other binary chat listeners that a new AI is online and listening to Binary.
if(announce_init_to_others && !is_centcom_level(z)) //Skip new syndicate AIs and also new AIs on centcom Z
for(var/mob/McMobby as anything in GLOB.player_list)
@@ -84,7 +84,13 @@
log_silicon("New cyborg [key_name(src)] created with [connected_ai ? "master AI: [key_name(connected_ai)]" : "no master AI"]")
log_current_laws()
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), list(z))
var/static/list/alert_areas
if(isnull(alert_areas))
alert_areas = (GLOB.the_station_areas + typesof(/area/mine))
if(is_station_level(z))
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), SSmapping.levels_by_trait(ZTRAIT_STATION), alert_areas)
else
alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), (SSmapping.levels_by_trait(ZTRAIT_STATION) + z), alert_areas)
RegisterSignal(alert_control.listener, COMSIG_ALARM_LISTENER_TRIGGERED, PROC_REF(alarm_triggered))
RegisterSignal(alert_control.listener, COMSIG_ALARM_LISTENER_CLEARED, PROC_REF(alarm_cleared))
alert_control.listener.RegisterSignal(src, COMSIG_LIVING_DEATH, TYPE_PROC_REF(/datum/alarm_listener, prevent_alarm_changes))
@@ -0,0 +1 @@
/obj/machinery/computer/station_alert/station_only : /obj/machinery/computer/station_alert{@OLD}