diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 390de70f33c..67ee5928fe9 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -265,10 +265,10 @@ SUBSYSTEM_DEF(shuttle) /// 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." +/datum/controller/subsystem/shuttle/proc/canEvac() + var/shuttle_refuel_delay = CONFIG_GET(number/shuttle_refuel_delay) + if(world.time - SSticker.round_start_time < shuttle_refuel_delay) + return "The emergency shuttle is refueling. Please wait [DisplayTimeText(shuttle_refuel_delay - (world.time - SSticker.round_start_time))] before attempting to call." switch(emergency.mode) if(SHUTTLE_RECALL) @@ -286,58 +286,73 @@ SUBSYSTEM_DEF(shuttle) 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 +/datum/controller/subsystem/shuttle/proc/check_backup_emergency_shuttle() + if(emergency) + return TRUE - var/can_evac_or_fail_reason = SSshuttle.canEvac(user) + WARNING("check_backup_emergency_shuttle(): There is no emergency shuttle, but the \ + shuttle was called. Using the backup shuttle instead.") + + if(!backup_shuttle) + CRASH("check_backup_emergency_shuttle(): 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 + + return TRUE + +/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason) + if (!check_backup_emergency_shuttle()) + return + + var/can_evac_or_fail_reason = SSshuttle.canEvac() 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 && SSsecurity_level.get_current_level_as_number() > SEC_LEVEL_GREEN) + if(length(trim(call_reason)) < CALL_SHUTTLE_REASON_LENGTH && SSsecurity_level.get_current_level_as_number() > 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 = SSsecurity_level.get_current_level_as_number() - 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 = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) - - if(!frequency) - return - - var/datum/signal/status_signal = new(list("command" = "update")) // Start processing shuttle-mode displays to display the timer - frequency.post_signal(src, status_signal) - - var/area/A = get_area(user) + call_evac_shuttle(call_reason, signal_origin) 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) + deadchat_broadcast(" has called the shuttle 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_shuttle("Shuttle call reason: [call_reason]") SSticker.emergency_reason = call_reason message_admins("[ADMIN_LOOKUPFLW(user)] has called the shuttle. (TRIGGER CENTCOM RECALL)") +/// Call the emergency shuttle. +/// If you are doing this on behalf of a player, use requestEvac instead. +/// `signal_origin` is fluff occasionally provided to players. +/datum/controller/subsystem/shuttle/proc/call_evac_shuttle(call_reason, signal_origin) + if (!check_backup_emergency_shuttle()) + return + + call_reason = trim(html_encode(call_reason)) + + var/emergency_reason = "\nNature of emergency:\n\n[call_reason]" + + emergency.request( + signal_origin = signal_origin, + reason = html_decode(emergency_reason), + red_alert = (SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) + ) + + var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) + + if(frequency) + // Start processing shuttle-mode displays to display the timer + 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 diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index e00ccd27b70..ebe0780b539 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -119,8 +119,6 @@ blocking_rules = list(/datum/dynamic_ruleset/roundstart/revs) var/required_heads_of_staff = 3 var/finished = FALSE - /// How much threat should be injected when the revolution wins? - var/revs_win_threat_injection = 20 var/datum/team/revolution/revolution /datum/dynamic_ruleset/latejoin/provocateur/ready(forced=FALSE) @@ -155,7 +153,7 @@ return FALSE /datum/dynamic_ruleset/latejoin/provocateur/rule_process() - var/winner = revolution.process_victory(revs_win_threat_injection) + var/winner = revolution.process_victory() if (isnull(winner)) return diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 6c1bb16baec..39447b369b4 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -495,8 +495,6 @@ blocking_rules = list(/datum/dynamic_ruleset/latejoin/provocateur) // I give up, just there should be enough heads with 35 players... minimum_players = 35 - /// How much threat should be injected when the revolution wins? - var/revs_win_threat_injection = 20 var/datum/team/revolution/revolution var/finished = FALSE @@ -539,7 +537,7 @@ ..() /datum/dynamic_ruleset/roundstart/revs/rule_process() - var/winner = revolution.process_victory(revs_win_threat_injection) + var/winner = revolution.process_victory() if (isnull(winner)) return diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 2c12d3b4d2d..d940fdb0b70 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -520,7 +520,7 @@ data["alertLevel"] = SSsecurity_level.get_current_level_as_text() data["authorizeName"] = authorize_name data["canLogOut"] = !issilicon(user) - data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user) + data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac() if(syndicate) data["shuttleCanEvacOrFailReason"] = "You cannot summon the shuttle from this console!" diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index 86593367058..22b04b63be4 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -405,8 +405,7 @@ /// Updates the state of the world depending on if revs won or loss. /// Returns who won, at which case this method should no longer be called. -/// If revs_win_injection_amount is passed, then that amount of threat will be added if the revs win. -/datum/team/revolution/proc/process_victory(revs_win_injection_amount) +/datum/team/revolution/proc/process_victory() if (check_rev_victory()) . = REVOLUTION_VICTORY else if (check_heads_victory()) @@ -417,8 +416,6 @@ SSshuttle.clearHostileEnvironment(src) save_members() - var/charter_given = FALSE - // Remove everyone as a revolutionary for (var/_rev_mind in members) var/datum/mind/rev_mind = _rev_mind @@ -441,56 +438,105 @@ priority_announce("It appears the mutiny has been quelled. Please return yourself and your incapacitated colleagues to work. \ We have remotely blacklisted the head revolutionaries in your medical records to prevent accidental revival.", null, null, null, "Central Command Loyalty Monitoring Division") else - for(var/datum/mind/headrev_mind as anything in ex_headrevs) - if(charter_given) - break - if(!headrev_mind.current || headrev_mind.current.stat != CONSCIOUS) - continue - charter_given = TRUE - podspawn(list( - "target" = get_turf(headrev_mind.current), - "style" = STYLE_SYNDICATE, - "spawn" = /obj/item/station_charter/revolution, - )) - to_chat(headrev_mind.current, span_hear("You hear something crackle in your ears for a moment before a voice speaks. \ - \"Please stand by for a message from your benefactor. Message as follows, provocateur. \ - You have been chosen out of your fellow provocateurs to rename the station. Choose wisely. Message ends.\"")) - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_REVOLUTION_TAX_REMOVAL) + victory_effects() - for (var/mob/living/player as anything in GLOB.player_list) - var/datum/mind/player_mind = player.mind +/datum/team/revolution/proc/victory_effects(revs_win_injection_amount) + var/charter_given = FALSE - if (isnull(player_mind)) - continue + for(var/datum/mind/headrev_mind as anything in ex_headrevs) + if(charter_given) + break + if(!headrev_mind.current || headrev_mind.current.stat != CONSCIOUS) + continue + charter_given = TRUE + podspawn(list( + "target" = get_turf(headrev_mind.current), + "style" = STYLE_SYNDICATE, + "spawn" = /obj/item/station_charter/revolution, + )) + to_chat(headrev_mind.current, span_hear("You hear something crackle in your ears for a moment before a voice speaks. \ + \"Please stand by for a message from your benefactor. Message as follows, provocateur. \ + You have been chosen out of your fellow provocateurs to rename the station. Choose wisely. Message ends.\"")) + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_REVOLUTION_TAX_REMOVAL) - if (!(player_mind.assigned_role.departments_bitflags & (DEPARTMENT_BITFLAG_SECURITY|DEPARTMENT_BITFLAG_COMMAND))) - continue + for (var/mob/living/player as anything in GLOB.player_list) + var/datum/mind/player_mind = player.mind - if (player_mind in ex_revs + ex_headrevs) - continue + if (isnull(player_mind)) + continue - player_mind.add_antag_datum(/datum/antagonist/enemy_of_the_revolution) + if (!(player_mind.assigned_role.departments_bitflags & (DEPARTMENT_BITFLAG_SECURITY|DEPARTMENT_BITFLAG_COMMAND))) + continue - if (!istype(player)) - continue + if (player_mind in ex_revs + ex_headrevs) + continue - if(player_mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) - ADD_TRAIT(player, TRAIT_DEFIB_BLACKLISTED, REF(src)) - player.med_hud_set_status() + player_mind.add_antag_datum(/datum/antagonist/enemy_of_the_revolution) - for(var/datum/job/job as anything in SSjob.joinable_occupations) - if(!(job.departments_bitflags & (DEPARTMENT_BITFLAG_SECURITY|DEPARTMENT_BITFLAG_COMMAND))) - continue - job.allow_bureaucratic_error = FALSE - job.total_positions = 0 + if (!istype(player)) + continue - if (revs_win_injection_amount) - var/datum/game_mode/dynamic/dynamic = SSticker.mode - dynamic.unfavorable_situation() + if(player_mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + ADD_TRAIT(player, TRAIT_DEFIB_BLACKLISTED, REF(src)) + player.med_hud_set_status() - priority_announce("A recent assessment of your station has marked your station as a severe risk area for high ranking Nanotrasen officials. \ - For the safety of our staff, we have blacklisted your station for new employment of security and command. \ - [pick(world.file2list("strings/anti_union_propaganda.txt"))]", null, null, null, "Central Command Loyalty Monitoring Division") + for(var/datum/job/job as anything in SSjob.joinable_occupations) + if(!(job.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY|DEPARTMENT_BITFLAG_COMMAND)) + continue + job.allow_bureaucratic_error = FALSE + job.total_positions = 0 + + var/datum/game_mode/dynamic/dynamic = SSticker.mode + dynamic.unfavorable_situation() + + var/message_header = "A recent assessment of your station has marked your station as a severe risk area for high ranking Nanotrasen officials." + var/extra_detail = try_auto_call_shuttle() \ + ? "For the safety of our staff, we are expediting an emergency shuttle for remaining members of security and command." \ + : "For the safety of our staff, we have blacklisted your station for new employment of security and command." + var/propaganda = pick(world.file2list("strings/anti_union_propaganda.txt")) + + priority_announce( + "[message_header]\n\n[extra_detail]\n\n[propaganda]", + sender_override = "Central Command Loyalty Monitoring Division" + ) + +/// How much of the station, ignoring sec and command, should be revs before a shuttle will be automatically called? +#define REV_AUTO_CALL_THRESHOLD 0.65 + +/datum/team/revolution/proc/try_auto_call_shuttle() + var/total_revs = ex_revs.len + ex_headrevs.len + var/total_candidates = 0 + + for (var/mob/player as anything in GLOB.player_list) + if (player.mind.has_antag_datum(/datum/antagonist/enemy_of_the_revolution)) + continue + + total_candidates += 1 + + var/display_percent = round(total_revs / total_candidates * 100) + + if (total_revs / total_candidates < REV_AUTO_CALL_THRESHOLD) + log_game("REVOLUTION: Not calling the shuttle, [display_percent]% are revs") + return FALSE + + // Do it later so everyone has time to see the messages + addtimer(CALLBACK(src, .proc/perform_auto_shuttle_call), 20 SECONDS) + + var/log = "REVOLUTION: Auto-calling the shuttle, [display_percent]% are revs" + log_game(log) + message_admins(log) + + return TRUE + +#undef REV_AUTO_CALL_THRESHOLD + +/datum/team/revolution/proc/perform_auto_shuttle_call() + var/can_evac_result = SSshuttle.canEvac() + if (can_evac_result != TRUE) + log_game("REVOLUTION: Not calling the shuttle, canEvac() returned [can_evac_result]") + return + + SSshuttle.call_evac_shuttle("Sending emergency shuttle to rescue command and security staff.") /// Mutates the ticker to report that the revs have won /datum/team/revolution/proc/round_result(finished) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 88703333a0a..b3cfe93d9a1 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -318,7 +318,7 @@ to_chat(usr, span_warning("Wireless control is disabled!")) return - var/can_evac_or_fail_reason = SSshuttle.canEvac(src) + var/can_evac_or_fail_reason = SSshuttle.canEvac() if(can_evac_or_fail_reason != TRUE) to_chat(usr, span_alert("[can_evac_or_fail_reason]")) return diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index e173e4c72ed..2cf09e2f86a 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -319,7 +319,7 @@ . = ..() -/obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, area/signalOrigin, reason, redAlert, set_coefficient=null) +/obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, area/signal_origin, reason, red_alert, set_coefficient=null) if(!isnum(set_coefficient)) var/security_num = SSsecurity_level.get_current_level_as_number() switch(security_num) @@ -342,11 +342,11 @@ SSshuttle.emergencyCallAmount++ 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("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergency_last_call_loc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ][SSshuttle.admin_emergency_no_recall ? "\n\nWarning: Shuttle recall subroutines disabled; Recall not possible." : ""]", null, ANNOUNCER_SHUTTLECALLED, "Priority") + priority_announce("The emergency shuttle has been called. [red_alert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergency_last_call_loc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ][SSshuttle.admin_emergency_no_recall ? "\n\nWarning: Shuttle recall subroutines disabled; Recall not possible." : ""]", null, ANNOUNCER_SHUTTLECALLED, "Priority") /obj/docking_port/mobile/emergency/cancel(area/signalOrigin) if(mode != SHUTTLE_CALL)