diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index c465c357c148..a1233b0479a4 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -1369,19 +1369,6 @@
},
/turf/open/floor/iron,
/area/centcom/tdome/observation)
-"dY" = (
-/obj/effect/turf_decal/siding/thinplating_new/dark{
- dir = 8
- },
-/obj/structure/table/glass/plasmaglass,
-/obj/item/folder/white,
-/obj/item/pen{
- pixel_x = 6;
- pixel_y = 5
- },
-/obj/machinery/light/cold/directional/west,
-/turf/open/floor/mineral/plastitanium,
-/area/centcom/syndicate_mothership/control)
"dZ" = (
/obj/item/kirbyplants{
icon_state = "plant-21"
@@ -5646,16 +5633,6 @@
},
/turf/open/floor/iron,
/area/centcom/central_command_areas/supplypod/loading/ert)
-"pY" = (
-/obj/item/gun/energy/pulse/carbine,
-/obj/item/flashlight/seclite,
-/obj/structure/table/reinforced,
-/obj/machinery/airalarm/directional/south,
-/obj/effect/turf_decal/stripes/line{
- dir = 1
- },
-/turf/open/floor/iron,
-/area/centcom/central_command_areas/admin/storage)
"pZ" = (
/obj/effect/turf_decal/tile/bar,
/obj/effect/turf_decal/tile/bar{
@@ -5932,6 +5909,10 @@
},
/turf/open/floor/iron/dark,
/area/centcom/central_command_areas/supplypod)
+"qJ" = (
+/obj/structure/filingcabinet/chestdrawer,
+/turf/open/floor/iron/dark,
+/area/centcom/central_command_areas/ferry)
"qK" = (
/obj/machinery/air_sensor{
chamber_id = "nukiebase";
@@ -6714,6 +6695,12 @@
},
/turf/open/floor/wood,
/area/centcom/central_command_areas/admin)
+"tm" = (
+/obj/machinery/light/directional/south,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/machinery/status_display/evac/directional/south,
+/turf/open/floor/iron/dark,
+/area/centcom/central_command_areas/ferry)
"tn" = (
/obj/machinery/power/apc/auto_name/directional/south,
/obj/structure/cable/smart_cable/color/yellow,
@@ -7003,15 +6990,6 @@
/obj/effect/turf_decal/siding/thinplating_new/dark,
/turf/open/floor/mineral/plastitanium/red,
/area/centcom/syndicate_mothership/expansion_bioterrorism)
-"um" = (
-/obj/machinery/computer/communications{
- dir = 1
- },
-/obj/effect/turf_decal/stripes/line{
- dir = 5
- },
-/turf/open/floor/iron,
-/area/centcom/central_command_areas/ferry)
"uo" = (
/obj/machinery/light/floor/has_bulb,
/turf/open/floor/catwalk_floor/titanium,
@@ -8126,17 +8104,6 @@
/obj/machinery/vending/snack,
/turf/open/floor/engine/cult,
/area/centcom/wizard_station)
-"xy" = (
-/obj/structure/table/reinforced,
-/obj/item/paper/fluff/stations/centcom/disk_memo{
- pixel_x = -6;
- pixel_y = -7
- },
-/obj/item/taperecorder{
- pixel_y = 15
- },
-/turf/open/floor/carpet,
-/area/centcom/syndicate_mothership/control)
"xz" = (
/obj/structure/table/wood/fancy,
/obj/item/storage/photo_album,
@@ -8719,6 +8686,17 @@
},
/turf/open/misc/asteroid/snow/airless,
/area/centcom/syndicate_mothership)
+"zv" = (
+/obj/structure/table/reinforced,
+/obj/item/paper/fluff/stations/centcom/disk_memo{
+ pixel_x = -6;
+ pixel_y = -7
+ },
+/obj/item/taperecorder{
+ pixel_y = 15
+ },
+/turf/open/floor/carpet,
+/area/centcom/syndicate_mothership/control)
"zw" = (
/obj/structure/sign/nanotrasen,
/turf/closed/indestructible/reinforced/centcom,
@@ -9466,13 +9444,6 @@
/obj/effect/turf_decal/tile/neutral/fourcorners,
/turf/open/floor/iron/dark,
/area/centcom/tdome/administration)
-"By" = (
-/obj/machinery/light/directional/south,
-/obj/structure/filingcabinet/chestdrawer,
-/obj/effect/turf_decal/tile/neutral/fourcorners,
-/obj/machinery/status_display/evac/directional/south,
-/turf/open/floor/iron/dark,
-/area/centcom/central_command_areas/ferry)
"Bz" = (
/obj/effect/light_emitter{
set_cap = 1;
@@ -11853,18 +11824,6 @@
},
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership/expansion_chemicalwarfare)
-"Ir" = (
-/obj/machinery/computer/emergency_shuttle{
- dir = 1
- },
-/obj/machinery/newscaster{
- pixel_y = -32
- },
-/obj/effect/turf_decal/stripes/line{
- dir = 9
- },
-/turf/open/floor/iron,
-/area/centcom/central_command_areas/ferry)
"Is" = (
/obj/machinery/igniter/on,
/obj/effect/turf_decal/delivery,
@@ -13310,6 +13269,18 @@
/obj/item/reagent_containers/food/drinks/trophy/gold_cup,
/turf/open/floor/iron/grimy,
/area/centcom/tdome/observation)
+"Nf" = (
+/obj/machinery/newscaster{
+ pixel_y = -32
+ },
+/obj/machinery/computer/communications{
+ dir = 1
+ },
+/obj/effect/turf_decal/stripes/end{
+ dir = 1
+ },
+/turf/open/floor/iron,
+/area/centcom/central_command_areas/ferry)
"Ng" = (
/obj/item/storage/box/ids{
pixel_x = 3;
@@ -14409,6 +14380,19 @@
/obj/structure/flora/grass/both,
/turf/open/misc/asteroid/snow,
/area/centcom/syndicate_mothership/control)
+"Qt" = (
+/obj/effect/turf_decal/siding/thinplating_new/dark{
+ dir = 8
+ },
+/obj/structure/table/glass/plasmaglass,
+/obj/item/folder/white,
+/obj/item/pen{
+ pixel_x = 6;
+ pixel_y = 5
+ },
+/obj/machinery/light/cold/directional/west,
+/turf/open/floor/mineral/plastitanium,
+/area/centcom/syndicate_mothership/control)
"Qu" = (
/obj/structure/window/paperframe{
can_atmos_pass = 4
@@ -17321,6 +17305,16 @@
"Ya" = (
/turf/closed/indestructible/reinforced/centcom,
/area/centcom/central_command_areas/armory)
+"Yb" = (
+/obj/item/gun/energy/pulse/carbine,
+/obj/item/flashlight/seclite,
+/obj/structure/table/reinforced,
+/obj/machinery/airalarm/directional/south,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/turf/open/floor/iron,
+/area/centcom/central_command_areas/admin/storage)
"Yc" = (
/obj/structure/fireplace,
/obj/effect/turf_decal/tile/neutral/fourcorners,
@@ -25612,7 +25606,7 @@ ng
VX
XE
od
-dY
+Qt
Sr
nz
br
@@ -37183,7 +37177,7 @@ nz
HN
gJ
dy
-xy
+zv
nt
HJ
VJ
@@ -52858,7 +52852,7 @@ oe
rm
ss
tq
-Ir
+Nf
mD
vA
oe
@@ -53115,7 +53109,7 @@ oe
rn
st
tr
-um
+qJ
mD
vB
oe
@@ -53372,7 +53366,7 @@ mD
WY
su
ts
-By
+tm
mD
vC
wr
@@ -58511,7 +58505,7 @@ XA
Sx
Nn
yi
-pY
+Yb
YU
vb
oe
diff --git a/_maps/multiz_debug.json b/_maps/multiz_debug.json
index 4ffc3abd519e..f8ce1a3e1db2 100644
--- a/_maps/multiz_debug.json
+++ b/_maps/multiz_debug.json
@@ -3,6 +3,9 @@
"map_name": "MultiZ Debug",
"map_path": "map_files/debug",
"map_file": "multiz.dmm",
+ "evacuation_controllers": [
+ "/datum/evacuation_controller/emergency_shuttle"
+ ],
"traits": [
{
"Up": 1,
diff --git a/_maps/runtimestation.json b/_maps/runtimestation.json
index 6b77a7321f21..db37c0ce3300 100644
--- a/_maps/runtimestation.json
+++ b/_maps/runtimestation.json
@@ -3,6 +3,9 @@
"map_name": "Runtime Station",
"map_path": "map_files/debug",
"map_file": "runtimestation.dmm",
+ "evacuation_controllers": [
+ "/datum/evacuation_controller/emergency_shuttle"
+ ],
"space_ruin_levels": 1,
"shuttles": {
"cargo": "cargo_delta"
diff --git a/_maps/shuttles/emergency_bar.dmm b/_maps/shuttles/emergency_bar.dmm
index 8891aaa82e72..2036966585ab 100644
--- a/_maps/shuttles/emergency_bar.dmm
+++ b/_maps/shuttles/emergency_bar.dmm
@@ -555,19 +555,6 @@
},
/turf/open/floor/mineral/titanium,
/area/shuttle/escape)
-"bV" = (
-/obj/structure/table/wood/shuttle_bar{
- boot_dir = 8
- },
-/obj/effect/fun_balloon/sentience/emergency_shuttle{
- group_name = "bar staff on the Emergency Escape Bar"
- },
-/obj/effect/turf_decal/tile/bar,
-/obj/effect/turf_decal/tile/bar{
- dir = 1
- },
-/turf/open/floor/iron,
-/area/shuttle/escape)
"bW" = (
/obj/machinery/light/small/directional/east,
/turf/open/floor/iron/grimy,
@@ -810,7 +797,7 @@ bG
aE
aO
aR
-bV
+aR
aR
aR
aR
diff --git a/_maps/shuttles/emergency_cruise.dmm b/_maps/shuttles/emergency_cruise.dmm
index 5a24b869a06b..165f61f57200 100644
--- a/_maps/shuttles/emergency_cruise.dmm
+++ b/_maps/shuttles/emergency_cruise.dmm
@@ -1069,9 +1069,6 @@
/obj/item/reagent_containers/glass/rag{
pixel_y = 7
},
-/obj/effect/fun_balloon/sentience/emergency_shuttle{
- group_name = "bar staff on the NTSS Independence"
- },
/turf/open/floor/wood,
/area/shuttle/escape)
"Vk" = (
diff --git a/_maps/shuttles/emergency_narnar.dmm b/_maps/shuttles/emergency_narnar.dmm
index ec5d26d3bd8a..95e6452f1e40 100644
--- a/_maps/shuttles/emergency_narnar.dmm
+++ b/_maps/shuttles/emergency_narnar.dmm
@@ -171,10 +171,6 @@
/area/shuttle/escape)
"I" = (
/obj/effect/rune/narsie,
-/obj/effect/fun_balloon/sentience/emergency_shuttle{
- effect_range = 5;
- group_name = "horrible monsters on Shuttle 667"
- },
/turf/open/floor/cult,
/area/shuttle/escape)
"J" = (
diff --git a/_maps/shuttles/emergency_rollerdome.dmm b/_maps/shuttles/emergency_rollerdome.dmm
index 9be037940ed4..a445c8b0fbe2 100644
--- a/_maps/shuttles/emergency_rollerdome.dmm
+++ b/_maps/shuttles/emergency_rollerdome.dmm
@@ -70,13 +70,6 @@
/obj/machinery/vending/games,
/turf/open/floor/eighties,
/area/shuttle/escape)
-"ns" = (
-/obj/structure/table/wood/shuttle_bar,
-/obj/effect/fun_balloon/sentience/emergency_shuttle{
- group_name = "snack bar drone at Uncle Pete's Rollerdome"
- },
-/turf/open/floor/wood,
-/area/shuttle/escape)
"nw" = (
/obj/structure/table,
/obj/machinery/chem_dispenser/drinks{
@@ -464,7 +457,7 @@ dJ
Cg
Cg
ce
-ns
+Ky
Qv
JR
KJ
diff --git a/_maps/theseus.json b/_maps/theseus.json
index cf1b931f4e6a..82609fc799f8 100644
--- a/_maps/theseus.json
+++ b/_maps/theseus.json
@@ -4,6 +4,9 @@
"map_path": "map_files/Theseus",
"map_file": "Theseus.dmm",
"webmap_id": "Theseus",
+ "evacuation_controllers": [
+ "/datum/evacuation_controller/emergency_shuttle"
+ ],
"shuttles": {
"cargo": "cargo_box",
"ferry": "ferry_fancy",
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 1ed5a60e97aa..c6b7c004d872 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -38,14 +38,6 @@
#define DEATHSQUAD "ds"
#define DEATHSQUAD_LEADER "ds_leader"
-//Shuttle elimination hijacking
-/// Does not stop elimination hijacking but itself won't elimination hijack
-#define ELIMINATION_NEUTRAL 0
-/// Needs to be present for shuttle to be elimination hijacked
-#define ELIMINATION_ENABLED 1
-/// Prevents elimination hijack same way as non-antags
-#define ELIMINATION_PREVENT 2
-
//Syndicate Contracts
#define CONTRACT_STATUS_INACTIVE 1
#define CONTRACT_STATUS_ACTIVE 2
@@ -125,12 +117,6 @@
/// JSON string file for all of our heretic influence flavors
#define HERETIC_INFLUENCE_FILE "antagonist_flavor/heretic_influences.json"
-///employers who hire agents to do the hijack
-GLOBAL_LIST_INIT(hijack_employers, list(
- "Legal Trouble",
- "Gone Postal",
-))
-
///employers who hire agents to do a task and escape...
GLOBAL_LIST_INIT(normal_employers, list(
"Legal Trouble",
diff --git a/code/__DEFINES/dcs/signals/signals_evacuation.dm b/code/__DEFINES/dcs/signals/signals_evacuation.dm
new file mode 100644
index 000000000000..e2a0bd09ef34
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_evacuation.dm
@@ -0,0 +1,8 @@
+///from emergency shuttle when arriving at station : ()
+#define COMSIG_EMERGENCYSHUTTLE_ARRIVAL "emergencyshuttle_arrival"
+///from emergency shuttle when making departing sound : ()
+#define COMSIG_EMERGENCYSHUTTLE_ANNOUNCE "emergencyshuttle_announce"
+///from emergency shuttle when departing from station : ()
+#define COMSIG_EMERGENCYSHUTTLE_DEPARTING "emergencyshuttle_departing"
+///from emergency shuttle when returning to CentCom : ()
+#define COMSIG_EMERGENCYSHUTTLE_RETURNED "emergencyshuttle_returned"
diff --git a/code/__DEFINES/dcs/signals/signals_global_object.dm b/code/__DEFINES/dcs/signals/signals_global_object.dm
index 452be16f8391..1212217440fe 100644
--- a/code/__DEFINES/dcs/signals/signals_global_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_global_object.dm
@@ -6,7 +6,7 @@
///from SSsun when the sun changes position : (azimuth)
#define COMSIG_SUN_MOVED "sun_moved"
-///from SSsecurity_level when the security level changes : (new_level)
+///from SSsecurity_level when the security level changes : (old_level, new_level)
#define COMSIG_SECURITY_LEVEL_CHANGED "security_level_changed"
///from SSshuttle when the supply shuttle starts spawning orders : ()
diff --git a/code/__DEFINES/evacuation.dm b/code/__DEFINES/evacuation.dm
new file mode 100644
index 000000000000..d9f3803e71be
--- /dev/null
+++ b/code/__DEFINES/evacuation.dm
@@ -0,0 +1,13 @@
+// States for the evacuation controller
+#define EVACUATION_STATE_IDLE 0
+#define EVACUATION_STATE_INITIATED 1 // Evacuation has begun, but it can be cancelled
+#define EVACUATION_STATE_AWAITING 2 // Awaiting players to board the shuttle/pods/etc, can't be cancelled but can be delayed
+#define EVACUATION_STATE_EVACUATED 3 // Shuttle/pods/etc have departed, can't be cancelled nor delayed
+#define EVACUATION_STATE_FINISHED 4
+
+// Reasons for automatic evacuation
+#define EVACUATION_REASON_CREW_DEATH "crew_death"
+#define EVACUATION_REASON_LONG_ROUND "long_round"
+#define EVACUATION_REASON_VOTE "vote"
+#define EVACUATION_REASON_CONSOLE_DESTROYED "console_destroyed"
+#define EVACUATION_REASON_AI_DESTROYED "ai_destroyed"
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index 447ef1cc165a..8b9f6fbbe145 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -160,7 +160,7 @@
#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
#define ui_ai_alerts "SOUTH:6,WEST+6"
#define ui_ai_announcement "SOUTH:6,WEST+7"
-#define ui_ai_shuttle "SOUTH:6,WEST+8"
+#define ui_ai_evac "SOUTH:6,WEST+8"
#define ui_ai_state_laws "SOUTH:6,WEST+9"
#define ui_ai_mod_int "SOUTH:6,WEST+10"
#define ui_ai_take_picture "SOUTH:6,WEST+11"
diff --git a/code/__DEFINES/roundend.dm b/code/__DEFINES/roundend.dm
index bf3367510219..384cdb35ebc3 100644
--- a/code/__DEFINES/roundend.dm
+++ b/code/__DEFINES/roundend.dm
@@ -1,7 +1,7 @@
//Endgame Results
/// Nuke was detonated in space on same z-level as station
#define NUKE_NEAR_MISS 1
-/// Nuke was detonated on another z-level
+/// Nuke was detonated on another z-level
#define NUKE_MISS_STATION 2
/// Nuke was detonated on the syndicate base
#define NUKE_SYNDICATE_BASE 3
@@ -35,9 +35,7 @@
#define WIZARD_KILLED 19
/// The station was destroyed by it's own self-destruct nuclear device
#define STATION_NUKED 20
-/// The emergency shuttle was successfully hijacked
-#define SHUTTLE_HIJACK 24
/// The gangs on the station were thwarted
#define GANG_DESTROYED 25 // Looks like it was deprecated at some point - Jan 2022
/// The gangs on the station still exist
-#define GANG_OPERATING 26
+#define GANG_OPERATING 26
diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm
index 5b906d97bf30..3201602eddaa 100644
--- a/code/__DEFINES/shuttles.dm
+++ b/code/__DEFINES/shuttles.dm
@@ -11,11 +11,6 @@
#define SHUTTLE_RECHARGING "recharging"
#define SHUTTLE_PREARRIVAL "landing"
-#define EMERGENCY_IDLE_OR_RECALLED (SSshuttle.emergency && ((SSshuttle.emergency.mode == SHUTTLE_IDLE) || (SSshuttle.emergency.mode == SHUTTLE_RECALL)))
-#define EMERGENCY_ESCAPED_OR_ENDGAMED (SSshuttle.emergency && ((SSshuttle.emergency.mode == SHUTTLE_ESCAPE) || (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)))
-#define EMERGENCY_AT_LEAST_DOCKED (SSshuttle.emergency && SSshuttle.emergency.mode != SHUTTLE_IDLE && SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_CALL)
-#define EMERGENCY_PAST_POINT_OF_NO_RETURN ((SSshuttle.emergency && SSshuttle.emergency.mode == SHUTTLE_CALL && !SSshuttle.canRecall()) || EMERGENCY_AT_LEAST_DOCKED)
-
// Shuttle return values
#define SHUTTLE_CAN_DOCK "can_dock"
#define SHUTTLE_NOT_A_DOCKING_PORT "not a docking port"
@@ -53,7 +48,7 @@
#define HYPERSPACE_LAUNCH 2
#define HYPERSPACE_END 3
-#define CALL_SHUTTLE_REASON_LENGTH 12
+#define EVAC_REASON_LENGTH 12
//Engine related
#define ENGINE_COEFF_MIN 0.5
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 9b5e215a2d60..38f59f76a6dd 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -165,6 +165,7 @@
#define INIT_ORDER_STICKY_BAN -10
#define INIT_ORDER_LIGHTING -20
#define INIT_ORDER_SHUTTLE -21
+#define INIT_ORDER_EVACUATION -22
#define INIT_ORDER_MINOR_MAPPING -40
#define INIT_ORDER_PATH -50
#define INIT_ORDER_DECAY -61
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 9ca782860b77..65debbf934b2 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -249,6 +249,10 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
if (CONFIG_GET(flag/log_shuttle))
WRITE_LOG(GLOB.world_shuttle_log, "SHUTTLE: [text]")
+/proc/log_evacuation(text)
+ if (CONFIG_GET(flag/log_evacuation))
+ WRITE_LOG(GLOB.world_evacuation_log, "EVACUATION: [text]")
+
/proc/log_topic(text)
WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]")
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 5a25fb2cc491..008cce1c08e9 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -1,6 +1,6 @@
#define POPCOUNT_SURVIVORS "survivors" //Not dead at roundend
#define POPCOUNT_ESCAPEES "escapees" //Not dead and on centcom/shuttles marked as escaped
-#define POPCOUNT_SHUTTLE_ESCAPEES "shuttle_escapees" //Emergency shuttle only.
+#define POPCOUNT_EVAC_ESCAPEES "evac_escapees" //Evac means shuttle only.
#define POPCOUNT_ESCAPEES_HUMANONLY "human_escapees"
#define PERSONAL_LAST_ROUND "personal last round"
#define SERVER_LAST_ROUND "server last round"
@@ -17,9 +17,7 @@
var/num_shuttle_escapees = 0 //Above and on escape shuttle
var/list/list_of_human_escapees = list() //References to all escaped humans
var/list/list_of_mobs_on_shuttle = list()
- var/list/area/shuttle_areas
- if(SSshuttle?.emergency)
- shuttle_areas = SSshuttle.emergency.shuttle_areas
+ var/list/area/evac_areas = SSevacuation.get_endgame_areas()
for(var/mob/M in GLOB.mob_list)
var/list/mob_data = list()
@@ -38,13 +36,13 @@
list_of_mobs_on_shuttle += M
if(M.stat != DEAD && !isbrain(M) && !iscameramob(M))
num_survivors++
- if(EMERGENCY_ESCAPED_OR_ENDGAMED && (M.onCentCom() || M.onSyndieBase()))
+ if(M.onCentCom() || M.onSyndieBase() || evac_areas[get_area(M)])
num_escapees++
if(ishuman(M))
num_human_escapees++
list_of_human_escapees += M
escape_status = "escapees"
- if(shuttle_areas[get_area(M)])
+ if(evac_areas[get_area(M)])
num_shuttle_escapees++
if(isliving(M))
var/mob/living/L = M
@@ -110,7 +108,7 @@
.[POPCOUNT_SURVIVORS] = num_survivors
.[POPCOUNT_ESCAPEES] = num_escapees
.[POPCOUNT_ESCAPEES_HUMANONLY] = num_human_escapees
- .[POPCOUNT_SHUTTLE_ESCAPEES] = num_shuttle_escapees
+ .[POPCOUNT_EVAC_ESCAPEES] = num_shuttle_escapees
.["all_mobs_on_shuttle"] = list_of_mobs_on_shuttle
.["human_escapees_list"] = list_of_human_escapees
.["station_integrity"] = station_integrity
@@ -330,7 +328,6 @@
/datum/controller/subsystem/ticker/proc/survivor_report(popcount)
var/list/parts = list()
- var/station_evacuated = EMERGENCY_ESCAPED_OR_ENDGAMED
if(GLOB.round_id)
var/statspage = CONFIG_GET(string/roundstatsurl)
@@ -341,9 +338,8 @@
var/total_players = GLOB.joined_player_list.len
if(total_players)
parts+= "[FOURSPACES]Total Population: [total_players]"
- if(station_evacuated)
- parts += " [FOURSPACES]Evacuation Rate: [popcount[POPCOUNT_ESCAPEES]] ([PERCENT(popcount[POPCOUNT_ESCAPEES]/total_players)]%)"
- parts += "[FOURSPACES](on emergency shuttle): [popcount[POPCOUNT_SHUTTLE_ESCAPEES]] ([PERCENT(popcount[POPCOUNT_SHUTTLE_ESCAPEES]/total_players)]%)"
+ parts += " [FOURSPACES]Evacuation Rate: [popcount[POPCOUNT_ESCAPEES]] ([PERCENT(popcount[POPCOUNT_ESCAPEES]/total_players)]%)"
+ parts += "[FOURSPACES](on means of evacuation): [popcount[POPCOUNT_EVAC_ESCAPEES]] ([PERCENT(popcount[POPCOUNT_EVAC_ESCAPEES]/total_players)]%)"
parts += "[FOURSPACES]Survival Rate: [popcount[POPCOUNT_SURVIVORS]] ([PERCENT(popcount[POPCOUNT_SURVIVORS]/total_players)]%)"
if(SSblackbox.first_death)
var/list/ded = SSblackbox.first_death
@@ -421,8 +417,9 @@
var/mob/M = C.mob
if(M.mind && !isnewplayer(M))
if(M.stat != DEAD && !isbrain(M))
- if(EMERGENCY_ESCAPED_OR_ENDGAMED)
- if(!M.onCentCom() && !M.onSyndieBase())
+ if(SSevacuation.evacuation_finished())
+ var/list/area/evac_areas = SSevacuation.get_endgame_areas()
+ if(!M.onCentCom() && !M.onSyndieBase() && !evac_areas[get_area(M)])
parts += "
"
parts += "You managed to survive, but were marooned on [station_name()]..."
else
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 6328be2ec463..1b3a23a8e20f 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -55,6 +55,8 @@ GLOBAL_VAR(tgui_log)
GLOBAL_PROTECT(tgui_log)
GLOBAL_VAR(world_shuttle_log)
GLOBAL_PROTECT(world_shuttle_log)
+GLOBAL_VAR(world_evacuation_log)
+GLOBAL_PROTECT(world_evacuation_log)
GLOBAL_VAR(filter_log)
GLOBAL_PROTECT(filter_log)
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index 0b3c4ebcabae..7cd2a650a10d 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -103,15 +103,15 @@
var/mob/living/silicon/ai/AI = usr
AI.announcement()
-/atom/movable/screen/ai/call_shuttle
- name = "Call Emergency Shuttle"
+/atom/movable/screen/ai/call_evac
+ name = "Start Evacuation"
icon_state = "call_shuttle"
-/atom/movable/screen/ai/call_shuttle/Click()
+/atom/movable/screen/ai/call_evac/Click()
if(..())
return
var/mob/living/silicon/ai/AI = usr
- AI.ai_call_shuttle()
+ AI.ai_start_evacuation()
/atom/movable/screen/ai/state_laws
name = "State Laws"
@@ -232,9 +232,9 @@
using.screen_loc = ui_ai_announcement
static_inventory += using
-//Shuttle
- using = new /atom/movable/screen/ai/call_shuttle(null, src)
- using.screen_loc = ui_ai_shuttle
+//Evac
+ using = new /atom/movable/screen/ai/call_evac(null, src)
+ using.screen_loc = ui_ai_evac
static_inventory += using
//Laws
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 87c37194aa69..a1abf0541210 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -344,6 +344,15 @@
min_val = 0
max_val = 1
integer = FALSE
+ deprecated_by = /datum/config_entry/number/evacuation_autocall_threshold
+
+/datum/config_entry/number/emergency_shuttle_autocall_threshold/DeprecationUpdate(value)
+ return value
+
+/datum/config_entry/number/evacuation_autocall_threshold
+ min_val = 0
+ max_val = 1
+ integer = FALSE
/datum/config_entry/flag/roundstart_traits
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index ca5d58a8364b..78a0fd982333 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -158,6 +158,9 @@
/// log shuttle related actions, ie shuttle computers, shuttle manipulator, emergency console
/datum/config_entry/flag/log_shuttle
+/// log evacuation related actions, ie when triggered, reasons, etc
+/datum/config_entry/flag/log_evacuation
+
/// logs all timers in buckets on automatic bucket reset (Useful for timer debugging)
/datum/config_entry/flag/log_timers_on_bucket_reset
diff --git a/modular_pariah/modules/autotransfer/code/autotransfer.dm b/code/controllers/subsystem/autotransfer.dm
similarity index 94%
rename from modular_pariah/modules/autotransfer/code/autotransfer.dm
rename to code/controllers/subsystem/autotransfer.dm
index 82f16a076444..a33c81fc31cf 100644
--- a/modular_pariah/modules/autotransfer/code/autotransfer.dm
+++ b/code/controllers/subsystem/autotransfer.dm
@@ -36,6 +36,6 @@ SUBSYSTEM_DEF(autotransfer)
targettime = targettime + voteinterval
curvotes++
else
- SSshuttle.autoEnd()
+ SSevacuation.trigger_auto_evac(EVACUATION_REASON_LONG_ROUND)
#undef NO_MAXVOTES_CAP
diff --git a/code/controllers/subsystem/evacuation.dm b/code/controllers/subsystem/evacuation.dm
new file mode 100644
index 000000000000..c7f68548acc8
--- /dev/null
+++ b/code/controllers/subsystem/evacuation.dm
@@ -0,0 +1,246 @@
+#define CREW_DEATH_MESSAGE "Automatically starting evacuation sequence due to crew death."
+
+SUBSYSTEM_DEF(evacuation)
+ name = "Evacuation"
+ wait = 1 SECONDS
+ init_order = INIT_ORDER_EVACUATION
+ flags = SS_KEEP_TIMING
+ runlevels = RUNLEVEL_GAME
+ /// Controllers that handle the evacuation of the station
+ var/list/datum/evacuation_controller/controllers = list()
+ /// A list of things blocking evacuation
+ var/list/evacuation_blockers = list()
+ /// Whether you can cancel evacuation. Used by automatic evacuation
+ var/cancel_blocked = FALSE
+
+/datum/controller/subsystem/evacuation/Initialize(start_timeofday)
+ for(var/path in SSmapping.config.evacuation_controllers)
+ var/datum/evacuation_controller/controller = new path
+ controllers[controller.id] = controller
+ return ..()
+
+/datum/controller/subsystem/evacuation/fire(resumed)
+ if(!SSticker.HasRoundStarted() || length(evacuation_blockers))
+ return
+
+ var/threshold = CONFIG_GET(number/evacuation_autocall_threshold)
+ if(!threshold)
+ return
+
+ var/alive = 0
+ for(var/mob/M as anything in GLOB.player_list)
+ if(M.stat != DEAD)
+ ++alive
+
+ var/total = length(GLOB.joined_player_list)
+ if(total <= 0)
+ return
+
+ if(alive / total > threshold)
+ return
+
+ message_admins(CREW_DEATH_MESSAGE)
+ log_evacuation("[CREW_DEATH_MESSAGE] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
+ priority_announce("Catastrophic casualties detected: crisis evacuation protocols activated - blocking recall signals across all channels.")
+ trigger_auto_evac(EVACUATION_REASON_CREW_DEATH)
+
+/datum/controller/subsystem/evacuation/proc/trigger_auto_evac(reason)
+ cancel_blocked = TRUE
+ for(var/identifier in controllers)
+ if(controllers[identifier].state != EVACUATION_STATE_IDLE)
+ return
+ if(controllers[identifier].start_automatic_evacuation(reason))
+ return
+
+/datum/controller/subsystem/evacuation/proc/can_evac(mob/caller, controller_id)
+ var/datum/evacuation_controller/controller = controllers[controller_id]
+ if(!controller)
+ stack_trace("Invalid controller ID")
+ return "Error 500. Please contact your system administrator."
+ for(var/identifier in controllers)
+ if(controllers[identifier].state >= EVACUATION_STATE_AWAITING)
+ return "Evacuation is already in progress."
+ return controller.can_evac(caller)
+
+/datum/controller/subsystem/evacuation/proc/request_evacuation(mob/caller, reason, controller_id, admin = FALSE)
+ var/datum/evacuation_controller/controller = controllers[controller_id]
+ if(!controller)
+ CRASH("Evacuation was requested for an invalid controller \"[controller_id]\"")
+ for(var/identifier in controllers)
+ if(controllers[identifier].state >= EVACUATION_STATE_AWAITING)
+ to_chat(caller, "Evacuation is already in progress.")
+ return
+ return controller.trigger_evacuation(caller, reason, admin)
+
+/datum/controller/subsystem/evacuation/proc/can_cancel(mob/caller, controller_id)
+ var/datum/evacuation_controller/controller = controllers[controller_id]
+ if(!controller)
+ stack_trace("can_cancel() was passed an invalid controller ID")
+ return "Error 500. Please contact your system administrator."
+ return controller.can_cancel(caller)
+
+/datum/controller/subsystem/evacuation/proc/request_cancel(mob/caller, controller_id)
+ var/datum/evacuation_controller/controller = controllers[controller_id]
+ if(!controller)
+ CRASH("Evacuation cancel was requested for an invalid controller \"[controller_id]\"")
+ controller.trigger_cancel_evacuation(caller)
+
+/datum/controller/subsystem/evacuation/proc/add_evacuation_blocker(datum/bad)
+ evacuation_blockers += bad
+ if(length(evacuation_blockers) == 1)
+ for(var/identifier in controllers)
+ controllers[identifier].evacuation_blocked()
+
+/datum/controller/subsystem/evacuation/proc/remove_evacuation_blocker(datum/bad)
+ evacuation_blockers -= bad
+ if(!length(evacuation_blockers))
+ for(var/identifier in controllers)
+ controllers[identifier].evacuation_unblocked()
+
+/datum/controller/subsystem/evacuation/proc/disable_evacuation(controller_id)
+ if(!controllers[controller_id])
+ CRASH("Tried to disable evacuation for an invalid controller \"[controller_id]\"")
+ controllers[controller_id].disable_evacuation()
+
+/datum/controller/subsystem/evacuation/proc/enable_evacuation(controller_id)
+ if(!controllers[controller_id])
+ CRASH("Tried to enable evacuation for an invalid controller \"[controller_id]\"")
+ controllers[controller_id].enable_evacuation()
+
+/datum/controller/subsystem/evacuation/proc/block_cancel(controller_id)
+ if(!controllers[controller_id])
+ CRASH("Tried to block cancel for an invalid controller \"[controller_id]\"")
+ controllers[controller_id].block_cancel()
+
+/datum/controller/subsystem/evacuation/proc/unblock_cancel(controller_id)
+ if(!controllers[controller_id])
+ CRASH("Tried to unblock cancel for an invalid controller \"[controller_id]\"")
+ controllers[controller_id].unblock_cancel()
+
+//Perhaps move it to SShuttle?
+/datum/controller/subsystem/evacuation/proc/get_customizable_shuttles()
+ var/list/shuttles = list()
+ for(var/identifier in controllers)
+ shuttles += controllers[identifier].get_customizable_shuttles()
+ return shuttles
+
+/datum/controller/subsystem/evacuation/proc/get_endgame_areas()
+ var/list/areas = list()
+ for(var/identifier in controllers)
+ var/datum/evacuation_controller/controller = controllers[identifier]
+ // We only want to add the areas if the controller is in a state where evacuation has finished
+ if(controller.state >= EVACUATION_STATE_EVACUATED)
+ areas += controller.get_endgame_areas()
+ return areas
+
+/datum/controller/subsystem/evacuation/proc/get_stat_data()
+ var/list/data = list()
+ for(var/identifier in controllers)
+ data += controllers[identifier].get_stat_data()
+ return data
+
+/datum/controller/subsystem/evacuation/proc/get_world_topic_status()
+ var/list/status = list()
+ for(var/identifier in controllers)
+ status += controllers[identifier].get_world_topic_status()
+ return status
+
+/datum/controller/subsystem/evacuation/proc/evacuation_in_progress()
+ for(var/identifier in controllers)
+ if(controllers[identifier].state == EVACUATION_STATE_AWAITING)
+ return TRUE
+ return FALSE
+
+/datum/controller/subsystem/evacuation/proc/evacuation_can_be_cancelled()
+ for(var/identifier in controllers)
+ if(!controllers[identifier].can_cancel(null))
+ return FALSE
+ return TRUE
+
+/datum/controller/subsystem/evacuation/proc/station_evacuated()
+ for(var/identifier in controllers)
+ if(controllers[identifier].state >= EVACUATION_STATE_EVACUATED)
+ return TRUE
+ return FALSE
+
+/datum/controller/subsystem/evacuation/proc/evacuation_finished()
+ for(var/identifier in controllers)
+ if(controllers[identifier].state >= EVACUATION_STATE_FINISHED)
+ return TRUE
+ return FALSE
+
+/datum/controller/subsystem/evacuation/proc/delay_evacuation(controller_id, delay)
+ if(!controllers[controller_id])
+ CRASH("Tried to delay evacuation for an invalid controller \"[controller_id]\"")
+ controllers[controller_id].delay_evacuation(delay)
+
+/datum/controller/subsystem/evacuation/proc/get_controllers_names(active_only = FALSE)
+ var/list/names = list()
+ for(var/identifier in controllers)
+ if(!active_only || controllers[identifier].state >= EVACUATION_STATE_AWAITING)
+ names[controllers[identifier].name] = identifier
+ return names
+
+/datum/controller/subsystem/evacuation/proc/get_controllers_list_ai()
+ var/list/names = list()
+ for(var/identifier in controllers)
+ names["[controllers[identifier].name] | [controllers[identifier].get_state()]"] = identifier
+ return names
+
+/datum/controller/subsystem/evacuation/proc/get_initiated_controller()
+ for(var/identifier in controllers)
+ if(controllers[identifier].state == EVACUATION_STATE_INITIATED)
+ return identifier
+ return null
+
+/datum/controller/subsystem/evacuation/proc/get_discord_status()
+ . = ""
+ for(var/identifier in controllers)
+ if(controllers[identifier].state != EVACUATION_STATE_IDLE)
+ . += "\n" + controllers[identifier].get_discord_status()
+ return .
+
+/datum/controller/subsystem/evacuation/proc/emergency_status_display_process(obj/machinery/status_display/evac/display)
+ for(var/identifier in controllers)
+ if(controllers[identifier].state != EVACUATION_STATE_IDLE)
+ . = controllers[identifier].emergency_status_display_process(display)
+ if(.)
+ return .
+ return list("", "")
+
+/datum/controller/subsystem/evacuation/proc/status_display_examine(mob/user, obj/machinery/status_display/evac/display)
+ . = list()
+ for(var/identifier in controllers)
+ . += controllers[identifier].status_display_examine(user, display)
+ return .
+
+/datum/controller/subsystem/evacuation/proc/centcom_recall(identifier, message)
+ if(controllers[identifier])
+ if(controllers[identifier].state != EVACUATION_STATE_INITIATED)
+ return
+ controllers[identifier].centcom_recall(message)
+
+/datum/controller/subsystem/evacuation/proc/get_evac_ui_data(mob/user)
+ var/list/data = list()
+ for(var/identifier in controllers)
+ data += list(controllers[identifier].get_evac_ui_data(user))
+ return data
+
+/datum/controller/subsystem/evacuation/proc/panel_act(list/href_list)
+ if(href_list["evac_controller"])
+ controllers[href_list["evac_controller"]]?.panel_act(href_list)
+
+/datum/controller/subsystem/evacuation/proc/admin_panel()
+ var/list/dat = list("Evacuation Panel
Evacuation Panel
")
+ for(var/identifier in controllers)
+ dat += ""
+ dat += "
[controllers[identifier].name]
"
+ dat += controllers[identifier].admin_panel()
+ usr << browse(dat.Join(), "window=evac_panel;size=500x500")
+ return
+
+/datum/controller/subsystem/evacuation/proc/escape_shuttle_replaced()
+ for(var/identifier in controllers)
+ controllers[identifier].escape_shuttle_replaced()
+
+#undef CREW_DEATH_MESSAGE
diff --git a/code/controllers/subsystem/security_level.dm b/code/controllers/subsystem/security_level.dm
index b8a3e79796d6..f576f0465600 100644
--- a/code/controllers/subsystem/security_level.dm
+++ b/code/controllers/subsystem/security_level.dm
@@ -22,7 +22,8 @@ SUBSYSTEM_DEF(security_level)
* * new_level The new security level that will become our current level
*/
/datum/controller/subsystem/security_level/proc/set_level(new_level)
+ var/old_level = SSsecurity_level.current_level
SSsecurity_level.current_level = new_level
- SEND_SIGNAL(src, COMSIG_SECURITY_LEVEL_CHANGED, new_level)
+ SEND_SIGNAL(src, COMSIG_SECURITY_LEVEL_CHANGED, old_level, new_level)
SSnightshift.check_nightshift()
SSblackbox.record_feedback("tally", "security_level_changes", 1, get_security_level())
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 3926be7f3cfb..a226672f7264 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -26,39 +26,8 @@ SUBSYSTEM_DEF(shuttle)
/// An associative list of the mobile docking ports that have failed a transit request, with the amount of times they've actually failed that transit request, up to MAX_TRANSIT_REQUEST_RETRIES
var/list/transit_request_failures = list()
- /**
- * Emergency shuttle stuff
- */
-
- /// The mobile docking port of the emergency shuttle.
- var/obj/docking_port/mobile/emergency/emergency
/// The mobile docking port of the arrivals shuttle.
var/obj/docking_port/mobile/arrivals/arrivals
- /// The mobile docking port of the backup emergency shuttle.
- var/obj/docking_port/mobile/emergency/backup/backup_shuttle
- /// Time taken for emergency shuttle to reach the station when called (in deciseconds).
- var/emergency_call_time = 10 MINUTES
- /// Time taken for emergency shuttle to leave again once it has docked (in deciseconds).
- var/emergency_dock_time = 3 MINUTES
- /// Time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds).
- var/emergency_escape_time = 2 MINUTES
- /// Where was the emergency shuttle last called from?
- var/area/emergency_last_call_loc
- /// How many times was the escape shuttle called?
- var/emergencyCallAmount = 0
- /// Is the departure of the shuttle currently prevented? FALSE for no, any other number for yes (thanks shuttle code).
- var/emergency_no_escape = FALSE
- /// Do we prevent the recall of the shuttle?
- var/emergency_no_recall = FALSE
- /// Did admins force-prevent the recall of the shuttle?
- var/admin_emergency_no_recall = FALSE
- /// Previous mode of the shuttle before it was forcefully disabled by admins.
- var/last_mode = SHUTTLE_IDLE
- /// Previous time left to the call, only useful for disabling and re-enabling the shuttle for admins so it doesn't have to start the whole timer again.
- var/last_call_time = 10 MINUTES
-
- /// Things blocking escape shuttle from leaving.
- var/list/hostile_environments = list()
/**
* Supply shuttle stuff
@@ -106,8 +75,6 @@ SUBSYSTEM_DEF(shuttle)
/// The current shuttle loan event, if any.
var/datum/round_event/shuttle_loan/shuttle_loan
- /// If the event happens where the crew can purchase shuttle insurance, catastrophe can't run.
- var/shuttle_insurance = FALSE
// If the station has purchased a replacement escape shuttle this round.
var/shuttle_purchased = SHUTTLEPURCHASE_PURCHASABLE
/// For keeping track of ingame events that would unlock new shuttles, such as defeating a boss or discovering a secret item.
@@ -158,10 +125,6 @@ SUBSYSTEM_DEF(shuttle)
if(!arrivals)
log_mapping("No /obj/docking_port/mobile/arrivals placed on the map!")
- if(!emergency)
- log_mapping("No /obj/docking_port/mobile/emergency placed on the map!")
- if(!backup_shuttle)
- log_mapping("No /obj/docking_port/mobile/emergency/backup placed on the map!")
if(!supply)
log_mapping("No /obj/docking_port/mobile/supply placed on the map!")
@@ -196,7 +159,6 @@ SUBSYSTEM_DEF(shuttle)
var/not_in_use = (!T.get_docked())
if(idle && not_centcom_evac && not_in_use)
qdel(T, force=TRUE)
- CheckAutoEvac()
if(!SSmapping.clearing_reserved_turfs)
while(transit_requesters.len)
@@ -212,47 +174,6 @@ SUBSYSTEM_DEF(shuttle)
if(MC_TICK_CHECK)
break
-/datum/controller/subsystem/shuttle/proc/CheckAutoEvac()
- if(emergency_no_escape || admin_emergency_no_recall || emergency_no_recall || !emergency || !SSticker.HasRoundStarted())
- return
-
- var/threshold = CONFIG_GET(number/emergency_shuttle_autocall_threshold)
- if(!threshold)
- return
-
- var/alive = 0
- for(var/I in GLOB.player_list)
- var/mob/M = I
- if(M.stat != DEAD)
- ++alive
-
- var/total = GLOB.joined_player_list.len
- if(total <= 0)
- return //no players no autoevac
-
- if(alive / total <= threshold)
- var/msg = "Automatically dispatching emergency shuttle due to crew death."
- message_admins(msg)
- log_shuttle("[msg] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
- emergency_no_recall = TRUE
- priority_announce("Catastrophic casualties detected: crisis shuttle protocols activated - jamming recall signals across all frequencies.")
- if(emergency.timeLeft(1) > emergency_call_time * 0.4)
- emergency.request(null, set_coefficient = 0.4)
-
-/datum/controller/subsystem/shuttle/proc/block_recall(lockout_timer)
- if(admin_emergency_no_recall)
- priority_announce("Error!", sub_title = "Emergency Shuttle Uplink Alert", sound_type = 'sound/misc/announce_dig.ogg')
- addtimer(CALLBACK(src, PROC_REF(unblock_recall)), lockout_timer)
- return
- emergency_no_recall = TRUE
- addtimer(CALLBACK(src, PROC_REF(unblock_recall)), lockout_timer)
-
-/datum/controller/subsystem/shuttle/proc/unblock_recall()
- if(admin_emergency_no_recall)
- priority_announce("Error!", sub_title = "Emergency Shuttle Uplink Alert", sound_type = 'sound/misc/announce_dig.ogg')
- return
- emergency_no_recall = FALSE
-
/datum/controller/subsystem/shuttle/proc/getShuttle(id)
for(var/obj/docking_port/mobile/M in mobile_docking_ports)
if(M.id == id)
@@ -265,170 +186,6 @@ SUBSYSTEM_DEF(shuttle)
return S
WARNING("couldn't find dock with id: [id]")
-/// Check if we can call the evac shuttle.
-/// Returns TRUE if we can. Otherwise, returns a string detailing the problem.
-/datum/controller/subsystem/shuttle/proc/canEvac(mob/user)
- var/srd = CONFIG_GET(number/shuttle_refuel_delay)
- if(world.time - SSticker.round_start_time < srd)
- return "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before attempting to call."
-
- switch(emergency.mode)
- if(SHUTTLE_RECALL)
- return "The emergency shuttle may not be called while returning to CentCom."
- if(SHUTTLE_CALL)
- return "The emergency shuttle is already on its way."
- if(SHUTTLE_DOCKED)
- return "The emergency shuttle is already here."
- if(SHUTTLE_IGNITING)
- return "The emergency shuttle is firing its engines to leave."
- if(SHUTTLE_ESCAPE)
- return "The emergency shuttle is moving away to a safe distance."
- if(SHUTTLE_STRANDED)
- return "The emergency shuttle has been disabled by CentCom."
-
- return TRUE
-
-/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
- if(!emergency)
- WARNING("requestEvac(): There is no emergency shuttle, but the \
- shuttle was called. Using the backup shuttle instead.")
- if(!backup_shuttle)
- CRASH("requestEvac(): There is no emergency shuttle, \
- or backup shuttle! The game will be unresolvable. This is \
- possibly a mapping error, more likely a bug with the shuttle \
- manipulation system, or badminry. It is possible to manually \
- resolve this problem by loading an emergency shuttle template \
- manually, and then calling register() on the mobile docking port. \
- Good luck.")
- emergency = backup_shuttle
-
- var/can_evac_or_fail_reason = SSshuttle.canEvac(user)
- if(can_evac_or_fail_reason != TRUE)
- to_chat(user, span_alert("[can_evac_or_fail_reason]"))
- return
-
- call_reason = trim(html_encode(call_reason))
-
- if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH && seclevel2num(get_security_level()) > SEC_LEVEL_GREEN)
- to_chat(user, span_alert("You must provide a reason."))
- return
-
- var/area/signal_origin = get_area(user)
- var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
- var/security_num = seclevel2num(get_security_level())
- switch(security_num)
- if(SEC_LEVEL_RED,SEC_LEVEL_DELTA)
- emergency.request(null, signal_origin, html_decode(emergency_reason), 1) //There is a serious threat we gotta move no time to give them five minutes.
- else
- emergency.request(null, signal_origin, html_decode(emergency_reason), 0)
-
- var/datum/radio_frequency/frequency = SSpackets.return_frequency(FREQ_STATUS_DISPLAYS)
-
- if(!frequency)
- return
-
- var/datum/signal/status_signal = new(src, list("command" = "update")) // Start processing shuttle-mode displays to display the timer
- frequency.post_signal(status_signal)
-
- var/area/A = get_area(user)
-
- log_shuttle("[key_name(user)] has called the emergency shuttle.")
- deadchat_broadcast(" has called the shuttle at [span_name("[A.name]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
- if(call_reason)
- SSblackbox.record_feedback("text", "shuttle_reason", 1, "[call_reason]")
- log_shuttle("Shuttle call reason: [call_reason]")
- SSticker.emergency_reason = call_reason
- message_admins("[ADMIN_LOOKUPFLW(user)] has called the shuttle. (TRIGGER CENTCOM RECALL)")
-
-/datum/controller/subsystem/shuttle/proc/centcom_recall(old_timer, admiral_message)
- if(emergency.mode != SHUTTLE_CALL || emergency.timer != old_timer)
- return
- emergency.cancel()
-
- if(!admiral_message)
- admiral_message = pick(GLOB.admiral_messages)
- var/intercepttext = "Daedalus Industries Update: Request For Shuttle.\
- To whom it may concern:
\
- We have taken note of the situation upon [station_name()] and have come to the \
- conclusion that it does not warrant the abandonment of the station. \
- If you do not agree with our opinion we suggest that you open a direct \
- line with us and explain the nature of your crisis.
\
- This message has been automatically generated based upon readings from long \
- range diagnostic tools. To assure the quality of your request every finalized report \
- is reviewed by an on-call rear admiral. \
- Rear Admiral's Notes: \
- [admiral_message]"
- print_command_report(intercepttext, announce = TRUE)
-
-// Called when an emergency shuttle mobile docking port is
-// destroyed, which will only happen with admin intervention
-/datum/controller/subsystem/shuttle/proc/emergencyDeregister()
- // When a new emergency shuttle is created, it will override the
- // backup shuttle.
- src.emergency = src.backup_shuttle
-
-/datum/controller/subsystem/shuttle/proc/cancelEvac(mob/user)
- if(canRecall())
- emergency.cancel(get_area(user))
- log_shuttle("[key_name(user)] has recalled the shuttle.")
- message_admins("[ADMIN_LOOKUPFLW(user)] has recalled the shuttle.")
- deadchat_broadcast(" has recalled the shuttle from [span_name("[get_area_name(user, TRUE)]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
- return 1
-
-/datum/controller/subsystem/shuttle/proc/canRecall()
- if(!emergency || emergency.mode != SHUTTLE_CALL || admin_emergency_no_recall || emergency_no_recall)
- return
- var/security_num = seclevel2num(get_security_level())
- switch(security_num)
- if(SEC_LEVEL_GREEN)
- if(emergency.timeLeft(1) < emergency_call_time)
- return
- if(SEC_LEVEL_BLUE)
- if(emergency.timeLeft(1) < emergency_call_time * 0.5)
- return
- else
- if(emergency.timeLeft(1) < emergency_call_time * 0.25)
- return
- return 1
-
-/datum/controller/subsystem/shuttle/proc/autoEvac()
- if (!SSticker.IsRoundInProgress())
- return
-
- var/callShuttle = TRUE
-
- for(var/thing in INSTANCES_OF(TRACKING_KEY_SHUTTLE_CALLER))
- if(isAI(thing))
- var/mob/living/silicon/ai/AI = thing
- if(AI.deployed_shell && !AI.deployed_shell.client)
- continue
- if(AI.stat || !AI.client)
- continue
- else if(istype(thing, /obj/machinery/computer/communications))
- var/obj/machinery/computer/communications/C = thing
- if(C.machine_stat & BROKEN)
- continue
-
- var/turf/T = get_turf(thing)
- if(T && is_station_level(T.z))
- callShuttle = FALSE
- break
-
- if(callShuttle)
- if(EMERGENCY_IDLE_OR_RECALLED)
- emergency.request(null, set_coefficient = 2.5)
- log_shuttle("There is no means of calling the emergency shuttle anymore. Shuttle automatically called.")
- message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
-
-/datum/controller/subsystem/shuttle/proc/registerHostileEnvironment(datum/bad)
- hostile_environments[bad] = TRUE
- checkHostileEnvironment()
-
-/datum/controller/subsystem/shuttle/proc/clearHostileEnvironment(datum/bad)
- hostile_environments -= bad
- checkHostileEnvironment()
-
-
/datum/controller/subsystem/shuttle/proc/registerTradeBlockade(datum/bad)
trade_blockade[bad] = TRUE
checkTradeBlockade()
@@ -437,7 +194,6 @@ SUBSYSTEM_DEF(shuttle)
trade_blockade -= bad
checkTradeBlockade()
-
/datum/controller/subsystem/shuttle/proc/checkTradeBlockade()
for(var/datum/d in trade_blockade)
if(!istype(d) || QDELETED(d))
@@ -452,32 +208,6 @@ SUBSYSTEM_DEF(shuttle)
supply.mode = SHUTTLE_DOCKED
//Make all cargo consoles speak up
-/datum/controller/subsystem/shuttle/proc/checkHostileEnvironment()
- for(var/datum/d in hostile_environments)
- if(!istype(d) || QDELETED(d))
- hostile_environments -= d
- emergency_no_escape = hostile_environments.len
-
- if(emergency_no_escape && (emergency.mode == SHUTTLE_IGNITING))
- emergency.mode = SHUTTLE_STRANDED
- emergency.timer = null
- emergency.sound_played = FALSE
- priority_announce("Hostile environment detected. \
- Departure has been postponed indefinitely pending \
- conflict resolution.",
- "LRSV Icarus Announcement",
- do_not_modify = TRUE
- )
- if(!emergency_no_escape && (emergency.mode == SHUTTLE_STRANDED))
- emergency.mode = SHUTTLE_DOCKED
- emergency.setTimer(emergency_dock_time)
- priority_announce("Hostile environment resolved. \
- You have 3 minutes to board the Emergency Shuttle.",
- "LRSV Icarus Announcement",
- sound_type = ANNOUNCER_SHUTTLEDOCK,
- do_not_modify = TRUE
- )
-
//try to move/request to dock_home if possible, otherwise dock_away. Mainly used for admin buttons
/datum/controller/subsystem/shuttle/proc/toggleShuttle(shuttle_id, dock_home, dock_away, timed)
var/obj/docking_port/mobile/shuttle_port = getShuttle(shuttle_id)
@@ -622,18 +352,8 @@ SUBSYSTEM_DEF(shuttle)
if (istype(SSshuttle.transit_request_failures))
transit_request_failures = SSshuttle.transit_request_failures
- if (istype(SSshuttle.emergency))
- emergency = SSshuttle.emergency
if (istype(SSshuttle.arrivals))
arrivals = SSshuttle.arrivals
- if (istype(SSshuttle.backup_shuttle))
- backup_shuttle = SSshuttle.backup_shuttle
-
- if (istype(SSshuttle.emergency_last_call_loc))
- emergency_last_call_loc = SSshuttle.emergency_last_call_loc
-
- if (istype(SSshuttle.hostile_environments))
- hostile_environments = SSshuttle.hostile_environments
if (istype(SSshuttle.supply))
supply = SSshuttle.supply
@@ -658,8 +378,6 @@ SUBSYSTEM_DEF(shuttle)
centcom_message = SSshuttle.centcom_message
order_number = SSshuttle.order_number
points = D.account_balance
- emergency_no_escape = SSshuttle.emergency_no_escape
- emergencyCallAmount = SSshuttle.emergencyCallAmount
shuttle_purchased = SSshuttle.shuttle_purchased
lockdown = SSshuttle.lockdown
@@ -997,11 +715,9 @@ SUBSYSTEM_DEF(shuttle)
shuttle_loading = FALSE
if("replace")
- if(existing_shuttle == backup_shuttle)
- // TODO make the load button disabled
- WARNING("The shuttle that the selected shuttle will replace \
- is the backup shuttle. Backup shuttle is required to be \
- intact for round sanity.")
+ if(existing_shuttle == GLOB.backup_shuttle)
+ WARNING("The shuttle to be replaced is marked as important. \
+ Replacing it may cause issues with the round.")
else if(S && !shuttle_loading)
. = TRUE
shuttle_loading = TRUE
@@ -1013,7 +729,7 @@ SUBSYSTEM_DEF(shuttle)
log_admin("[key_name(usr)] load/replaced [mdp] with the shuttle manipulator.")
SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]")
shuttle_loading = FALSE
- if(emergency == mdp) //you just changed the emergency shuttle, there are events in game + captains that can change your snowflake choice.
+ if(istype(mdp, /obj/docking_port/mobile/emergency)) //you just changed the emergency shuttle, there are events in game + captains that can change your snowflake choice.
var/set_purchase = tgui_alert(usr, "Do you want to also disable shuttle purchases/random events that would change the shuttle?", "Butthurt Admin Prevention", list("Yes, disable purchases/events", "No, I want to possibly get owned"))
if(set_purchase == "Yes, disable purchases/events")
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_FORCED
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index f22d003385d4..4ede6cc4cab0 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -37,10 +37,7 @@ SUBSYSTEM_DEF(statpanels)
"Players Playing/Connected: [get_active_player_count()]/[length(GLOB.clients)]"
)
- if(SSshuttle.emergency)
- var/ETA = SSshuttle.emergency.getModeStr()
- if(ETA)
- global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]"
+ global_data += SSevacuation.get_stat_data()
src.currentrun = GLOB.clients.Copy()
mc_data = null
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index dd81d82d3c50..70520b4863e2 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -601,8 +601,6 @@ SUBSYSTEM_DEF(ticker)
news_message = "Tensions have flared with the Space Wizard Federation following the death of one of their members aboard [decoded_station_name]."
if(STATION_NUKED)
news_message = "[decoded_station_name] activated its self-destruct device for unknown reasons. Attempts to clone the Captain for arrest and execution are underway."
- if(SHUTTLE_HIJACK)
- news_message = "During routine evacuation procedures, the emergency shuttle of [decoded_station_name] had its navigation protocols corrupted and went off course, but was recovered shortly after."
if(GANG_OPERATING)
news_message = "The company would like to state that any rumors of criminal organizing on board stations such as [decoded_station_name] are falsehoods, and not to be emulated."
if(GANG_DESTROYED)
diff --git a/code/datums/evacuation_controllers/emergency_shuttle.dm b/code/datums/evacuation_controllers/emergency_shuttle.dm
new file mode 100644
index 000000000000..bd70f07aa2f8
--- /dev/null
+++ b/code/datums/evacuation_controllers/emergency_shuttle.dm
@@ -0,0 +1,316 @@
+//Shouldn't be used anywhere but here
+GLOBAL_DATUM(emergency_shuttle, /obj/docking_port/mobile/emergency)
+GLOBAL_DATUM(backup_shuttle, /obj/docking_port/mobile/emergency)
+
+/datum/evacuation_controller/emergency_shuttle
+ name = "Emergency Shuttle"
+ id = "emergency_shuttle"
+ var/obj/docking_port/mobile/emergency/emergency
+ var/obj/docking_port/mobile/emergency/backup
+ var/emergency_call_time = 10 MINUTES
+ var/emergency_escape_time = 3 MINUTES
+ var/emergency_dock_time = 2 MINUTES
+ var/last_mode
+ var/last_call_time
+
+/datum/evacuation_controller/emergency_shuttle/New()
+ if(GLOB.backup_shuttle)
+ backup = GLOB.backup_shuttle
+ else
+ log_evacuation("Somehow backup shuttle is missing.")
+
+ if(GLOB.emergency_shuttle)
+ emergency = GLOB.emergency_shuttle
+ else
+ log_evacuation("Somehow emergency shuttle is missing. Using backup shuttle.")
+ GLOB.emergency_shuttle = backup
+
+ RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(sec_level_updated))
+
+ emergency.call_time = emergency_call_time * get_sec_level_modifier()
+ emergency.escape_time = emergency_escape_time
+ emergency.dock_time = emergency_dock_time
+
+/datum/evacuation_controller/emergency_shuttle/proc/on_emergency_shuttle_deleted()
+ log_evacuation("Emergency shuttle has been deleted. Using backup shuttle.")
+ emergency = backup
+ emergency.call_time = emergency_call_time * get_sec_level_modifier()
+ emergency.escape_time = emergency_escape_time
+ emergency.dock_time = emergency_dock_time
+
+/datum/evacuation_controller/emergency_shuttle/proc/get_sec_level_modifier(level = null)
+ if(isnull(level))
+ level = SSsecurity_level.current_level
+
+ switch(level)
+ if(SEC_LEVEL_GREEN)
+ return 2
+ if(SEC_LEVEL_BLUE)
+ return 1
+ if(SEC_LEVEL_RED, SEC_LEVEL_DELTA)
+ return 0.5
+
+/datum/evacuation_controller/emergency_shuttle/proc/sec_level_updated(datum/source, old_level, new_level)
+ var/modifier_result = get_sec_level_modifier(new_level) / get_sec_level_modifier(old_level)
+ if(modifier_result != 1)
+ emergency.call_time *= modifier_result
+ emergency.modTimer(modifier_result)
+
+/datum/evacuation_controller/emergency_shuttle/get_state()
+ switch(state)
+ if(EVACUATION_STATE_IDLE)
+ return "Emergency shuttle is idle at CentCom"
+ if(EVACUATION_STATE_INITIATED)
+ return "Emergency shuttle is on the way to the station. ETA: [emergency.getTimerStr()]"
+ if(EVACUATION_STATE_AWAITING)
+ return "Emergency shuttle is docked at the station. Awaiting crew. ETD: [emergency.getTimerStr()]"
+ if(EVACUATION_STATE_EVACUATED)
+ return "Emergency shuttle has left the station. ETA: [emergency.getTimerStr()]"
+ if(EVACUATION_STATE_FINISHED)
+ return "Emergency shuttle has arrived at CentCom"
+
+/datum/evacuation_controller/emergency_shuttle/can_evac(mob/user)
+ var/srd = CONFIG_GET(number/shuttle_refuel_delay)
+ if(world.time - SSticker.round_start_time < srd)
+ return "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before attempting to call."
+
+ if(state >= EVACUATION_STATE_IDLE)
+ switch(emergency.mode)
+ if(SHUTTLE_CALL)
+ return "The emergency shuttle is already on its way."
+ if(SHUTTLE_RECALL)
+ return "The emergency shuttle may not be called while returning to CentCom."
+ if(SHUTTLE_DOCKED)
+ return "The emergency shuttle is already here."
+ if(SHUTTLE_IGNITING)
+ return "The emergency shuttle is firing its engines to leave."
+ if(SHUTTLE_ESCAPE)
+ return "The emergency shuttle is moving away to a safe distance."
+ if(SHUTTLE_STRANDED)
+ return "The emergency shuttle has been disabled by CentCom."
+
+ if(evacuation_disabled)
+ return "The emergency shuttle has been disabled by CentCom."
+
+ return ..()
+
+/datum/evacuation_controller/emergency_shuttle/start_evacuation(mob/user, call_reason, area/signal_origin)
+ evac_calls_count++
+
+ var/message = "The emergency shuttle has been called."
+ if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED)
+ message += " Red Alert state confirmed: Dispatching priority shuttle."
+
+ RegisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_ARRIVAL, PROC_REF(on_emergency_shuttle_arrived))
+ state = EVACUATION_STATE_INITIATED
+ emergency.request(null)
+
+ message += " It will arrive in [emergency.timeLeft(600)] minutes.\nNature of emergency:\n\n[call_reason]"
+
+ if(last_evac_call_loc)
+ message += "\n\nCall signal traced. Results can be viewed on any communications console."
+
+ if(cancel_disabled)
+ message += "\n\nWarning: Shuttle recall subroutines disabled; Recall not possible."
+
+ priority_announce(message, FLAVOR_CENTCOM_NAME, sound_type = ANNOUNCER_SHUTTLECALLED)
+
+ var/datum/radio_frequency/frequency = SSpackets.return_frequency(FREQ_STATUS_DISPLAYS)
+ var/datum/signal/status_signal = new(src, list("command" = "update")) // Start processing shuttle -mode displays to display the timer
+ frequency.post_signal(status_signal)
+
+/datum/evacuation_controller/emergency_shuttle/start_automatic_evacuation(reason)
+ switch(reason)
+ if(EVACUATION_REASON_CREW_DEATH)
+ RegisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_ARRIVAL, PROC_REF(on_emergency_shuttle_arrived))
+ state = EVACUATION_STATE_INITIATED
+ UnregisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED)
+ emergency.call_time = emergency_call_time * 0.5
+ emergency.request(null)
+ if(EVACUATION_REASON_VOTE, EVACUATION_REASON_LONG_ROUND, EVACUATION_REASON_CONSOLE_DESTROYED)
+ RegisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_ARRIVAL, PROC_REF(on_emergency_shuttle_arrived))
+ state = EVACUATION_STATE_INITIATED
+ emergency.request(null)
+ priority_announce("The shift has come to an end and the shuttle called. [SSsecurity_level.current_level == SEC_LEVEL_RED ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [emergency.timeLeft(600)] minutes.", FLAVOR_CENTCOM_NAME, sound_type = ANNOUNCER_SHUTTLECALLED)
+
+/datum/evacuation_controller/emergency_shuttle/proc/on_emergency_shuttle_arrived(datum/source)
+ state = EVACUATION_STATE_AWAITING
+ UnregisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_ARRIVAL)
+ RegisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_ANNOUNCE, PROC_REF(on_emergency_shuttle_announce))
+ RegisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_DEPARTING, PROC_REF(on_emergency_shuttle_departed))
+ send2adminchat("Server", "The Emergency Shuttle has docked with the station.")
+ priority_announce("The Icarus has docked with the station. You have [emergency.timeLeft(600)] minutes to board before departure.", "LRSV Icarus Announcement", sound_type = ANNOUNCER_SHUTTLEDOCK)
+
+/datum/evacuation_controller/emergency_shuttle/proc/on_emergency_shuttle_announce(datum/source)
+ priority_announce("Engines spooling up. Prepare for resonance jump.", "LRSV Icarus Announcement", do_not_modify = TRUE)
+
+/datum/evacuation_controller/emergency_shuttle/proc/on_emergency_shuttle_departed(datum/source)
+ state = EVACUATION_STATE_EVACUATED
+ UnregisterSignal(emergency, list(COMSIG_EMERGENCYSHUTTLE_DEPARTING, COMSIG_EMERGENCYSHUTTLE_ANNOUNCE))
+ RegisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_RETURNED, PROC_REF(on_emergency_shuttle_returned))
+ priority_announce("The Icarus has entered the resonance gate and is enroute to it's destination. Estimate [emergency.timeLeft(600)] minutes until the shuttle docks at Sector Control.", "LRSV Icarus Announcement")
+ INVOKE_ASYNC(SSticker, TYPE_PROC_REF(/datum/controller/subsystem/ticker, poll_hearts))
+ SSmapping.mapvote() //If no map vote has been run yet, start one.
+
+/datum/evacuation_controller/emergency_shuttle/proc/on_emergency_shuttle_returned(datum/source)
+ state = EVACUATION_STATE_FINISHED
+ UnregisterSignal(emergency, COMSIG_EMERGENCYSHUTTLE_RETURNED)
+
+/datum/evacuation_controller/emergency_shuttle/can_cancel(mob/user)
+ // Point of no return after 50% of the time has passed
+ if(emergency.timeLeft(1) < emergency_call_time * get_sec_level_modifier() * 0.5)
+ return FALSE
+ return ..()
+
+/datum/evacuation_controller/emergency_shuttle/cancel_evacuation(mob/user)
+ state = EVACUATION_STATE_IDLE
+ emergency.cancel()
+ //TODO: Signal changes here
+ priority_announce("The emergency shuttle has been recalled.[last_evac_call_loc ? " Recall signal traced. Results can be viewed on any communications console." : "" ]", FLAVOR_CENTCOM_NAME, sound_type = ANNOUNCER_SHUTTLERECALLED)
+
+/datum/evacuation_controller/emergency_shuttle/get_customizable_shuttles()
+ return list(emergency)
+
+/datum/evacuation_controller/emergency_shuttle/get_endgame_areas()
+ return emergency.shuttle_areas
+
+/datum/evacuation_controller/emergency_shuttle/evacuation_blocked()
+ if(emergency.mode != SHUTTLE_DOCKED)
+ return
+ emergency.mode = SHUTTLE_STRANDED
+ emergency.timer = null
+ emergency.sound_played = FALSE
+ priority_announce("Hostile environment detected. \
+ Departure has been postponed indefinitely pending \
+ conflict resolution.",
+ "LRSV Icarus Announcement",
+ do_not_modify = TRUE
+ )
+
+/datum/evacuation_controller/emergency_shuttle/evacuation_unblocked()
+ if(emergency.mode != SHUTTLE_STRANDED)
+ return
+ emergency.mode = SHUTTLE_DOCKED
+ emergency.setTimer(emergency_dock_time)
+ priority_announce("Hostile environment resolved. \
+ You have 3 minutes to board the Emergency Shuttle.",
+ "LRSV Icarus Announcement",
+ sound_type = ANNOUNCER_SHUTTLEDOCK,
+ do_not_modify = TRUE
+ )
+
+/datum/evacuation_controller/emergency_shuttle/disable_evacuation()
+ if(!..())
+ return FALSE
+ last_mode = emergency.mode
+ last_call_time = emergency.timeLeft(1)
+ emergency.setTimer(0)
+ emergency.mode = SHUTTLE_DISABLED
+ priority_announce(
+ "Warning: Emergency Shuttle uplink failure, shuttle disabled until further notice.",
+ "LRSV Icarus Announcement",
+ "Emergency Shuttle Uplink Alert",
+ 'sound/misc/announce_dig.ogg'
+ )
+ return TRUE
+
+/datum/evacuation_controller/emergency_shuttle/enable_evacuation()
+ if(!..())
+ return FALSE
+ emergency.mode = last_mode
+ if(last_call_time < 10 SECONDS && last_mode != SHUTTLE_IDLE)
+ last_call_time = 10 SECONDS //Make sure no insta departures.
+ emergency.setTimer(last_call_time)
+ priority_announce("Warning: Emergency Shuttle uplink reestablished, shuttle enabled.", "LRSV Icarus Announcement", "Emergency Shuttle Uplink Alert", 'sound/misc/announce_dig.ogg')
+ return TRUE
+
+/datum/evacuation_controller/emergency_shuttle/delay_evacuation(delay)
+ ..()
+ emergency.setTimer(emergency.timeLeft(1) + delay)
+
+/datum/evacuation_controller/emergency_shuttle/centcom_recall(message)
+ emergency.cancel()
+ //TODO: Signal changes here
+
+ if(!message)
+ message = pick(GLOB.admiral_messages)
+ var/intercepttext = "Daedalus Industries Update: Request For Shuttle.\
+ To whom it may concern:
\
+ We have taken note of the situation upon [station_name()] and have come to the \
+ conclusion that it does not warrant the abandonment of the station. \
+ If you do not agree with our opinion we suggest that you open a direct \
+ line with us and explain the nature of your crisis.
\
+ This message has been automatically generated based upon readings from long \
+ range diagnostic tools. To assure the quality of your request every finalized report \
+ is reviewed by an on-call rear admiral. \
+ Rear Admiral's Notes: \
+ [message]"
+ print_command_report(intercepttext, announce = TRUE)
+
+/datum/evacuation_controller/emergency_shuttle/get_stat_data()
+ var/ETA = emergency.getModeStr()
+ if(ETA)
+ return list("[ETA] [emergency.getTimerStr()]")
+ return list()
+
+/datum/evacuation_controller/emergency_shuttle/get_world_topic_status()
+ return list(
+ "shuttle_mode" = emergency.mode,
+ "shuttle_timer" = emergency.timeLeft()
+ )
+
+/datum/evacuation_controller/emergency_shuttle/get_discord_status()
+ if(emergency.getModeStr())
+ . = "[emergency.getModeStr()]: [emergency.getTimerStr()]"
+ if(SSticker.emergency_reason)
+ . += ", Shuttle call reason: [SSticker.emergency_reason]"
+ return .
+ return ""
+
+/datum/evacuation_controller/emergency_shuttle/emergency_status_display_process(obj/machinery/status_display/evac/display)
+ return list("-[emergency.getModeStr()]-", emergency.getTimerStr())
+
+/datum/evacuation_controller/emergency_shuttle/status_display_examine(mob/user, obj/machinery/status_display/evac/display)
+ return list(
+ display.examine_shuttle(user, emergency)
+ )
+
+/datum/evacuation_controller/emergency_shuttle/get_evac_ui_data(mob/user)
+ .=..()
+ .["icon"] = "space-shuttle"
+ if(state != EVACUATION_STATE_IDLE)
+ .["actionName"] = "Recall Emergency Shuttle"
+ else
+ .["actionName"] = "Call Emergency Shuttle"
+
+/datum/evacuation_controller/emergency_shuttle/admin_panel()
+ var/list/dat = list()
+ if(state == EVACUATION_STATE_IDLE)
+ dat += "Call Shuttle "
+ else
+ dat += "[emergency.getModeStr()]: [emergency.getTimerStr()] "
+ if(state == EVACUATION_STATE_INITIATED)
+ dat += "Recall Shuttle "
+ return dat
+
+/datum/evacuation_controller/emergency_shuttle/panel_act(list/href_list)
+ if(!check_rights(R_ADMIN))
+ return
+
+ if(href_list["call_shuttle"] && state == EVACUATION_STATE_IDLE)
+ SSevacuation.request_evacuation(usr, null, id, admin = TRUE)
+
+ if(href_list["recall_shuttle"] && state != EVACUATION_STATE_IDLE)
+ SSevacuation.request_cancel(usr, id)
+
+ if(href_list["edit_shuttle_time"])
+ var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", emergency.timeLeft() ) as num|null
+ if(!timer)
+ return
+ emergency.setTimer(timer SECONDS)
+ log_evacuation("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.")
+ minor_announce("The emergency shuttle will reach its destination in [DisplayTimeText(timer SECONDS)].")
+ message_admins(span_adminnotice("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds."))
+
+/datum/evacuation_controller/emergency_shuttle/escape_shuttle_replaced()
+ emergency = GLOB.emergency_shuttle
diff --git a/code/datums/evacuation_controllers/evacuation_controller.dm b/code/datums/evacuation_controllers/evacuation_controller.dm
new file mode 100644
index 000000000000..383db78dd398
--- /dev/null
+++ b/code/datums/evacuation_controllers/evacuation_controller.dm
@@ -0,0 +1,216 @@
+/datum/evacuation_controller
+ /// Name of the evacuation controller
+ var/name = "Evacuation Controller"
+ /// The type of the evacuation controller
+ var/id = "evacuation_controller"
+ /// The current state of the evacuation
+ var/state = EVACUATION_STATE_IDLE
+ /// How many times was evacuation triggered
+ var/evac_calls_count = 0
+ /// Where was the last evacuation call
+ var/area/last_evac_call_loc
+ /// Did admins block the recall of evacuation
+ var/cancel_disabled = FALSE
+ /// Did admins block the evacuation
+ var/evacuation_disabled = FALSE
+ /// The time until the evacuation is delayed.
+ /// Used to prevent canceling to reset the timer
+ var/delayed_until = 0
+
+/// Returns the current state of the evacuation
+/datum/evacuation_controller/proc/get_state()
+ switch(state)
+ if(EVACUATION_STATE_IDLE)
+ return "Idle"
+ if(EVACUATION_STATE_INITIATED)
+ return "Initiadited"
+ if(EVACUATION_STATE_AWAITING)
+ return "Awaiting crew"
+ if(EVACUATION_STATE_EVACUATED)
+ return "Past point of no return"
+ if(EVACUATION_STATE_FINISHED)
+ return "Finished"
+
+/// Whether we can trigger evacuation sequence at the moment.
+/// Returns either TRUE or a string with the reason why it's not possible
+/datum/evacuation_controller/proc/can_evac(mob/user)
+ if(evacuation_disabled)
+ return "Evacuation is disabled"
+ if(state != EVACUATION_STATE_IDLE)
+ return "Evacuation is already in progress"
+ return TRUE
+
+/// Triggers the evacuation sequence, if possible
+/datum/evacuation_controller/proc/trigger_evacuation(mob/user, call_reason, admin)
+ var/can_evac_or_fail_reason = can_evac(user)
+ if(can_evac_or_fail_reason != TRUE)
+ to_chat(user, span_alert("[can_evac_or_fail_reason]"))
+ return FALSE
+ log_evacuation("[key_name(user)] has start the evacuation.")
+
+ var/area/signal_origin = get_area(user)
+ if(!admin && prob(70))
+ last_evac_call_loc = signal_origin
+ else
+ last_evac_call_loc = null
+ start_evacuation(user, call_reason)
+
+ deadchat_broadcast(" has started the evacuation at [span_name("[signal_origin.name]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
+ if(call_reason)
+ SSblackbox.record_feedback("text", "shuttle_reason", 1, "[call_reason]")
+ log_evacuation("Evacuation reason: [call_reason]")
+ SSticker.emergency_reason = call_reason
+ message_admins("[ADMIN_LOOKUPFLW(user)] has started the evacuation. (TRIGGER CENTCOM RECALL)")
+ return TRUE
+
+/// Starts the evacuation sequence. Should not be called directly, use trigger_evacuation instead
+/datum/evacuation_controller/proc/start_evacuation(mob/user, call_reason)
+ CRASH("start_evacuation not implemented. Type: [type]")
+
+/// Starts the automatic evacuation sequence
+/// Called when round is going for too long, when most of the crew is dead, etc.
+/datum/evacuation_controller/proc/start_automatic_evacuation(reason)
+ CRASH("start_automatic_evacuation not implemented. Type: [type]")
+
+/// Whether we can cancel the evacuation sequence at the moment
+/datum/evacuation_controller/proc/can_cancel(mob/user)
+ if(SSevacuation.cancel_blocked || cancel_disabled)
+ return FALSE
+ if(delayed_until > world.time)
+ return FALSE
+ if(state == EVACUATION_STATE_IDLE || state >= EVACUATION_STATE_EVACUATED)
+ return FALSE
+ return TRUE
+
+/// Cancels the evacuation sequence, if possible
+/datum/evacuation_controller/proc/trigger_cancel_evacuation(mob/user, admin)
+ if(!can_cancel(user))
+ return FALSE
+ log_evacuation("[key_name(user)] has canceled the evacuation.")
+ var/area/signal_origin = get_area(user)
+ if(!admin && prob(70))
+ last_evac_call_loc = signal_origin
+ else
+ last_evac_call_loc = null
+ cancel_evacuation(user)
+ SSticker.emergency_reason = null
+ message_admins("[ADMIN_LOOKUPFLW(user)] has canceled the evacuation.")
+ deadchat_broadcast(" has canceled the evacuation from [span_name("[signal_origin.name]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
+ return TRUE
+
+/// Cancels the evacuation sequence. Should not be called directly, use trigger_cancel_evacuation instead
+/datum/evacuation_controller/proc/cancel_evacuation(mob/user)
+ CRASH("trigger_cancel_evacuation not implemented. Type: [type]")
+
+/// Returns a list of shuttles that can be replaced with a custom shuttle
+/datum/evacuation_controller/proc/get_customizable_shuttles()
+ return list()
+
+/// Returns assoc list of evac areas = TRUE. E.g. escape pods, shuttles, etc. Doesn't include CentCom
+/datum/evacuation_controller/proc/get_endgame_areas()
+ return list()
+
+/// Called when the evacuation is blocked for some reason
+/datum/evacuation_controller/proc/evacuation_blocked()
+ return
+
+/// Called when the evacuation is unblocked
+/datum/evacuation_controller/proc/evacuation_unblocked()
+ return
+
+/// Called when admins force-disable evacuation
+/datum/evacuation_controller/proc/disable_evacuation()
+ if(evacuation_disabled)
+ return FALSE
+ evacuation_disabled = TRUE
+ return TRUE
+
+/// Called when admins re-enable evacuation
+/datum/evacuation_controller/proc/enable_evacuation()
+ if(!evacuation_disabled)
+ return FALSE
+ evacuation_disabled = FALSE
+ return TRUE
+
+/// Called when admins force-disable evacuation cancellation
+/datum/evacuation_controller/proc/block_cancel()
+ if(cancel_disabled)
+ return FALSE
+ cancel_disabled = TRUE
+ return TRUE
+
+/// Called when admins allow cancellation of evacuation
+/datum/evacuation_controller/proc/unblock_cancel()
+ if(!cancel_disabled)
+ return FALSE
+ cancel_disabled = FALSE
+ return TRUE
+
+/// Called when you need to delay the evacuation for some reason.
+/datum/evacuation_controller/proc/delay_evacuation(delay)
+ if(delayed_until > world.time)
+ delayed_until = world.time + delay
+ else
+ delayed_until += delay
+
+/// Called when admin cancels the evacuation through CentCom for RP reasons
+/datum/evacuation_controller/proc/centcom_recall(message)
+ return
+
+/// Returns a list of strings to display in the evacuation status panel
+/datum/evacuation_controller/proc/get_stat_data()
+ return list()
+
+/// Returns assoc list of world status
+/datum/evacuation_controller/proc/get_world_topic_status()
+ return list()
+
+/// Returns a string with the current state of the evacuation for the Discord message
+/datum/evacuation_controller/proc/get_discord_status()
+ return ""
+
+/// Returns a string with the current state of the evacuation for the status display
+/datum/evacuation_controller/proc/emergency_status_display_process(obj/machinery/status_display/evac/display)
+ return // should return list with 2 strings
+
+/// Returns a list of strigns to display when examining the status display during evacuation
+/datum/evacuation_controller/proc/status_display_examine(mob/user, obj/machinery/status_display/evac/display)
+ return list()
+
+/// Returns data for the communication console
+/datum/evacuation_controller/proc/get_evac_ui_data(mob/user)
+ . = list(
+ "id" = id, // unique identifier for the evacuation
+ "started" = null, // if the evacuation has started
+ "actionName" = null, // name for the button to call or recall
+ "canEvacOrRecall" = null, // whether the user can call or recall the evacuation. If not, the reason
+ "status" = null, // current status of the evacuation
+ "traceString" = null, // string to display if last evacuation call was traced
+ "icon" = null,
+ )
+
+ if(state != EVACUATION_STATE_IDLE)
+ .["started"] = TRUE
+ .["actionName"] = "Cancel Evacuation ([name]})"
+ .["canEvacOrRecall"] = can_cancel(user)
+ .["status"] = get_state()
+ else
+ .["started"] = FALSE
+ .["actionName"] = "Start Evacuation [name]"
+ .["canEvacOrRecall"] = can_evac(user)
+
+ if(last_evac_call_loc)
+ .["traceString"] = "Last evacuation call was traced to [last_evac_call_loc]"
+ else if(evac_calls_count > 0)
+ .["traceString"] = "Unable to trace last evacuation call"
+
+ return .
+
+/datum/evacuation_controller/proc/admin_panel()
+ return list()
+
+/datum/evacuation_controller/proc/panel_act(list/href_list)
+ return
+
+/datum/evacuation_controller/proc/escape_shuttle_replaced()
+ return
diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm
index 8cc705af3023..8d5e576959da 100644
--- a/code/datums/map_config.dm
+++ b/code/datums/map_config.dm
@@ -32,6 +32,10 @@
"whiteship" = "whiteship_box",
"emergency" = "emergency_box")
+ var/evacuation_controllers = list(
+ /datum/evacuation_controller/emergency_shuttle
+ )
+
/// Dictionary of job sub-typepath to template changes dictionary
var/job_changes = list()
/// List of additional areas that count as a part of the library
@@ -193,6 +197,23 @@
continue
library_areas += path
+ evacuation_controllers = list()
+ if("evacuation_controllers" in json)
+ if(!islist(json["evacuation_controllers"]))
+ log_world("map_config \"evacuation_controllers\" field is missing or invalid!")
+ return
+
+ for(var/path_as_text in json["evacuation_controllers"])
+ var/path = text2path(path_as_text)
+ if(!ispath(path, /datum/evacuation_controller))
+ stack_trace("Invalid path in mapping config for evacuation controllers: \[[path_as_text]\]")
+ continue
+ evacuation_controllers += path
+
+ if(!length(evacuation_controllers))
+ log_world("map_config \"evacuation_controllers\" field is empty!")
+ return
+
defaulted = FALSE
return TRUE
#undef CHECK_EXISTS
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 6ab6f6425a3b..5bd0208dd7c8 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -843,12 +843,6 @@
if(G)
G.reenter_corpse()
-/// Sets our can_hijack to the fastest speed our antag datums allow.
-/datum/mind/proc/get_hijack_speed()
- . = 0
- for(var/datum/antagonist/A in antag_datums)
- . = max(., A.hijack_speed())
-
/datum/mind/proc/has_objective(objective_type)
for(var/datum/antagonist/A in antag_datums)
for(var/O in A.objectives)
diff --git a/code/datums/status_effects/debuffs/drunk.dm b/code/datums/status_effects/debuffs/drunk.dm
index 9bdef49a61e3..4a54cb975d3e 100644
--- a/code/datums/status_effects/debuffs/drunk.dm
+++ b/code/datums/status_effects/debuffs/drunk.dm
@@ -191,7 +191,7 @@
owner.adjustToxLoss(1)
if(owner.stat == CONSCIOUS && prob(20))
// Don't put us in a deep sleep if the shuttle's here. QoL, mainly.
- if(SSshuttle.emergency.mode == SHUTTLE_DOCKED && is_station_level(owner.z))
+ if(SSevacuation.evacuation_in_progress() && is_station_level(owner.z))
to_chat(owner, span_warning("You're so tired... but you can't miss that shuttle..."))
else
diff --git a/code/datums/vote.dm b/code/datums/vote.dm
index f69f680cb34e..bc698e40993f 100644
--- a/code/datums/vote.dm
+++ b/code/datums/vote.dm
@@ -167,7 +167,9 @@
if(!(real_winner == INITIATE_TRANSFER))
return
- SSshuttle.autoEnd()
+ SSevacuation.trigger_auto_evac(EVACUATION_REASON_VOTE)
+ log_game("Round end vote passed. Shuttle has been auto-called.")
+ message_admins("Round end vote passed. Shuttle has been auto-called.")
var/obj/machinery/computer/communications/C = locate() in INSTANCES_OF(/obj/machinery/computer/communications)
if(C)
C.post_status("shuttle")
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index f47b4b9e7ea3..c289390e4a8b 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -230,11 +230,7 @@
.["popcap"] = max(CONFIG_GET(number/soft_popcap), CONFIG_GET(number/hard_popcap), CONFIG_GET(number/extreme_popcap)) //generalized field for this concept for use across ss13 codebases
.["bunkered"] = CONFIG_GET(flag/panic_bunker) || FALSE
.["interviews"] = CONFIG_GET(flag/panic_bunker_interview) || FALSE
- if(SSshuttle?.emergency)
- .["shuttle_mode"] = SSshuttle.emergency.mode
- // Shuttle status, see /__DEFINES/stat.dm
- .["shuttle_timer"] = SSshuttle.emergency.timeLeft()
- // Shuttle timer, in seconds
+ . += SSevacuation.get_world_topic_status()
//Status Cog Support Code
/datum/world_topic/whois
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index a94b59897c13..e07bb2335207 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -616,7 +616,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
midround_injection_cooldown = (round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), midround_delay_min, midround_delay_max)) + world.time)
// Time to inject some threat into the round
- if(EMERGENCY_PAST_POINT_OF_NO_RETURN) // Unless the shuttle is past the point of no return
+ if(SSevacuation.station_evacuated()) // Unless the shuttle is past the point of no return
return
message_admins("DYNAMIC: Checking for midround injection.")
@@ -701,7 +701,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer)
if (GLOB.dynamic_forced_extended)
return
- if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
+ // No more rules after the game ended
+ if(!SSticker.IsRoundInProgress())
return
if (forced_latejoin_rule)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index 5f0985765ecf..518edbb08122 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -143,7 +143,7 @@
new_head = M.mind.add_antag_datum(new_head, revolution)
revolution.update_objectives()
revolution.update_heads()
- SSshuttle.registerHostileEnvironment(revolution)
+ SSevacuation.add_evacuation_blocker(revolution)
return TRUE
else
log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index 0c72e22d7976..aa2646d86ac1 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -541,7 +541,7 @@
if(revolution.members.len)
revolution.update_objectives()
revolution.update_heads()
- SSshuttle.registerHostileEnvironment(revolution)
+ SSevacuation.add_evacuation_blocker(revolution)
return TRUE
log_game("DYNAMIC: [ruletype] [name] failed to get any eligible headrevs. Refunding [cost] threat.")
return FALSE
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index cc22a5cf58ff..5f1e05c7ed8c 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -59,7 +59,7 @@
/datum/game_mode/proc/check_finished(force_ending) //to be called by SSticker
if(!SSticker.setup_done)
return FALSE
- if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
+ if(SSevacuation.evacuation_finished())
return TRUE
if(GLOB.station_was_nuked)
return TRUE
@@ -196,10 +196,8 @@
SSticker.mode_result = "undefined"
if(GLOB.station_was_nuked)
SSticker.news_report = STATION_DESTROYED_NUKE
- if(EMERGENCY_ESCAPED_OR_ENDGAMED)
+ if(SSevacuation.evacuation_finished())
SSticker.news_report = STATION_EVACUATED
- if(SSshuttle.emergency.is_hijacked())
- SSticker.news_report = SHUTTLE_HIJACK
/// Mode specific admin panel.
/datum/game_mode/proc/admin_panel()
diff --git a/code/game/gamemodes/objectives/_objective.dm b/code/game/gamemodes/objectives/_objective.dm
index e597ea18c69d..a09db350181c 100644
--- a/code/game/gamemodes/objectives/_objective.dm
+++ b/code/game/gamemodes/objectives/_objective.dm
@@ -72,13 +72,14 @@ GLOBAL_LIST_EMPTY(objectives) //PARIAH EDIT
return TRUE
if(SSticker.force_ending || GLOB.station_was_nuked) // Just let them win.
return TRUE
- if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
+ if(SSevacuation.evacuation_finished())
return FALSE
var/area/current_area = get_area(M.current)
if(!current_area || istype(current_area, /area/shuttle/escape/brig)) // Fails if they are in the shuttle brig
return FALSE
var/turf/current_turf = get_turf(M.current)
- return current_turf.onCentCom() || current_turf.onSyndieBase()
+ var/list/area/evac_areas = SSevacuation.get_endgame_areas()
+ return current_turf.onCentCom() || current_turf.onSyndieBase() || evac_areas[current_area]
/datum/objective/proc/check_completion()
return completed
@@ -260,60 +261,6 @@ GLOBAL_LIST_EMPTY(objectives) //PARIAH EDIT
else
explanation_text = "Free Objective"
-/datum/objective/hijack
- name = "hijack"
- explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console)."
- team_explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console). Leave no team member behind."
-
- /// Overrides the hijack speed of any antagonist datum it is on ONLY, no other datums are impacted.
- var/hijack_speed_override = 1
-
-/datum/objective/hijack/check_completion() // Requires all owners to escape.
- if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
- return FALSE
- var/list/datum/mind/owners = get_owners()
- for(var/datum/mind/M in owners)
- if(!considered_alive(M) || !SSshuttle.emergency.shuttle_areas[get_area(M.current)])
- return FALSE
- return SSshuttle.emergency.is_hijacked()
-
-/datum/objective/elimination
- name = "elimination"
- explanation_text = "Slaughter all loyalist crew aboard the shuttle. You, and any likeminded individuals, must be the only remaining people on the shuttle."
- team_explanation_text = "Slaughter all loyalist crew aboard the shuttle. You, and any likeminded individuals, must be the only remaining people on the shuttle. Leave no team member behind."
-
-/datum/objective/elimination/check_completion()
- if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
- return FALSE
- var/list/datum/mind/owners = get_owners()
- for(var/datum/mind/M in owners)
- if(!considered_alive(M, enforce_human = FALSE) || !SSshuttle.emergency.shuttle_areas[get_area(M.current)])
- return FALSE
- return SSshuttle.emergency.elimination_hijack()
-
-/datum/objective/elimination/highlander
- name="highlander elimination"
- explanation_text = "Escape on the shuttle alone. Ensure that nobody else makes it out."
-
-/datum/objective/elimination/highlander/check_completion()
- if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
- return FALSE
- var/list/datum/mind/owners = get_owners()
- for(var/datum/mind/M in owners)
- if(!considered_alive(M, enforce_human = FALSE) || !SSshuttle.emergency.shuttle_areas[get_area(M.current)])
- return FALSE
- return SSshuttle.emergency.elimination_hijack(filter_by_human = FALSE, solo_hijack = TRUE)
-
-/datum/objective/purge/check_completion()
- if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
- return TRUE
- for(var/mob/living/player in GLOB.player_list)
- if((get_area(player) in SSshuttle.emergency.shuttle_areas) && player.mind && player.stat != DEAD && ishuman(player))
- var/mob/living/carbon/human/H = player
- if(H.dna.species.id != SPECIES_HUMAN)
- return FALSE
- return TRUE
-
/// Escape. Should not be given to anyone straight up. Exists for Escape with Identity.
/datum/objective/escape
name = "escape"
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 2ef1bbb42358..1ae5dd7ffc20 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -173,13 +173,13 @@
return
message.answered = answer_index
message.answer_callback.InvokeAsync()
- if ("callShuttle")
+ if ("startEvac")
if (!authenticated(usr) || syndicate)
return
var/reason = trim(params["reason"], MAX_MESSAGE_LEN)
- if (length(reason) < CALL_SHUTTLE_REASON_LENGTH)
+ if (length(reason) < EVAC_REASON_LENGTH)
return
- SSshuttle.requestEvac(usr, reason)
+ SSevacuation.request_evacuation(usr, reason, params["evacController"])
post_status("shuttle")
if ("changeSecurityLevel")
if (!authenticated_as_silicon_or_captain(usr))
@@ -275,13 +275,13 @@
if (bank_account.account_balance < shuttle.credit_cost)
return
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_PURCHASED
- for(var/datum/round_event_control/shuttle_insurance/insurance_event in SSevents.control)
- insurance_event.weight *= 20
SSshuttle.unload_preview()
- SSshuttle.existing_shuttle = SSshuttle.emergency
+ SSshuttle.existing_shuttle = GLOB.emergency_shuttle
SSshuttle.action_load(shuttle, replace = TRUE)
bank_account.adjust_money(-shuttle.credit_cost)
+ SSevacuation.escape_shuttle_replaced()
+
var/purchaser_name = (obj_flags & EMAGGED) ? scramble_message_replace_chars("AUTHENTICATION FAILURE: CVE-2018-17107", 60) : usr.real_name
minor_announce("[purchaser_name] has purchased [shuttle.name] for [shuttle.credit_cost] credits.[shuttle.extra_desc ? " [shuttle.extra_desc]" : ""]" , "Shuttle Purchase")
@@ -289,11 +289,11 @@
log_shuttle("[key_name(usr)] has purchased [shuttle.name].")
SSblackbox.record_feedback("text", "shuttle_purchase", 1, shuttle.name)
state = STATE_MAIN
- if ("recallShuttle")
+ if ("cancelEvac")
// AIs cannot recall the shuttle
if (!authenticated(usr) || issilicon(usr) || syndicate)
return
- SSshuttle.cancelEvac(usr)
+ SSevacuation.request_cancel(usr, params["evacController"])
if ("requestNukeCodes")
if (!authenticated_as_non_silicon_captain(usr))
return
@@ -509,21 +509,18 @@
data["canBuyShuttles"] = can_buy_shuttles(user)
data["canMakeAnnouncement"] = FALSE
data["canMessageAssociates"] = FALSE
- data["canRecallShuttles"] = !issilicon(user)
+ data["canRecallEvac"] = !issilicon(user)
data["canRequestNuke"] = FALSE
data["canSendToSectors"] = FALSE
data["canSetAlertLevel"] = FALSE
data["canToggleEmergencyAccess"] = FALSE
data["importantActionReady"] = COOLDOWN_FINISHED(src, important_action_cooldown)
- data["shuttleCalled"] = FALSE
- data["shuttleLastCalled"] = FALSE
+ data["evacStarted"] = FALSE
+ data["evacLastCalled"] = FALSE
data["aprilFools"] = SSevents.holidays && SSevents.holidays[APRIL_FOOLS]
data["alertLevel"] = get_security_level()
data["authorizeName"] = authorize_name
data["canLogOut"] = !issilicon(user)
- data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user)
- if(syndicate)
- data["shuttleCanEvacOrFailReason"] = "You cannot summon the shuttle from this console!"
if (authenticated_as_non_silicon_captain(user))
data["canMessageAssociates"] = TRUE
@@ -552,14 +549,9 @@
else if(syndicate)
data["canMakeAnnouncement"] = TRUE
- if (SSshuttle.emergency.mode != SHUTTLE_IDLE && SSshuttle.emergency.mode != SHUTTLE_RECALL)
- data["shuttleCalled"] = TRUE
- data["shuttleRecallable"] = SSshuttle.canRecall() || syndicate
+ data["canRequestEvac"] = !syndicate
+ data["evacOptions"] = SSevacuation.get_evac_ui_data(user)
- if (SSshuttle.emergencyCallAmount)
- data["shuttleCalledPreviously"] = TRUE
- if (SSshuttle.emergency_last_call_loc)
- data["shuttleLastCalled"] = format_text(SSshuttle.emergency_last_call_loc.name)
if (STATE_MESSAGES)
data["messages"] = list()
@@ -573,6 +565,7 @@
"possibleAnswers" = message.possible_answers,
))
if (STATE_BUYING_SHUTTLE)
+ data["shuttleToReplace"] = SSevacuation.get_customizable_shuttles()
var/datum/bank_account/bank_account = SSeconomy.department_accounts_by_id[ACCOUNT_CAR]
var/list/shuttles = list()
@@ -611,7 +604,7 @@
/obj/machinery/computer/communications/ui_static_data(mob/user)
return list(
- "callShuttleReasonMinLength" = CALL_SHUTTLE_REASON_LENGTH,
+ "callShuttleReasonMinLength" = EVAC_REASON_LENGTH,
"maxStatusLineLength" = MAX_STATUS_LINE_LENGTH,
"maxMessageLength" = MAX_MESSAGE_LEN,
)
@@ -669,8 +662,8 @@
if (!has_access)
return FALSE
- if (SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
- return "The shuttle is already in transit."
+ //if (SSevacuation.controller.state != EVACUATION_STATE_IDLE)
+ // return "The shuttle is already in transit."
if (SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_PURCHASED)
return "A replacement shuttle has already been purchased."
if (SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_FORCED)
@@ -755,7 +748,7 @@
/obj/machinery/computer/communications/Destroy()
UNSET_TRACKING(__TYPE__)
UNSET_TRACKING(TRACKING_KEY_SHUTTLE_CALLER)
- SSshuttle.autoEvac()
+ SSevacuation.trigger_auto_evac(EVACUATION_REASON_CONSOLE_DESTROYED)
return ..()
/// Override the cooldown for special actions
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index bbe099954574..919434033d90 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -193,7 +193,7 @@ DEFINE_INTERACTABLE(/obj/machinery/door)
* * source The datum source of the signal
* * new_level The new security level that is in effect
*/
-/obj/machinery/door/proc/check_security_level(datum/source, new_level)
+/obj/machinery/door/proc/check_security_level(datum/source, old_level, new_level)
SIGNAL_HANDLER
if(new_level <= SEC_LEVEL_BLUE)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 0855ae5b1dba..3625ce9d5726 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -176,7 +176,7 @@ DEFINE_INTERACTABLE(/obj/machinery/firealarm)
* * source The datum source of the signal
* * new_level The new security level that is in effect
*/
-/obj/machinery/firealarm/proc/check_security_level(datum/source, new_level)
+/obj/machinery/firealarm/proc/check_security_level(datum/source, old_level, new_level)
SIGNAL_HANDLER
if(is_station_level(z))
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index 360c94d96b9a..69d230c7c1b3 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -322,7 +322,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/evac, 32)
return PROCESS_KILL
if(SD_EMERGENCY)
- return display_shuttle_status(SSshuttle.emergency)
+ . = SSevacuation.emergency_status_display_process(src)
+ if(!.)
+ set_messages("eva?#","")
+ return PROCESS_KILL
+ set_messages(copytext(.[1], CHARS_PER_LINE+1), copytext(.[2], CHARS_PER_LINE+1))
if(SD_MESSAGE)
return PROCESS_KILL
@@ -334,7 +338,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/evac, 32)
/obj/machinery/status_display/evac/examine(mob/user)
. = ..()
if(current_mode == SD_EMERGENCY)
- . += examine_shuttle(user, SSshuttle.emergency)
+ . += SSevacuation.status_display_examine(user, src)
else if(!message1 && !message2)
. += "The display is blank."
diff --git a/code/game/world.dm b/code/game/world.dm
index bd7abbdd4294..68145a5bc295 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -43,7 +43,7 @@ GLOBAL_VAR(restart_counter)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
- GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_econ_log = GLOB.world_shuttle_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
+ GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_econ_log = GLOB.world_shuttle_log = GLOB.world_evacuation_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
#ifdef REFERENCE_DOING_IT_LIVE
GLOB.harddel_log = GLOB.world_game_log
#endif
@@ -158,6 +158,7 @@ GLOBAL_VAR(restart_counter)
GLOB.world_paper_log = "[GLOB.log_directory]/paper.log"
GLOB.tgui_log = "[GLOB.log_directory]/tgui.log"
GLOB.world_shuttle_log = "[GLOB.log_directory]/shuttle.log"
+ GLOB.world_evacuation_log = "[GLOB.log_directory]/evacuation.log"
GLOB.filter_log = "[GLOB.log_directory]/filters.log"
GLOB.demo_log = "[GLOB.log_directory]/demo.log"
@@ -186,6 +187,7 @@ GLOBAL_VAR(restart_counter)
start_log(GLOB.world_job_debug_log)
start_log(GLOB.tgui_log)
start_log(GLOB.world_shuttle_log)
+ start_log(GLOB.world_evacuation_log)
var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM") + ".yml")
GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 9a58e38085e3..4c09780f82c2 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -30,6 +30,7 @@
dat += ""
if(SSticker.IsRoundInProgress())
dat += "(Game Mode Panel) "
+ dat += "(Evacuation Panel) "
dat += {"
Create Object
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 2322eac3cc33..fc3cded38d46 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -63,8 +63,8 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/jumptoturf, /*allows us to jump to a specific turf*/
/client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
/client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/
- /client/proc/admin_disable_shuttle, /*allows us to disable the emergency shuttle admin-wise so that it cannot be called*/
- /client/proc/admin_enable_shuttle, /*undoes the above*/
+ /client/proc/admin_disable_evac, /*allows us to disable the emergency shuttle admin-wise so that it cannot be called*/
+ /client/proc/admin_enable_evac, /*undoes the above*/
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
/client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/
diff --git a/code/modules/admin/check_antagonists.dm b/code/modules/admin/check_antagonists.dm
index fe6a13643bd9..140ec05e6370 100644
--- a/code/modules/admin/check_antagonists.dm
+++ b/code/modules/admin/check_antagonists.dm
@@ -124,19 +124,9 @@
tgui_alert(usr, "The game hasn't started yet!")
return
var/list/dat = list("Round Status
Round Status
")
- if(IS_DYNAMIC_GAME_MODE) // Currently only used by dynamic. If more start using this, find a better way.
- dat += "Game Mode Panel "
+ dat += "Game Mode Panel "
dat += "Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)] "
- dat += "Emergency shuttle "
- if(EMERGENCY_IDLE_OR_RECALLED)
- dat += "Call Shuttle "
- else
- var/timeleft = SSshuttle.emergency.timeLeft()
- if(SSshuttle.emergency.mode == SHUTTLE_CALL)
- dat += "ETA: [(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")] "
- dat += "Send Back "
- else
- dat += "ETA: [(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")] "
+ dat += "Evacuation Panel "
dat += "End Round Now "
if(SSticker.delay_end)
dat += "End Round Normally "
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index a6e343229ac1..23650a0573d0 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -98,16 +98,6 @@
body.key = C.key
new /obj/effect/temp_visual/gravpush(get_turf(body))
-// ----------- Emergency Shuttle Balloon
-/obj/effect/fun_balloon/sentience/emergency_shuttle
- name = "shuttle sentience fun balloon"
- var/trigger_time = 60
-
-/obj/effect/fun_balloon/sentience/emergency_shuttle/check()
- . = FALSE
- if(SSshuttle.emergency && (SSshuttle.emergency.timeLeft() <= trigger_time) && (SSshuttle.emergency.mode == SHUTTLE_CALL))
- . = TRUE
-
// ----------- Scatter Balloon
/obj/effect/fun_balloon/scatter
name = "scatter fun balloon"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 109780f5efe6..6a44fcb920c7 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -93,50 +93,25 @@
return
SSticker.mode.admin_panel()
- else if(href_list["call_shuttle"])
+ else if(href_list["evac_panel"])
if(!check_rights(R_ADMIN))
return
+ SSevacuation.admin_panel()
-
- switch(href_list["call_shuttle"])
- if("1")
- if(EMERGENCY_AT_LEAST_DOCKED)
- return
- SSshuttle.emergency.request()
- log_admin("[key_name(usr)] called the Emergency Shuttle.")
- message_admins(span_adminnotice("[key_name_admin(usr)] called the Emergency Shuttle to the station."))
-
- if("2")
- if(EMERGENCY_AT_LEAST_DOCKED)
- return
- switch(SSshuttle.emergency.mode)
- if(SHUTTLE_CALL)
- SSshuttle.emergency.cancel()
- log_admin("[key_name(usr)] sent the Emergency Shuttle back.")
- message_admins(span_adminnotice("[key_name_admin(usr)] sent the Emergency Shuttle back."))
- else
- SSshuttle.emergency.cancel()
- log_admin("[key_name(usr)] called the Emergency Shuttle.")
- message_admins(span_adminnotice("[key_name_admin(usr)] called the Emergency Shuttle to the station."))
-
-
-
- else if(href_list["edit_shuttle_time"])
- if(!check_rights(R_SERVER))
+ else if(href_list["evac_controller"])
+ if(!check_rights(R_ADMIN))
return
+ SSevacuation.panel_act(href_list)
- var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num|null
- if(!timer)
- return
- SSshuttle.emergency.setTimer(timer SECONDS)
- log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.")
- minor_announce("The emergency shuttle will reach its destination in [DisplayTimeText(timer SECONDS)].")
- message_admins(span_adminnotice("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds."))
else if(href_list["trigger_centcom_recall"])
if(!check_rights(R_ADMIN))
return
- usr.client.trigger_centcom_recall()
+ usr.client.trigger_centcom_recall(href_list["trigger_centcom_recall"])
+
+ else if(href_list["start_evac"])
+ if(!check_rights(R_ADMIN))
+ return
else if(href_list["move_shuttle"])
if(!check_rights(R_ADMIN))
@@ -156,6 +131,7 @@
return
shuttle_console.admin_controlled = !shuttle_console.admin_controlled
to_chat(usr, "[shuttle_console] was [shuttle_console.admin_controlled ? "locked" : "unlocked"].", confidential = TRUE)
+
else if(href_list["delay_round_end"])
if(!check_rights(R_SERVER))
return
@@ -178,6 +154,7 @@
log_admin("[key_name(usr)] delayed the round end for reason: [SSticker.admin_delay_notice]")
message_admins("[key_name_admin(usr)] delayed the round end for reason: [SSticker.admin_delay_notice]")
+
else if(href_list["undelay_round_end"])
if(!check_rights(R_SERVER))
return
@@ -193,6 +170,7 @@
message_admins("[key_name_admin(usr)] undelayed the round end. You must now manually Reboot World to start the next shift.")
else
message_admins("[key_name_admin(usr)] undelayed the round end.")
+
else if(href_list["end_round"])
if(!check_rights(R_ADMIN))
return
diff --git a/code/modules/admin/verbs/admin.dm b/code/modules/admin/verbs/admin.dm
index 648b7329611b..8198e8ed5eef 100644
--- a/code/modules/admin/verbs/admin.dm
+++ b/code/modules/admin/verbs/admin.dm
@@ -70,7 +70,7 @@
msg += ""
src << browse(msg.Join(), "window=Player_playtime_check")
-/client/proc/trigger_centcom_recall()
+/client/proc/trigger_centcom_recall(identifier)
if(!check_rights(R_ADMIN))
return
var/message = pick(GLOB.admiral_messages)
@@ -81,7 +81,7 @@
message_admins("[key_name_admin(usr)] triggered a CentCom recall, with the admiral message of: [message]")
log_game("[key_name(usr)] triggered a CentCom recall, with the message of: [message]")
- SSshuttle.centcom_recall(SSshuttle.emergency.timer, message)
+ SSevacuation.centcom_recall(identifier, message)
/datum/admins/proc/cmd_show_exp_panel(client/client_to_check)
if(!check_rights(R_ADMIN))
diff --git a/code/modules/admin/verbs/adminevents.dm b/code/modules/admin/verbs/adminevents.dm
index 47516da16328..099a840d017b 100644
--- a/code/modules/admin/verbs/adminevents.dm
+++ b/code/modules/admin/verbs/adminevents.dm
@@ -149,12 +149,13 @@
/client/proc/admin_call_shuttle()
set category = "Admin.Events"
- set name = "Call Shuttle"
+ set name = "Start Evacuation"
- if(EMERGENCY_AT_LEAST_DOCKED)
+ if(!check_rights(R_ADMIN))
return
- if(!check_rights(R_ADMIN))
+ var/identifier = tgui_input_list(usr, "Choose evacuation option", "Evacuation", SSevacuation.get_controllers_names())
+ if(identifier == null)
return
var/confirm = tgui_alert(usr, "You sure?", "Confirm", list("Yes", "Yes (No Recall)", "No"))
@@ -162,89 +163,68 @@
if(null, "No")
return
if("Yes (No Recall)")
- SSshuttle.admin_emergency_no_recall = TRUE
- SSshuttle.emergency.mode = SHUTTLE_IDLE
+ SSevacuation.block_cancel(identifier)
- SSshuttle.emergency.request()
+ SSevacuation.request_evacuation(usr, null, identifier, admin = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Call Shuttle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- log_admin("[key_name(usr)] admin-called the emergency shuttle.")
- message_admins(span_adminnotice("[key_name_admin(usr)] admin-called the emergency shuttle[confirm == "Yes (No Recall)" ? " (non-recallable)" : ""]."))
+ log_admin("[key_name(usr)] admin-started the evacuation.")
+ message_admins(span_adminnotice("[key_name_admin(usr)] admin-started the evacuation[confirm == "Yes (No Recall)" ? " (non-recallable)" : ""]."))
return
/client/proc/admin_cancel_shuttle()
set category = "Admin.Events"
- set name = "Cancel Shuttle"
+ set name = "Cancel Evacuation"
+
if(!check_rights(0))
return
- if(tgui_alert(usr, "You sure?", "Confirm", list("Yes", "No")) != "Yes")
- return
- if(SSshuttle.admin_emergency_no_recall)
- SSshuttle.admin_emergency_no_recall = FALSE
+ var/identifier = tgui_input_list(usr, "Choose evacuation option", "Evacuation", SSevacuation.get_controllers_names(TRUE))
+ if(identifier == null)
+ return
- if(EMERGENCY_AT_LEAST_DOCKED)
+ if(tgui_alert(usr, "You sure?", "Confirm", list("Yes", "No")) != "Yes")
return
- SSshuttle.emergency.cancel()
+ SSevacuation.unblock_cancel(identifier)
+ SSevacuation.request_cancel(usr, identifier)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Cancel Shuttle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] admin-recalled the emergency shuttle.")
message_admins(span_adminnotice("[key_name_admin(usr)] admin-recalled the emergency shuttle."))
return
-/client/proc/admin_disable_shuttle()
+/client/proc/admin_disable_evac()
set category = "Admin.Events"
- set name = "Disable Shuttle"
+ set name = "Disable Evacuation"
if(!check_rights(R_ADMIN))
return
- if(SSshuttle.emergency.mode == SHUTTLE_DISABLED)
- to_chat(usr, span_warning("Error, shuttle is already disabled."))
+ var/identifier = tgui_input_list(usr, "Choose evacuation option", "Evacuation", SSevacuation.get_controllers_names())
+ if(identifier == null)
return
if(tgui_alert(usr, "You sure?", "Confirm", list("Yes", "No")) != "Yes")
return
- message_admins(span_adminnotice("[key_name_admin(usr)] disabled the shuttle."))
+ message_admins(span_adminnotice("[key_name_admin(usr)] disabled the [identifier] evacuation option."))
+ SSevacuation.disable_evacuation(identifier)
- SSshuttle.last_mode = SSshuttle.emergency.mode
- SSshuttle.last_call_time = SSshuttle.emergency.timeLeft(1)
- SSshuttle.admin_emergency_no_recall = TRUE
- SSshuttle.emergency.setTimer(0)
- SSshuttle.emergency.mode = SHUTTLE_DISABLED
- priority_announce(
- "Warning: Emergency Shuttle uplink failure, shuttle disabled until further notice.",
- "LRSV Icarus Announcement",
- "Emergency Shuttle Uplink Alert",
- 'sound/misc/announce_dig.ogg'
- )
-
-/client/proc/admin_enable_shuttle()
+/client/proc/admin_enable_evac()
set category = "Admin.Events"
- set name = "Enable Shuttle"
+ set name = "Enable Evacuation"
if(!check_rights(R_ADMIN))
return
- if(SSshuttle.emergency.mode != SHUTTLE_DISABLED)
- to_chat(usr, span_warning("Error, shuttle not disabled."))
+ var/identifier = tgui_input_list(usr, "Choose evacuation option", "Evacuation", SSevacuation.get_controllers_names())
+ if(identifier == null)
return
if(tgui_alert(usr, "You sure?", "Confirm", list("Yes", "No")) != "Yes")
return
- message_admins(span_adminnotice("[key_name_admin(usr)] enabled the emergency shuttle."))
- SSshuttle.admin_emergency_no_recall = FALSE
- SSshuttle.emergency_no_recall = FALSE
- if(SSshuttle.last_mode == SHUTTLE_DISABLED) //If everything goes to shit, fix it.
- SSshuttle.last_mode = SHUTTLE_IDLE
-
- SSshuttle.emergency.mode = SSshuttle.last_mode
- if(SSshuttle.last_call_time < 10 SECONDS && SSshuttle.last_mode != SHUTTLE_IDLE)
- SSshuttle.last_call_time = 10 SECONDS //Make sure no insta departures.
- SSshuttle.emergency.setTimer(SSshuttle.last_call_time)
- priority_announce("Warning: Emergency Shuttle uplink reestablished, shuttle enabled.", "LRSV Icarus Announcement", "Emergency Shuttle Uplink Alert", 'sound/misc/announce_dig.ogg')
+ SSevacuation.enable_evacuation(identifier)
/client/proc/toggle_nuke(obj/machinery/nuclearbomb/N in INSTANCES_OF(/obj/machinery/nuclearbomb))
set category = "Admin.Events"
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 17cb13dda57d..28bfb3856203 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -255,10 +255,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
round_state = "Round has not started"
if(GAME_STATE_PLAYING)
round_state = "Round is ongoing."
- if(SSshuttle.emergency.getModeStr())
- round_state += "\n[SSshuttle.emergency.getModeStr()]: [SSshuttle.emergency.getTimerStr()]"
- if(SSticker.emergency_reason)
- round_state += ", Shuttle call reason: [SSticker.emergency_reason]"
+ round_state += SSevacuation.get_discord_status()
if(GAME_STATE_FINISHED)
round_state = "Round has ended"
var/list/admin_counts = get_admin_counts(R_BAN)
diff --git a/code/modules/admin/verbs/highlander_datum.dm b/code/modules/admin/verbs/highlander_datum.dm
index bd0a60a848da..89e4ed301a0a 100644
--- a/code/modules/admin/verbs/highlander_datum.dm
+++ b/code/modules/admin/verbs/highlander_datum.dm
@@ -36,7 +36,6 @@ GLOBAL_DATUM(highlander_controller, /datum/highlander_controller)
robot.gib()
continue
robot.make_scottish()
- addtimer(CALLBACK(SSshuttle.emergency, TYPE_PROC_REF(/obj/docking_port/mobile/emergency, request), null, 1), 50)
/datum/highlander_controller/Destroy(force, ...)
. = ..()
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 51157258efe3..278ad27ff032 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -26,10 +26,6 @@ GLOBAL_LIST_EMPTY(antagonists)
var/antag_memory = ""
///typepath of moodlet that the mob will gain when granted this antagonist type.
var/antag_moodlet
- ///If these antags are alone when a shuttle elimination happens.
- var/can_elimination_hijack = ELIMINATION_NEUTRAL
- ///If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
- var/hijack_speed = 0
///The antag hud's icon file
var/hud_icon = 'icons/mob/huds/antag_hud.dmi'
///Name of the antag hud we provide to this mob.
@@ -384,14 +380,6 @@ GLOBAL_LIST_EMPTY(antagonists)
return
antag_memory = new_memo
-/**
- * Gets how fast we can hijack the shuttle, return 0 for can not hijack.
- * Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
- */
-/datum/antagonist/proc/hijack_speed()
- var/datum/objective/hijack/H = locate() in objectives
- return H?.hijack_speed_override || hijack_speed
-
/// Adds a HUD that will show you other members with the same antagonist.
/// If an antag typepath is passed to `antag_to_check`, will check that, otherwise will use the source type.
/datum/antagonist/proc/add_team_hud(mob/target, antag_to_check)
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index 90e8ccee9c9e..4142bd9896cc 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -64,7 +64,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
color = blobstrain.complementary_color
if(blob_core)
blob_core.update_appearance()
- SSshuttle.registerHostileEnvironment(src)
+ SSevacuation.add_evacuation_blocker(src)
. = ..()
START_PROCESSING(SSobj, src)
@@ -219,7 +219,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
GLOB.overminds -= src
QDEL_LIST_ASSOC_VAL(strain_choices)
- SSshuttle.clearHostileEnvironment(src)
+ SSevacuation.remove_evacuation_blocker(src)
STOP_PROCESSING(SSobj, src)
return ..()
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 8932e9763fe4..551bd96c59c6 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -4,7 +4,6 @@
job_rank = ROLE_BROTHER
var/special_role = ROLE_BROTHER
antag_hud_name = "brother"
- hijack_speed = 0.5
ui_name = "AntagInfoBrother"
suicide_cry = "FOR MY BROTHER!!"
var/datum/team/brother_team/team
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 8eb234e63055..9f3e75a5ba40 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -12,7 +12,6 @@
antagpanel_category = "Changeling"
job_rank = ROLE_CHANGELING
antag_hud_name = "changeling"
- hijack_speed = 0.5
ui_name = "AntagInfoChangeling"
suicide_cry = "FOR THE HIVE!!"
/// Whether to give this changeling objectives or not
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 71f49aa2df7c..7a3faba055c9 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -513,13 +513,15 @@ Striking a noncultist, however, will tear their flesh."}
list_reagents = list(/datum/reagent/fuel/unholywater = 50)
///how many times can the shuttle be cursed?
-#define MAX_SHUTTLE_CURSES 3
+#define MAX_EVAC_CURSES 3
///if the max number of shuttle curses are used within this duration, the entire cult gets an achievement
#define SHUTTLE_CURSE_OMFG_TIMESPAN 10 SECONDS
+///how long is the evacuation delayed by?
+#define EVAC_DELAY 3 MINUTES
/obj/item/shuttle_curse
name = "cursed orb"
- desc = "You peer within this smokey orb and glimpse terrible fates befalling the emergency escape shuttle. "
+ desc = "You peer within this smokey orb and glimpse terrible fates befalling people during the evacuation. "
icon = 'icons/obj/cult/items_and_weapons.dmi'
icon_state = "shuttlecurse"
///how many times has the shuttle been cursed so far?
@@ -535,58 +537,47 @@ Striking a noncultist, however, will tear their flesh."}
user.Paralyze(100)
to_chat(user, span_warning("A powerful force shoves you away from [src]!"))
return
- if(totalcurses >= MAX_SHUTTLE_CURSES)
+ if(totalcurses >= MAX_EVAC_CURSES)
to_chat(user, span_warning("You try to shatter the orb, but it remains as solid as a rock!"))
- to_chat(user, span_danger(span_big("It seems that the blood cult has exhausted its ability to curse the emergency escape shuttle. It would be unwise to create more cursed orbs or to continue to try to shatter this one.")))
+ to_chat(user, span_danger(span_big("It seems that the blood cult has exhausted its ability to curse the evacuation. It would be unwise to create more cursed orbs or to continue to try to shatter this one.")))
return
if(locate(/obj/narsie) in SSpoints_of_interest.narsies)
to_chat(user, span_warning("Nar'Sie is already on this plane, there is no delaying the end of all things."))
return
- if(SSshuttle.emergency.mode == SHUTTLE_CALL)
- var/cursetime = 3 MINUTES
- var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
- var/security_num = seclevel2num(get_security_level())
- var/set_coefficient = 1
+ var/identifier = SSevacuation.get_initiated_controller()
+ if(!identifier)
+ return
- if(totalcurses == 0)
- first_curse_time = world.time
+ SSevacuation.delay_evacuation(identifier, 3 MINUTES)
- switch(security_num)
- if(SEC_LEVEL_GREEN)
- set_coefficient = 1
- else
- set_coefficient = 0.5
-
- var/surplus = timer - (SSshuttle.emergency_call_time * set_coefficient)
- SSshuttle.emergency.setTimer(timer)
- if(surplus > 0)
- SSshuttle.block_recall(surplus)
- totalcurses++
- to_chat(user, span_danger("You shatter the orb! A dark essence spirals into the air, then disappears."))
- playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, TRUE)
-
- if(!remaining_curses)
- remaining_curses = strings(CULT_SHUTTLE_CURSE, "curse_announce")
-
- var/curse_message = pick_n_take(remaining_curses) || "Something has gone horrendously wrong..."
-
- curse_message += " The shuttle will be delayed by three minutes."
- priority_announce("[curse_message]", "LSRV Icarus Announcement", "System Failure", 'sound/misc/notice1.ogg')
- if(MAX_SHUTTLE_CURSES-totalcurses <= 0)
- to_chat(user, span_danger(span_big("You sense that the emergency escape shuttle can no longer be cursed. It would be unwise to create more cursed orbs.")))
- else if(MAX_SHUTTLE_CURSES-totalcurses == 1)
- to_chat(user, span_danger(span_big("You sense that the emergency escape shuttle can only be cursed one more time.")))
- else
- to_chat(user, span_danger(span_big("You sense that the emergency escape shuttle can only be cursed [MAX_SHUTTLE_CURSES-totalcurses] more times.")))
+ totalcurses++
+ to_chat(user, span_danger("You shatter the orb! A dark essence spirals into the air, then disappears."))
+ playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, TRUE)
- if(totalcurses >= MAX_SHUTTLE_CURSES && (world.time < first_curse_time + SHUTTLE_CURSE_OMFG_TIMESPAN))
- var/omfg_message = pick_list(CULT_SHUTTLE_CURSE, "omfg_announce") || "LEAVE US ALONE!"
- addtimer(CALLBACK(GLOBAL_PROC,PROC_REF(priority_announce),omfg_message,"Daedalus Industries Shuttle Dispatch","FUCK OFF",'sound/misc/notice1.ogg'), rand(2 SECONDS, 6 SECONDS))
+ if(!remaining_curses)
+ remaining_curses = strings(CULT_SHUTTLE_CURSE, "curse_announce")
- qdel(src)
+ var/curse_message = pick_n_take(remaining_curses) || "Something has gone horrendously wrong..."
+
+ curse_message += " The shuttle will be delayed by three minutes."
+ priority_announce("[curse_message]", "LSRV Icarus Announcement", "System Failure", 'sound/misc/notice1.ogg')
+ if(MAX_EVAC_CURSES-totalcurses <= 0)
+ to_chat(user, span_danger(span_big("You sense that the emergency escape shuttle can no longer be cursed. It would be unwise to create more cursed orbs.")))
+ else if(MAX_EVAC_CURSES-totalcurses == 1)
+ to_chat(user, span_danger(span_big("You sense that the emergency escape shuttle can only be cursed one more time.")))
+ else
+ to_chat(user, span_danger(span_big("You sense that the emergency escape shuttle can only be cursed [MAX_EVAC_CURSES-totalcurses] more times.")))
+
+ if(totalcurses >= MAX_EVAC_CURSES && (world.time < first_curse_time + SHUTTLE_CURSE_OMFG_TIMESPAN))
+ var/omfg_message = pick_list(CULT_SHUTTLE_CURSE, "omfg_announce") || "LEAVE US ALONE!"
+ addtimer(CALLBACK(GLOBAL_PROC,PROC_REF(priority_announce),omfg_message,"Daedalus Industries Shuttle Dispatch","FUCK OFF",'sound/misc/notice1.ogg'), rand(2 SECONDS, 6 SECONDS))
+
+ qdel(src)
-#undef MAX_SHUTTLE_CURSES
+#undef SHUTTLE_CURSE_OMFG_TIMESPAN
+#undef EVAC_DELAY
+#undef MAX_EVAC_CURSES
/obj/item/cult_shift
name = "veil shifter"
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index bd62e44e694e..5db631686047 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -5,7 +5,6 @@
/datum/antagonist/ert
name = "Emergency Response Officer"
- can_elimination_hijack = ELIMINATION_PREVENT
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
suicide_cry = "FOR NANOTRASEN!!"
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index 19846ddb2e44..1f920167bf6b 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -19,7 +19,6 @@
ui_name = "AntagInfoHeretic"
job_rank = ROLE_HERETIC
antag_hud_name = "heretic"
- hijack_speed = 0.5
suicide_cry = "THE MANSUS SMILES UPON ME!!"
preview_outfit = /datum/outfit/heretic
/// Whether we give this antagonist objectives on gain.
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index a8af18b1b752..6949755ba474 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -3,7 +3,6 @@
var/obj/item/claymore/highlander/sword
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
- can_elimination_hijack = ELIMINATION_ENABLED
suicide_cry = "FOR SCOTLAND!!" // If they manage to lose their no-drop stuff somehow
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
@@ -27,9 +26,6 @@
steal_objective.owner = owner
steal_objective.set_target(new /datum/objective_item/steal/nukedisc)
objectives += steal_objective
- var/datum/objective/elimination/highlander/elimination_objective = new
- elimination_objective.owner = owner
- objectives += elimination_objective
/datum/antagonist/highlander/on_gain()
forge_objectives()
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index cfbad105c32b..65dc9cd8b449 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -511,7 +511,7 @@ GLOBAL_VAR(station_nuke_source)
off_station = NUKE_MISS_STATION
if(off_station < NUKE_MISS_STATION)
- SSshuttle.registerHostileEnvironment(src)
+ SSevacuation.add_evacuation_blocker(src)
SSshuttle.lockdown = TRUE
//Cinematic
GLOB.station_nuke_source = off_station
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 788924a11a25..7205e12f427a 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -5,7 +5,6 @@
job_rank = ROLE_OPERATIVE
antag_hud_name = "synd"
show_to_ghosts = TRUE
- hijack_speed = 2 //If you can't take out the station, take the shuttle instead.
suicide_cry = "FOR THE SYNDICATE!!"
var/datum/team/nuclear/nuke_team
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
@@ -338,12 +337,9 @@
/datum/team/nuclear/proc/disk_rescued()
for(var/obj/item/disk/nuclear/D in SSpoints_of_interest.real_nuclear_disks)
//If emergency shuttle is in transit disk is only safe on it
- if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
- if(!SSshuttle.emergency.is_in_shuttle_bounds(D))
- return FALSE
- //If shuttle escaped check if it's on centcom side
- else if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME)
- if(!D.onCentCom())
+ if(SSevacuation.evacuation_finished())
+ var/list/area/evac_areas = SSevacuation.get_endgame_areas()
+ if(!D.onCentCom() && !evac_areas[get_area(D)])
return FALSE
else //Otherwise disk is safe when on station
var/turf/T = get_turf(D)
@@ -359,7 +355,6 @@
return TRUE
/datum/team/nuclear/proc/get_result()
- var/evacuation = EMERGENCY_ESCAPED_OR_ENDGAMED
var/disk_rescued = disk_rescued()
var/syndies_didnt_escape = !syndies_escaped()
var/station_was_nuked = GLOB.station_was_nuked
@@ -375,13 +370,13 @@
return NUKE_RESULT_WRONG_STATION
else if (!disk_rescued && !station_was_nuked && station_nuke_source && syndies_didnt_escape)
return NUKE_RESULT_WRONG_STATION_DEAD
- else if ((disk_rescued && evacuation) && operatives_dead())
+ else if (disk_rescued && operatives_dead())
return NUKE_RESULT_CREW_WIN_SYNDIES_DEAD
else if (disk_rescued)
return NUKE_RESULT_CREW_WIN
else if (!disk_rescued && operatives_dead())
return NUKE_RESULT_DISK_LOST
- else if (!disk_rescued && evacuation)
+ else if (!disk_rescued)
return NUKE_RESULT_DISK_STOLEN
else
return //Undefined result
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index 6d37eb507272..5262b495c913 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -5,7 +5,6 @@
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
suicide_cry = "FOR ME MATEYS!!"
- hijack_speed = 2 // That is without doubt the worst pirate I have ever seen.
var/datum/team/pirate/crew
/datum/antagonist/pirate/greet()
diff --git a/code/modules/antagonists/revolution/enemy_of_the_state.dm b/code/modules/antagonists/revolution/enemy_of_the_state.dm
index e42e5d75b558..b9ff1364b6db 100644
--- a/code/modules/antagonists/revolution/enemy_of_the_state.dm
+++ b/code/modules/antagonists/revolution/enemy_of_the_state.dm
@@ -7,7 +7,6 @@
name = "\improper Enemy of the State"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
- hijack_speed = 2 //not like they have much to do
suicide_cry = "FOR THE ETERNAL REVOLUTION!!"
/datum/antagonist/enemy_of_the_state/proc/forge_objectives()
@@ -17,11 +16,6 @@
exile_choice.objective_name = "Choice"
objectives += exile_choice
- var/datum/objective/hijack/hijack_choice = new
- hijack_choice.owner = owner
- hijack_choice.objective_name = "Choice"
- objectives += hijack_choice
-
/datum/antagonist/enemy_of_the_state/on_gain()
owner.special_role = "exiled headrev"
forge_objectives()
@@ -45,23 +39,16 @@
//needs to complete only one objective, not all
var/option_chosen = FALSE
- var/badass = FALSE
if(objectives.len)
report += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
option_chosen = TRUE
- if(istype(objective, /datum/objective/hijack))
- badass = TRUE
break
if(objectives.len == 0 || option_chosen)
- if(badass)
- report += "Major [name] Victory"
- report += "[name] chose the badass option, and hijacked the shuttle!"
- else
- report += "Minor [name] Victory"
- report += "[name] has survived as an exile!"
+ report += "[name] Victory"
+ report += "[name] has survived as an exile!"
else
report += "The [name] has failed!"
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index f8c224a73570..0ffe9d7a2226 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -414,7 +414,7 @@
else
return
- SSshuttle.clearHostileEnvironment(src)
+ SSevacuation.remove_evacuation_blocker(src)
save_members()
var/charter_given = FALSE
diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm
index 959fada29615..9c136d3f499e 100644
--- a/code/modules/antagonists/space_ninja/space_ninja.dm
+++ b/code/modules/antagonists/space_ninja/space_ninja.dm
@@ -3,7 +3,6 @@
antagpanel_category = "Space Ninja"
job_rank = ROLE_NINJA
antag_hud_name = "space_ninja"
- hijack_speed = 1
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
suicide_cry = "FOR THE SPIDER CLAN!!"
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index d9e9c24f65b6..88e7255f9b52 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -4,7 +4,6 @@
antagpanel_category = "Traitor"
job_rank = ROLE_TRAITOR
antag_hud_name = "traitor"
- hijack_speed = 0.5 //10 seconds per hijack stage by default
ui_name = "AntagInfoTraitor"
suicide_cry = "FOR THE SYNDICATE!!"
preview_outfit = /datum/outfit/traitor
@@ -12,8 +11,6 @@
var/should_give_codewords = FALSE
///give this traitor an uplink?
var/give_uplink = TRUE
- ///if TRUE, this traitor will always get hijacking as their final objective
- var/is_hijacker = FALSE
///the name of the antag flavor this traitor has.
var/employer
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index 262ea29b922c..23817f281001 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -273,7 +273,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
timing = FALSE
QDEL_NULL(countdown)
STOP_PROCESSING(SSfastprocess, src)
- SSshuttle.clearHostileEnvironment(src)
+ SSevacuation.remove_evacuation_blocker(src)
SSmapping.remove_nuke_threat(src)
set_security_level("red")
for(var/mob/living/silicon/robot/borg in owner?.connected_robots)
@@ -293,7 +293,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
timing = TRUE
countdown.start()
START_PROCESSING(SSfastprocess, src)
- SSshuttle.registerHostileEnvironment(src)
+ SSevacuation.add_evacuation_blocker(src)
SSmapping.add_nuke_threat(src) //This causes all blue "circuit" tiles on the map to change to animated red icon state.
for(var/mob/living/silicon/robot/borg in owner.connected_robots)
borg.lamp_doom = TRUE
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 82caf79143d5..bf6f8d819a95 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -2,13 +2,10 @@
name = "\improper Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
- hijack_speed = 2 //You literally are here to do nothing else. Might as well be fast about it.
suicide_cry = "HAHAHAHAHA!!"
/datum/antagonist/wishgranter/proc/forge_objectives()
- var/datum/objective/hijack/hijack = new
- hijack.owner = owner
- objectives += hijack
+ CRASH("Tried to forge wishgranter objective.")
/datum/antagonist/wishgranter/on_gain()
owner.special_role = "Avatar of the Wish Granter"
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 354986b7a3ee..d85e6a53aa2a 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -7,7 +7,6 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
antagpanel_category = "Wizard"
job_rank = ROLE_WIZARD
antag_hud_name = "wizard"
- hijack_speed = 0.5
ui_name = "AntagInfoWizard"
suicide_cry = "FOR THE FEDERATION!!"
preview_outfit = /datum/outfit/wizard
diff --git a/code/modules/credits_roll/episode_name.dm b/code/modules/credits_roll/episode_name.dm
index 42a85dae13e0..184353cc7bba 100644
--- a/code/modules/credits_roll/episode_name.dm
+++ b/code/modules/credits_roll/episode_name.dm
@@ -155,12 +155,12 @@
episode_names += new /datum/episode_name/rare("[pick("WHERE NO DOG HAS GONE BEFORE", "IAN SAYS", "IAN'S DAY OUT", "EVERY DOG HAS ITS DAY", "THE ONE WITH THE MAGIC PUPPY")]", "You know what you did.", 1000)
break
- if(!EMERGENCY_ESCAPED_OR_ENDGAMED)
+ if(!SSevacuation.station_evacuated())
return
var/dead = GLOB.joined_player_list.len - SSticker.popcount[POPCOUNT_ESCAPEES]
var/escaped = SSticker.popcount[POPCOUNT_ESCAPEES]
- var/escaped_on_shuttle = SSticker.popcount[POPCOUNT_SHUTTLE_ESCAPEES]
+ var/escaped_on_shuttle = SSticker.popcount[POPCOUNT_EVAC_ESCAPEES]
var/human_escapees = SSticker.popcount[POPCOUNT_ESCAPEES_HUMANONLY]
if((REALTIMEOFDAY - SSticker.round_start_timeofday) < 20 MINUTES) //shuttle docked in less than 16 minutes!!
episode_names += new /datum/episode_name/rare("[pick("THE CAPTAIN STUBS THEIR TOE", "QUICK GETAWAY", "A MOST EFFICIENT APOCALYPSE", "THE CREW'S [round((REALTIMEOFDAY - SSticker.round_start_timeofday)/60)] MINUTES OF FAME", "ON SECOND THOUGHT, LET'S NOT GO TO [uppr_name]. 'TIS A SILLY PLACE.")]", "This round was about as short as they come.", 750)
@@ -168,7 +168,7 @@
episode_names += new /datum/episode_name/rare("DRY RUN", "This round was as short as they come, and there were no escapees.", 2500)
if(dead == 0)
episode_names += new /datum/episode_name/rare("[pick("EMPLOYEE TRANSFER", "LIVE LONG AND PROSPER", "PEACE AND QUIET IN [uppr_name]", "THE ONE WITHOUT ALL THE FIGHTING", "THE CREW TRIES TO KILL A FLY FOR [round((REALTIMEOFDAY - SSticker.round_start_timeofday)/60)] MINUTES")]", "No-one died this round.", 2500) //in practice, this one is very very very rare, so if it happens let's pick it more often
- if(escaped == 0 || SSshuttle.emergency.is_hijacked())
+ if(escaped == 0)
episode_names += new /datum/episode_name("[pick("DEAD SPACE", "THE CREW GOES MISSING", "LOST IN TRANSLATION", "[uppr_name]: DELETED SCENES", "WHAT HAPPENS IN [uppr_name], STAYS IN [uppr_name]", "MISSING IN ACTION", "SCOOBY-DOO, WHERE'S THE CREW?")]", "There were no escapees on the shuttle.", 300)
if(escaped < 6 && escaped > 0 && dead > escaped*2)
episode_names += new /datum/episode_name("[pick("AND THEN THERE WERE FEWER", "THE 'FUN' IN 'FUNERAL'", "FREEDOM RIDE OR DIE", "THINGS WE LOST IN [uppr_name]", "GONE WITH [uppr_name]", "LAST TANGO IN [uppr_name]", "GET BUSY LIVING OR GET BUSY DYING", "THE CREW FUCKING DIES", "WISH YOU WERE HERE")]", "[dead] people died this round.", 400)
@@ -224,8 +224,6 @@
if(voxcount / human_escapees > 0.6 && human_escapees > 2)
episode_names += new /datum/episode_name/rare("BIRDS OF A FEATHER...", "Most of the survivors were Vox.", min(1500, voxcount*250))
- if(voxcount / human_escapees > 0.6 && SSshuttle.emergency.launch_status == EARLY_LAUNCHED)
- episode_names += new /datum/episode_name/rare("EARLY BIRD GETS THE WORM", "Most or all of the survivors were Vox, and the shuttle timer was shortened.", 1500)
//if(voxcount / human_escapees.len > 0.6 && score.shuttlebombed > 3)
// episode_names += new /datum/episode_name/rare("SITTING DUCKS", "Most or all of the survivors were Vox, and the shuttle was bombed.", min(1500,score.shuttlebombed*3))
if(baldycount / human_escapees> 0.6 && human_escapees > 3)
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 79cef6d02464..090b8bc7309a 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -49,7 +49,7 @@
return FALSE
if(holidayID && (!SSevents.holidays || !SSevents.holidays[holidayID]))
return FALSE
- if(EMERGENCY_ESCAPED_OR_ENDGAMED)
+ if(SSticker.current_state >= GAME_STATE_FINISHED)
return FALSE
if(ispath(typepath, /datum/round_event/ghost_role) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT))
return FALSE
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index 0407353399cb..b9f88d0d7c85 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -9,7 +9,7 @@
dynamic_should_hijack = TRUE
/datum/round_event_control/blob/canSpawnEvent(players)
- if(EMERGENCY_PAST_POINT_OF_NO_RETURN) // no blobs if the shuttle is past the point of no return
+ if(SSevacuation.station_evacuated()) // no blobs if the shuttle is past the point of no return
return FALSE
return ..()
diff --git a/code/modules/events/shuttle_catastrophe.dm b/code/modules/events/shuttle_catastrophe.dm
deleted file mode 100644
index cc14d6c25054..000000000000
--- a/code/modules/events/shuttle_catastrophe.dm
+++ /dev/null
@@ -1,53 +0,0 @@
-/datum/round_event_control/shuttle_catastrophe
- name = "Shuttle Catastrophe"
- typepath = /datum/round_event/shuttle_catastrophe
- weight = 10
- max_occurrences = 1
-
-/datum/round_event_control/shuttle_catastrophe/canSpawnEvent(players)
- if(SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_FORCED)
- return FALSE //don't do it if its already been done
- if(istype(SSshuttle.emergency, /obj/docking_port/mobile/emergency/shuttle_build))
- return FALSE //don't undo manual player engineering, it also would unload people and ghost them, there's just a lot of problems
- if(EMERGENCY_AT_LEAST_DOCKED)
- return FALSE //don't remove all players when its already on station or going to centcom
- return ..()
-
-
-/datum/round_event/shuttle_catastrophe
- var/datum/map_template/shuttle/new_shuttle
-
-/datum/round_event/shuttle_catastrophe/announce(fake)
- var/cause = pick("was attacked by [syndicate_name()] Operatives", "mysteriously teleported away", "had its refuelling crew mutiny",
- "was found with its engines stolen", "\[REDACTED\]", "flew into the sunset, and melted", "learned something from a very wise cow, and left on its own",
- "had cloning devices on it", "had its shuttle inspector put the shuttle in reverse instead of park, causing the shuttle to crash into the hangar")
- var/message = "Your emergency shuttle [cause]. "
-
- if(SSshuttle.shuttle_insurance)
- message += "Luckily, your shuttle insurance has covered the costs of repair!"
- if(SSeconomy.department_accounts_by_id[ACCOUNT_CAR])
- message += " You have been awarded a bonus from [command_name()] for smart spending."
- else
- message += "Your replacement shuttle will be the [new_shuttle.name] until further notice."
- priority_announce(message, "[command_name()] Spacecraft Engineering")
-
-/datum/round_event/shuttle_catastrophe/setup()
- if(SSshuttle.shuttle_insurance)
- return
- var/list/valid_shuttle_templates = list()
- for(var/shuttle_id in SSmapping.shuttle_templates)
- var/datum/map_template/shuttle/template = SSmapping.shuttle_templates[shuttle_id]
- if(!isnull(template.who_can_purchase) && template.credit_cost < INFINITY) //if we could get it from the communications console, it's cool for us to get it here
- valid_shuttle_templates += template
- new_shuttle = pick(valid_shuttle_templates)
-
-/datum/round_event/shuttle_catastrophe/start()
- if(SSshuttle.shuttle_insurance)
- var/datum/bank_account/station_balance = SSeconomy.department_accounts_by_id[ACCOUNT_CAR]
- station_balance?.adjust_money(8000)
- return
- SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_FORCED
- SSshuttle.unload_preview()
- SSshuttle.existing_shuttle = SSshuttle.emergency
- SSshuttle.action_load(new_shuttle, replace = TRUE)
- log_shuttle("Shuttle Catastrophe set a new shuttle, [new_shuttle.name].")
diff --git a/code/modules/events/shuttle_insurance.dm b/code/modules/events/shuttle_insurance.dm
deleted file mode 100644
index d16638d69a57..000000000000
--- a/code/modules/events/shuttle_insurance.dm
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-/datum/round_event_control/shuttle_insurance
- name = "Shuttle Insurance"
- typepath = /datum/round_event/shuttle_insurance
- max_occurrences = 1
-
-/datum/round_event_control/shuttle_insurance/canSpawnEvent(players)
- if(!SSeconomy.department_accounts_by_id[ACCOUNT_CAR])
- return FALSE //They can't pay?
- if(SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_FORCED)
- return FALSE //don't do it if there's nothing to insure
- if(EMERGENCY_AT_LEAST_DOCKED)
- return FALSE //catastrophes won't trigger so no point
- return ..()
-
-/datum/round_event/shuttle_insurance
- var/ship_name = "\"In the Unlikely Event\""
- var/datum/comm_message/insurance_message
- var/insurance_evaluation = 0
-
-/datum/round_event/shuttle_insurance/announce(fake)
- priority_announce("Incoming long range communication. Secure channel opened at all communication consoles.", sound_type = ANNOUNCER_CENTCOM)
-
-/datum/round_event/shuttle_insurance/setup()
- ship_name = pick(strings(PIRATE_NAMES_FILE, "rogue_names"))
- for(var/shuttle_id in SSmapping.shuttle_templates)
- var/datum/map_template/shuttle/template = SSmapping.shuttle_templates[shuttle_id]
- if(template.name == SSshuttle.emergency.name) //found you slackin
- insurance_evaluation = template.credit_cost/2
- break
- if(!insurance_evaluation)
- insurance_evaluation = 5000 //gee i dunno
-
-/datum/round_event/shuttle_insurance/start()
- insurance_message = new("Shuttle Insurance", "Hey, pal, this is the [ship_name]. Can't help but notice you're rocking a wild and crazy shuttle there with NO INSURANCE! Crazy. What if something happened to it, huh?! We've done a quick evaluation on your rates in this sector and we're offering [insurance_evaluation] to cover for your shuttle in case of any disaster.", list("Purchase Insurance.","Reject Offer."))
- insurance_message.answer_callback = CALLBACK(src,PROC_REF(answered))
- SScommunications.send_message(insurance_message, unique = TRUE)
-
-/datum/round_event/shuttle_insurance/proc/answered()
- if(EMERGENCY_AT_LEAST_DOCKED)
- priority_announce("You are definitely too late to purchase insurance, my friends. Our agents don't work on site.", ship_name)
- return
- if(insurance_message && insurance_message.answered == 1)
- var/datum/bank_account/station_balance = SSeconomy.department_accounts_by_id[ACCOUNT_CAR]
- if(!station_balance?.adjust_money(-insurance_evaluation))
- priority_announce("You didn't send us enough money for shuttle insurance. This, in the space layman's terms, is considered scamming. We're keeping your money, scammers!", ship_name)
- return
- priority_announce("Thank you for purchasing shuttle insurance!", ship_name)
- SSshuttle.shuttle_insurance = TRUE
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 545938c2280d..c0c1554d031f 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -375,13 +375,8 @@
GLOB.joined_player_list += character.ckey
if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais.
- if(SSshuttle.emergency)
- switch(SSshuttle.emergency.mode)
- if(SHUTTLE_RECALL, SHUTTLE_IDLE)
- SSticker.mode.make_antag_chance(humanc)
- if(SHUTTLE_CALL)
- if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergency_call_time)*0.5)
- SSticker.mode.make_antag_chance(humanc)
+ if(SSevacuation.evacuation_can_be_cancelled())
+ SSticker.mode.make_antag_chance(humanc)
if((job.job_flags & JOB_ASSIGN_QUIRKS) && humanc && CONFIG_GET(flag/roundstart_traits))
SSquirks.AssignQuirks(humanc, humanc.client)
@@ -404,13 +399,10 @@
if(SSlag_switch.measures[DISABLE_NON_OBSJOBS])
dat += "