diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm index 5b48acc82d9..34b0bf4242a 100644 --- a/code/__DEFINES/shuttles.dm +++ b/code/__DEFINES/shuttles.dm @@ -14,7 +14,7 @@ #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) +#define EMERGENCY_PAST_POINT_OF_NO_RETURN ((SSshuttle.emergency && SSshuttle.emergency.mode == SHUTTLE_CALL && !SSshuttle.past_restriction_point()) || EMERGENCY_AT_LEAST_DOCKED) // Shuttle return values #define SHUTTLE_CAN_DOCK "can_dock" diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index ea8e2b82602..7b1ba551a8e 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -417,10 +417,14 @@ SUBSYSTEM_DEF(shuttle) var/datum/signal/status_signal = new(list("command" = "update")) frequency.post_signal(src, status_signal) -/datum/controller/subsystem/shuttle/proc/centcom_recall(old_timer, admiral_message) - if(emergency.mode != SHUTTLE_CALL || emergency.timer != old_timer) - return - emergency.cancel() +/// Does a fluffy CentCom recall of the emergency shuttle with additional message as desired. +/// Returns TRUE on success, FALSE otherwise. +/datum/controller/subsystem/shuttle/proc/centcom_recall(mob/user, old_timer, admiral_message) + if(emergency.timer != old_timer) + return FALSE + + if(!cancel_evac(user)) + return FALSE //feedback handled in cancel_evac() if(!admiral_message) admiral_message = pick(GLOB.admiral_messages) @@ -437,6 +441,8 @@ SUBSYSTEM_DEF(shuttle) [admiral_message]" print_command_report(intercepttext, announce = TRUE) + return TRUE + // Called when an emergency shuttle mobile docking port is // destroyed, which will only happen with admin intervention /datum/controller/subsystem/shuttle/proc/emergencyDeregister() @@ -444,29 +450,76 @@ SUBSYSTEM_DEF(shuttle) // 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 +/// Actually work on canceling the emergency shuttle recall. Returns TRUE if successful, FALSE otherwise. +/datum/controller/subsystem/shuttle/proc/cancel_evac(mob/user) + if(!can_recall(user)) + return FALSE -/datum/controller/subsystem/shuttle/proc/canRecall() - if(!emergency || emergency.mode != SHUTTLE_CALL || admin_emergency_no_recall || emergency_no_recall) - return + 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 TRUE + +/// Can this user recall the emergency shuttle? Returns TRUE if they can, otherwise returns FALSE. +/datum/controller/subsystem/shuttle/proc/can_recall(mob/user) + if(isnull(emergency) || emergency.mode != SHUTTLE_CALL) + return FALSE + + var/is_admin = !!user.client?.holder + if(is_admin) + return admin_recall(user) + + if(admin_emergency_no_recall || emergency_no_recall) + return FALSE + + return past_restriction_point() + +/// Are we past the restriction point (i.e. more than half of the shuttle timer has elapsed) for recalling the shuttle? Returns TRUE if we are, FALSE otherwise. +/datum/controller/subsystem/shuttle/proc/past_restriction_point() var/security_num = SSsecurity_level.get_current_level_as_number() switch(security_num) if(SEC_LEVEL_GREEN) if(emergency.timeLeft(1) < emergency_call_time) - return + return FALSE if(SEC_LEVEL_BLUE) if(emergency.timeLeft(1) < emergency_call_time * 0.5) - return + return FALSE else if(emergency.timeLeft(1) < emergency_call_time * 0.25) - return - return 1 + return FALSE + + return TRUE + +/// Handle admin level overrides of recalling the shuttle. We assume that the user passed is an admin. +/// If a special state exists, we prompt the admin to confirm they want to override the wishes of other admins/game code. Returns TRUE if they elect to do so, FALSE otherwise. +/datum/controller/subsystem/shuttle/proc/admin_recall(mob/user) + if(admin_emergency_no_recall) + var/admin_no_recall_alert = tgui_alert( + user, + "An administrator has disabled the emergency shuttle recall function, are you sure you want to proceed with the recall?", + "Admin Level Recall Confirmation", + list("Yes", "No"), + ) + if(admin_no_recall_alert == "Yes") + admin_emergency_no_recall = FALSE + return TRUE + return FALSE + + if(emergency_no_recall) + var/general_no_recall_alert = tgui_alert( + user, + "The emergency shuttle recall function is currently disabled by game code, are you sure you want to proceed with the recall?", + "Recall Confirmation", + list("Yes", "No"), + ) + if(general_no_recall_alert == "Yes") + // we will not unset emergency_no_recall here, as it's game code enforced and I want it to stay active, admins can transiently override it through this though. + // they can always edit the variable on SSshuttle if desperate. + return TRUE + return FALSE + + return TRUE // we assume that they've seen other warnings at earlier points if they got here from a verb or something like that /datum/controller/subsystem/shuttle/proc/autoEvac() if (!SSticker.IsRoundInProgress() || supermatter_cascade) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index d3e9f2899bb..b590505a239 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -292,7 +292,7 @@ // AIs cannot recall the shuttle if (!authenticated(user) || HAS_SILICON_ACCESS(user) || syndicate) return - SSshuttle.cancelEvac(user) + SSshuttle.cancel_evac(user) if ("requestNukeCodes") if (!authenticated_as_non_silicon_captain(user)) return @@ -575,7 +575,7 @@ if (SSshuttle.emergency.mode != SHUTTLE_IDLE && SSshuttle.emergency.mode != SHUTTLE_RECALL) data["shuttleCalled"] = TRUE - data["shuttleRecallable"] = SSshuttle.canRecall() || syndicate + data["shuttleRecallable"] = SSshuttle.can_recall(user) || syndicate if (SSshuttle.emergencyCallAmount) data["shuttleCalledPreviously"] = TRUE diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 1f8f1f0eb6d..881523c3a59 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -96,11 +96,11 @@ return switch(SSshuttle.emergency.mode) if(SHUTTLE_CALL) - SSshuttle.emergency.cancel() + SSshuttle.cancel_evac(usr) // note that this does provide logging/feedback of its own but it's better for backcompatibility to retain the following logging lines 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() + 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.")) diff --git a/code/modules/admin/verbs/admin.dm b/code/modules/admin/verbs/admin.dm index b7b6d6a0ac1..f2af83fcd60 100644 --- a/code/modules/admin/verbs/admin.dm +++ b/code/modules/admin/verbs/admin.dm @@ -58,9 +58,12 @@ ADMIN_VERB(cmd_admin_check_player_exp, R_ADMIN, "Player Playtime", "View player if(!message) return + if(!SSshuttle.centcom_recall(usr, SSshuttle.emergency.timer, message)) // feedback handled within + return + message_admins("[key_name_admin(usr)] triggered a CentCom recall, with the admiral message of: [message]") usr.log_message("triggered a CentCom recall, with the message of: [message]", LOG_GAME) - SSshuttle.centcom_recall(SSshuttle.emergency.timer, message) + /datum/admins/proc/cmd_show_exp_panel(client/client_to_check) if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/adminshuttle.dm b/code/modules/admin/verbs/adminshuttle.dm index 6f2a0d42b19..eaac09eceea 100644 --- a/code/modules/admin/verbs/adminshuttle.dm +++ b/code/modules/admin/verbs/adminshuttle.dm @@ -51,10 +51,12 @@ ADMIN_VERB(cancel_shuttle, R_ADMIN, "Cancel Shuttle", "Recall the shuttle, regar if(EMERGENCY_AT_LEAST_DOCKED) return - if(tgui_alert(user, "You sure?", "Confirm", list("Yes", "No")) != "Yes") + if(tgui_alert(user, "You sure?", "Confirm Shuttle Cancellation", list("Yes", "No")) != "Yes") return - SSshuttle.admin_emergency_no_recall = FALSE - SSshuttle.emergency.cancel() + + if(!SSshuttle.cancel_evac(user.mob)) // handles the case where the shuttle is set to unrecallable by another admin or the code + return + BLACKBOX_LOG_ADMIN_VERB("Cancel Shuttle") log_admin("[key_name(user)] admin-recalled the emergency shuttle.") message_admins(span_adminnotice("[key_name_admin(user)] admin-recalled the emergency shuttle.")) diff --git a/code/modules/mob/dead/new_player/latejoin_menu.dm b/code/modules/mob/dead/new_player/latejoin_menu.dm index bb871470e93..2ed4396c3eb 100644 --- a/code/modules/mob/dead/new_player/latejoin_menu.dm +++ b/code/modules/mob/dead/new_player/latejoin_menu.dm @@ -51,8 +51,7 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new) if(SHUTTLE_ESCAPE) data["shuttle_status"] = "The station has been evacuated." if(SHUTTLE_CALL, SHUTTLE_DOCKED, SHUTTLE_IGNITING, SHUTTLE_ESCAPE) - if(!SSshuttle.canRecall()) - data["shuttle_status"] = "The station is currently undergoing evacuation procedures." + data["shuttle_status"] = "The station is currently undergoing evacuation procedures." for(var/datum/job/prioritized_job in SSjob.prioritized_jobs) if(prioritized_job.current_positions >= prioritized_job.total_positions) diff --git a/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm b/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm index f708b805a8b..c96fa9b0f89 100644 --- a/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm +++ b/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm @@ -55,19 +55,20 @@ color_override = "orange", ) -/obj/docking_port/mobile/emergency/cancel(area/signalOrigin) +/// This proc will assume you have done all of the necessary checks to see if the shuttle can be recalled, it will always recall when invoked. +/// signal_origin is an optional parameter that will log where the recall signal was sent from +/obj/docking_port/mobile/emergency/cancel(area/signal_origin = null) if(mode != SHUTTLE_CALL) return - if(SSshuttle.emergency_no_recall) - return invertTimer() mode = SHUTTLE_RECALL if(prob(70)) - SSshuttle.emergency_last_call_loc = signalOrigin + SSshuttle.emergency_last_call_loc = signal_origin else SSshuttle.emergency_last_call_loc = null + priority_announce( text = "The emergency shuttle has been recalled.[SSshuttle.emergency_last_call_loc ? " Recall signal traced. Results can be viewed on any communications console." : "" ]", title = "Emergency Shuttle Recalled",