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 += "