diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index f486e6a943b..63c1f2fd713 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -108,4 +108,6 @@ #define COOLDOWN_RESET(cd_source, cd_index) cd_source.cd_index = 0 +#define COOLDOWN_STARTED(cd_source, cd_index) (cd_source.cd_index != 0) + #define COOLDOWN_TIMELEFT(cd_source, cd_index) (max(0, cd_source.cd_index - world.time)) diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 00488ba1dfb..bdefee42c3e 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -236,18 +236,46 @@ GLOBAL_LIST_EMPTY(objectives) //SKYRAT EDIT ADDITION /datum/objective/assassinate/admin_edit(mob/admin) admin_simple_target_pick(admin) +#define DISCONNECT_GRACE_TIME (2 MINUTES) +#define DISCONNECT_GRACE_WARNING_TIME (1 MINUTES) + /datum/objective/mutiny name = "mutiny" martyr_compatible = 1 var/target_role_type = FALSE + /// Not primarily used as a cooldown but a timer to give a little bit more of a chance for the player to reconnect. + COOLDOWN_DECLARE(disconnect_timer) + /// Whether admins have been warned about the potentially AFK player + var/warned_admins = FALSE +/datum/objective/mutiny/proc/warn_admins() + message_admins("[ADMIN_LOOKUPFLW(target.current)] has gone AFK with a mutiny objective that involves them. They only have [COOLDOWN_TIMELEFT(src, disconnect_timer) / 10] seconds remaining before they are treated as if they were dead.") /datum/objective/mutiny/check_completion() - if(!target || !considered_alive(target) || considered_afk(target) || considered_exiled(target)) + if(!target || !considered_alive(target) || considered_exiled(target)) return TRUE + + if(considered_afk(target)) + if(!COOLDOWN_STARTED(src, disconnect_timer)) + COOLDOWN_START(src, disconnect_timer, DISCONNECT_GRACE_TIME) + warn_admins() + else if(COOLDOWN_FINISHED(src, disconnect_timer)) + return TRUE + else if(COOLDOWN_TIMELEFT(src, disconnect_timer) <= DISCONNECT_GRACE_WARNING_TIME && !warned_admins) + warned_admins = TRUE + warn_admins() + else + COOLDOWN_RESET(src, disconnect_timer) + warned_admins = FALSE + var/turf/T = get_turf(target.current) return !T || !is_station_level(T.z) +#undef DISCONNECT_GRACE_TIME +#undef DISCONNECT_GRACE_WARNING_TIME + + + /datum/objective/mutiny/update_explanation_text() ..() if(target?.current) diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index e88730f7026..3e759bdc362 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -397,6 +397,9 @@ /// List of all ex-revs. Useful because dynamic removes antag status when it ends, so this can be kept for the roundend report. var/list/ex_revs = list() + /// The objective of the heads of staff, aka to kill the headrevs. + var/list/datum/objective/mutiny/heads_objective = list() + /// Proc called on periodic timer. /// Updates the rev team's objectives to make sure all heads are targets, useful when new heads latejoin. /// Propagates all objectives to all revs. @@ -472,11 +475,34 @@ /// Checks if heads have won /datum/team/revolution/proc/check_heads_victory() - for(var/datum/mind/rev_mind in get_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)) - return FALSE - return TRUE + // List of headrevs we're currently tracking + var/list/included_headrevs = list() + // List of current headrevs + var/list/current_headrevs = get_head_revolutionaries() + // A copy of the head of staff objective list, since we're going to be modifying the original list. + var/list/heads_objective_copy = heads_objective.Copy() + + var/objective_complete = TRUE + // Here, we check current head of staff objectives and remove them if the target doesn't exist as a headrev anymore + for(var/datum/objective/mutiny/objective in heads_objective_copy) + if(!(objective.target in current_headrevs)) + heads_objective -= objective + continue + if(!objective.check_completion()) + objective_complete = FALSE + included_headrevs += objective.target + + // Here, we check current headrevs and add them as objectives if they didn't exist as a head of staff objective before. + // Additionally, we make sure the objective is not completed by running the check_completion check on them. + for(var/datum/mind/rev_mind as anything in current_headrevs) + if(!(rev_mind in included_headrevs)) + var/datum/objective/mutiny/objective = new() + objective.target = rev_mind + if(!objective.check_completion()) + objective_complete = FALSE + heads_objective += objective + + return objective_complete /// 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.