From 63adea0ebb329cad58aa6190875695ee2b012099 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Tue, 12 Jan 2021 06:50:49 +0100 Subject: [PATCH] [MIRROR] [READY] A victory for revolutionaries will no longer end the round on dynamic, instead no new security and command will be allowed to join (#2653) * [READY] A victory for revolutionaries will no longer end the round on dynamic, instead no new security and command will be allowed to join (#55956) Currently, a victory for revoutionaries leads to the round abruptly ending on dynamic. This has been replaced by all revolutionaries being deconverted, and no new security or command positions being available. Also attempts to unionize (pun intended) copy and paste code in the round start and late join revolutionary rulesets into being handled under the revolutionary team instead. * [READY] A victory for revolutionaries will no longer end the round on dynamic, instead no new security and command will be allowed to join Co-authored-by: Mothblocks <35135081+Jared-Fogle@users.noreply.github.com> --- code/__DEFINES/role_preferences.dm | 1 + code/controllers/subsystem/job.dm | 2 + code/datums/mind.dm | 3 + code/game/gamemodes/dynamic/dynamic.dm | 7 - .../gamemodes/dynamic/dynamic_rulesets.dm | 5 - .../dynamic/dynamic_rulesets_latejoin.dm | 58 +----- .../dynamic/dynamic_rulesets_roundstart.dm | 58 ++---- code/modules/admin/antag_panel.dm | 2 +- .../antagonists/revolution/revolution.dm | 166 ++++++++++++++++-- code/modules/events/bureaucratic_error.dm | 4 +- code/modules/jobs/job_types/_job.dm | 3 + code/modules/jobs/job_types/ai.dm | 1 + strings/anti_union_propaganda.txt | 4 + 13 files changed, 186 insertions(+), 128 deletions(-) create mode 100644 strings/anti_union_propaganda.txt diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 4b3c371a33e..63b8480d865 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -14,6 +14,7 @@ #define ROLE_MALF "Malf AI" #define ROLE_REV "Revolutionary" #define ROLE_REV_HEAD "Head Revolutionary" +#define ROLE_REV_SUCCESSFUL "Victorious Revolutionary" #define ROLE_ALIEN "Xenomorph" #define ROLE_PAI "pAI" #define ROLE_CULTIST "Cultist" diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index da7400e9858..445780aa915 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -30,11 +30,13 @@ SUBSYSTEM_DEF(job) var/datum/job/new_overflow = GetJob(new_overflow_role) var/cap = CONFIG_GET(number/overflow_cap) + new_overflow.allow_bureaucratic_error = FALSE new_overflow.spawn_positions = cap new_overflow.total_positions = cap if(new_overflow_role != overflow_role) var/datum/job/old_overflow = GetJob(overflow_role) + old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error) old_overflow.spawn_positions = initial(old_overflow.spawn_positions) old_overflow.total_positions = initial(old_overflow.total_positions) overflow_role = new_overflow_role diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 979bd758bb1..5c154621110 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -80,6 +80,9 @@ ///Skill multiplier list, just slap your multiplier change onto this with the type it is coming from as key. var/list/experience_multiplier_reasons = list() + /// A lazy list of statuses to add next to this mind in the traitor panel + var/list/special_statuses + /datum/mind/New(_key) key = _key martial_art = default_martial_art diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 9358085ffa7..63a96b2d4f6 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -190,10 +190,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) // Checks if there are HIGHLANDER_RULESETs and calls the rule's round_result() proc /datum/game_mode/dynamic/set_round_result() - for(var/datum/dynamic_ruleset/rule in executed_rules) - if(rule.flags & HIGHLANDER_RULESET) - if(rule.check_finished()) // Only the rule that actually finished the round sets round result. - return rule.round_result() // If it got to this part, just pick one highlander if it exists for(var/datum/dynamic_ruleset/rule in executed_rules) if(rule.flags & HIGHLANDER_RULESET) @@ -250,9 +246,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) return TRUE if(force_ending) return TRUE - for(var/datum/dynamic_ruleset/rule in executed_rules) - if(rule.flags & HIGHLANDER_RULESET) - return rule.check_finished() /datum/game_mode/dynamic/proc/show_threatlog(mob/admin) if(!SSticker.HasRoundStarted()) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index b82cd2169fb..df025eba7d6 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -182,11 +182,6 @@ /// Only called if ruleset is flagged as HIGHLANDER_RULESET /datum/dynamic_ruleset/proc/round_result() -/// Checks if round is finished, return true to end the round. -/// Only called if ruleset is flagged as HIGHLANDER_RULESET -/datum/dynamic_ruleset/proc/check_finished() - return FALSE - ////////////////////////////////////////////// // // // ROUNDSTART RULESETS // diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index c4767cc6829..3c933aa275a 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -86,6 +86,8 @@ 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) @@ -112,7 +114,7 @@ new_head = M.mind.add_antag_datum(new_head, revolution) revolution.update_objectives() revolution.update_heads() - SSshuttle.registerHostileEnvironment(src) + SSshuttle.registerHostileEnvironment(revolution) return TRUE else log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.") @@ -120,29 +122,12 @@ return FALSE /datum/dynamic_ruleset/latejoin/provocateur/rule_process() - if(check_rev_victory()) - finished = REVOLUTION_VICTORY - return RULESET_STOP_PROCESSING - else if (check_heads_victory()) - finished = STATION_VICTORY - SSshuttle.clearHostileEnvironment(src) - revolution.save_members() - for(var/datum/mind/M in revolution.members) // Remove antag datums and prevents podcloned or exiled headrevs restarting rebellions. - if(M.has_antag_datum(/datum/antagonist/rev/head)) - var/datum/antagonist/rev/head/R = M.has_antag_datum(/datum/antagonist/rev/head) - R.remove_revolutionary(FALSE, "gamemode") - if(M.current) - var/mob/living/carbon/C = M.current - if(istype(C) && C.stat == DEAD) - C.makeUncloneable() - if(M.has_antag_datum(/datum/antagonist/rev)) - var/datum/antagonist/rev/R = M.has_antag_datum(/datum/antagonist/rev) - R.remove_revolutionary(FALSE, "gamemode") - 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, 'sound/ai/attention.ogg', null, "Central Command Loyalty Monitoring Division") - return RULESET_STOP_PROCESSING - + var/winner = revolution.process_victory(revs_win_threat_injection) + if (isnull(winner)) + return + finished = winner + return RULESET_STOP_PROCESSING /// Checks for revhead loss conditions and other antag datums. /datum/dynamic_ruleset/latejoin/provocateur/proc/check_eligible(datum/mind/M) @@ -151,33 +136,8 @@ return TRUE return FALSE -/datum/dynamic_ruleset/latejoin/provocateur/check_finished() - if(finished == REVOLUTION_VICTORY) - return TRUE - else - return ..() - -/datum/dynamic_ruleset/latejoin/provocateur/proc/check_rev_victory() - for(var/datum/objective/mutiny/objective in revolution.objectives) - if(!(objective.check_completion())) - return FALSE - return TRUE - -/datum/dynamic_ruleset/latejoin/provocateur/proc/check_heads_victory() - for(var/datum/mind/rev_mind in revolution.head_revolutionaries()) - var/turf/T = get_turf(rev_mind.current) - if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(T.z)) - if(ishuman(rev_mind.current)) - return FALSE - return TRUE - /datum/dynamic_ruleset/latejoin/provocateur/round_result() - if(finished == REVOLUTION_VICTORY) - SSticker.mode_result = "win - heads killed" - SSticker.news_report = REVS_WIN - else if(finished == STATION_VICTORY) - SSticker.mode_result = "loss - rev heads killed" - SSticker.news_report = REVS_LOSE + revolution.round_result(finished) ////////////////////////////////////////////// // // diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 31045e13e2d..357ecc26077 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -380,6 +380,8 @@ 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 @@ -412,7 +414,7 @@ if(revolution.members.len) revolution.update_objectives() revolution.update_heads() - SSshuttle.registerHostileEnvironment(src) + SSshuttle.registerHostileEnvironment(revolution) return TRUE log_game("DYNAMIC: [ruletype] [name] failed to get any eligible headrevs. Refunding [cost] threat.") return FALSE @@ -422,27 +424,12 @@ ..() /datum/dynamic_ruleset/roundstart/revs/rule_process() - if(check_rev_victory()) - finished = REVOLUTION_VICTORY - return RULESET_STOP_PROCESSING - else if (check_heads_victory()) - finished = STATION_VICTORY - SSshuttle.clearHostileEnvironment(src) - revolution.save_members() - for(var/datum/mind/M in revolution.members) // Remove antag datums and prevents podcloned or exiled headrevs restarting rebellions. - if(M.has_antag_datum(/datum/antagonist/rev/head)) - var/datum/antagonist/rev/head/R = M.has_antag_datum(/datum/antagonist/rev/head) - R.remove_revolutionary(FALSE, "gamemode") - if(M.current) - var/mob/living/carbon/C = M.current - if(istype(C) && C.stat == DEAD) - C.makeUncloneable() - if(M.has_antag_datum(/datum/antagonist/rev)) - var/datum/antagonist/rev/R = M.has_antag_datum(/datum/antagonist/rev) - R.remove_revolutionary(FALSE, "gamemode") - 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, 'sound/ai/attention.ogg', null, "Central Command Loyalty Monitoring Division") - return RULESET_STOP_PROCESSING + var/winner = revolution.process_victory(revs_win_threat_injection) + if (isnull(winner)) + return + + finished = winner + return RULESET_STOP_PROCESSING /// Checks for revhead loss conditions and other antag datums. /datum/dynamic_ruleset/roundstart/revs/proc/check_eligible(datum/mind/M) @@ -451,33 +438,8 @@ return TRUE return FALSE -/datum/dynamic_ruleset/roundstart/revs/check_finished() - if(finished == REVOLUTION_VICTORY) - return TRUE - else - return ..() - -/datum/dynamic_ruleset/roundstart/revs/proc/check_rev_victory() - for(var/datum/objective/mutiny/objective in revolution.objectives) - if(!(objective.check_completion())) - return FALSE - return TRUE - -/datum/dynamic_ruleset/roundstart/revs/proc/check_heads_victory() - for(var/datum/mind/rev_mind in revolution.head_revolutionaries()) - var/turf/T = get_turf(rev_mind.current) - if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(T.z)) - if(ishuman(rev_mind.current)) - return FALSE - return TRUE - /datum/dynamic_ruleset/roundstart/revs/round_result() - if(finished == REVOLUTION_VICTORY) - SSticker.mode_result = "win - heads killed" - SSticker.news_report = REVS_WIN - else if(finished == STATION_VICTORY) - SSticker.mode_result = "loss - rev heads killed" - SSticker.news_report = REVS_LOSE + revolution.round_result(finished) ////////////////////////////////////////////// // // diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm index f34130609db..f8ebc0037ec 100644 --- a/code/modules/admin/antag_panel.dm +++ b/code/modules/admin/antag_panel.dm @@ -77,7 +77,7 @@ GLOBAL_VAR(antag_prototypes) return common_commands /datum/mind/proc/get_special_statuses() - var/list/result = list() + var/list/result = LAZYCOPY(special_statuses) if(!current) result += "No body!" if(current && HAS_TRAIT(current, TRAIT_MINDSHIELD)) diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index 3cdfa6dbeeb..32bbceb8abe 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -1,3 +1,5 @@ +#define DECONVERTER_STATION_WIN "gamemode_station_win" +#define DECONVERTER_REVS_WIN "gamemode_revs_win" //How often to check for promotion possibility #define HEAD_UPDATE_PERIOD 300 @@ -11,6 +13,9 @@ antag_hud_name = "rev" var/datum/team/revolution/rev_team + /// What message should the player receive when they are being demoted, and the revolution has won? + var/victory_message = "The revolution has overpowered the command staff! Viva la revolution! Execute any head of staff and security should you find them alive." + /datum/antagonist/rev/can_be_owned(datum/mind/new_owner) . = ..() if(.) @@ -200,7 +205,22 @@ new_rev.silent = FALSE to_chat(old_owner, "Revolution has been disappointed of your leader traits! You are a regular revolutionary now!") +/// Checks if the revolution succeeded, and lets them know. +/datum/antagonist/rev/proc/announce_victorious() + . = rev_team.check_rev_victory() + + if (!.) + return + + to_chat(owner, "") + var/policy = get_policy(ROLE_REV_SUCCESSFUL) + if (policy) + to_chat(owner, policy) + /datum/antagonist/rev/farewell() + if (announce_victorious()) + return + if(ishuman(owner.current)) owner.current.visible_message("", null, null, null, owner.current) to_chat(owner, "") @@ -209,6 +229,9 @@ to_chat(owner, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.") /datum/antagonist/rev/head/farewell() + if (announce_victorious()) + return + if((ishuman(owner.current))) if(owner.current.stat != DEAD) owner.current.visible_message("", null, null, null, owner.current) @@ -225,13 +248,13 @@ if(borged) message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]") owner.special_role = null - if(iscarbon(owner.current)) + if(iscarbon(owner.current) && deconverter != DECONVERTER_REVS_WIN) var/mob/living/carbon/C = owner.current C.Unconscious(100) owner.remove_antag_datum(type) /datum/antagonist/rev/head/remove_revolutionary(borged,deconverter) - if(borged || deconverter == "gamemode") + if(borged || deconverter == DECONVERTER_STATION_WIN || deconverter == DECONVERTER_REVS_WIN) . = ..() /datum/antagonist/rev/head/equip_rev() @@ -257,6 +280,21 @@ S.Insert(C) to_chat(C, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.") +/// "Enemy of the Revolutionary", given to heads and security when the revolution wins +/datum/antagonist/revolution_enemy + name = "Enemy of the Revolution" + show_in_antagpanel = FALSE + +/datum/antagonist/revolution_enemy/on_gain() + owner.special_role = "revolution enemy" + + var/datum/objective/survive/survive = new /datum/objective/survive + survive.owner = owner + survive.explanation_text = "The station has been overrun by revolutionaries, stay alive until the end." + objectives += survive + + return ..() + /datum/team/revolution name = "Revolution" var/max_headrevs = 3 @@ -315,6 +353,100 @@ ex_headrevs = get_antag_minds(/datum/antagonist/rev/head, TRUE) ex_revs = get_antag_minds(/datum/antagonist/rev, TRUE) +/// Checks if revs have won +/datum/team/revolution/proc/check_rev_victory() + for(var/datum/objective/mutiny/objective in objectives) + if(!(objective.check_completion())) + return FALSE + return TRUE + +/// Checks if heads have won +/datum/team/revolution/proc/check_heads_victory() + for(var/datum/mind/rev_mind in head_revolutionaries()) + var/turf/rev_turf = get_turf(rev_mind.current) + if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(rev_turf.z)) + if(ishuman(rev_mind.current)) + return FALSE + return TRUE + +/// 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) + if (check_rev_victory()) + . = REVOLUTION_VICTORY + else if (check_heads_victory()) + . = STATION_VICTORY + else + return + + SSshuttle.clearHostileEnvironment(src) + save_members() + + // Remove everyone as a revolutionary + for (var/_rev_mind in members) + var/datum/mind/rev_mind = _rev_mind + if (rev_mind.has_antag_datum(/datum/antagonist/rev)) + var/datum/antagonist/rev/rev_antag = rev_mind.has_antag_datum(/datum/antagonist/rev) + rev_antag.remove_revolutionary(FALSE, . == STATION_VICTORY ? DECONVERTER_STATION_WIN : DECONVERTER_REVS_WIN) + LAZYADD(rev_mind.special_statuses, "Former [(rev_mind in ex_headrevs) ? "head revolutionary" : "revolutionary"]") + + if (. == STATION_VICTORY) + // If the revolution was quelled, make rev heads unable to be revived through pods + for (var/_rev_head_mind in ex_revs) + var/datum/mind/rev_head_mind = _rev_head_mind + var/mob/living/carbon/rev_head_body = rev_head_mind.current + if(istype(rev_head_body) && rev_head_body.stat == DEAD) + rev_head_body.makeUncloneable() + + 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, 'sound/ai/attention.ogg', null, "Central Command Loyalty Monitoring Division") + else + for (var/_player in GLOB.player_list) + var/mob/player = _player + var/datum/mind/mind = player.mind + + if (isnull(mind)) + continue + + if (!(mind.assigned_role in GLOB.command_positions + GLOB.security_positions)) + continue + + var/mob/living/carbon/target_body = mind.current + + mind.add_antag_datum(/datum/antagonist/revolution_enemy) + + if (!istype(target_body)) + continue + + if (target_body.stat == DEAD) + target_body.makeUncloneable() + else + mind.announce_objectives() + + for (var/job_name in GLOB.command_positions + GLOB.security_positions) + var/datum/job/job = SSjob.GetJob(job_name) + job.allow_bureaucratic_error = FALSE + job.total_positions = 0 + + if (revs_win_injection_amount) + var/datum/game_mode/dynamic/dynamic = SSticker.mode + dynamic.create_threat(revs_win_injection_amount) + dynamic.threat_log += "[worldtime2text()]: Revolution victory. Added [revs_win_injection_amount] threat." + + 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, 'sound/ai/attention.ogg', null, "Central Command Loyalty Monitoring Division") + +/// Mutates the ticker to report that the revs have won +/datum/team/revolution/proc/round_result(finished) + if (finished == REVOLUTION_VICTORY) + SSticker.mode_result = "win - heads killed" + SSticker.news_report = REVS_WIN + else if (finished == STATION_VICTORY) + SSticker.mode_result = "loss - rev heads killed" + SSticker.news_report = REVS_LOSE + /datum/team/revolution/roundend_report() if(!members.len && !ex_headrevs.len) return @@ -323,18 +455,6 @@ result += "