From 3f8aff437461579802fbb68003fad89beaf3c979 Mon Sep 17 00:00:00 2001 From: DGamerL <108773801+DGamerL@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:09:14 +0200 Subject: [PATCH] Delays handing out of Traitor objectives (#24857) * Idk man * IT WORKS * SO CLOSE TO GREATNESS * IT ALL WORKS AAAAAA * Actually add a random timer * Oopsie * Removes empty proc * Update code/game/gamemodes/traitor/traitor.dm Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com> * Contra requested changes * Adds randomness to random_time * Whoops * Contra + AA Review * Removes TODO * Reverts standard mode * Makes CI not fail * Removes unused proc * FUCK * Fixes a kit bug (Thanks Erik) * Tourte Review * Matt review * Changes text, adds admin logs * Clarifies the log * New and improved version! * Fixes * Revert "Fixes" This reverts commit ea29aa92b2d8b62b55f6ec563d48d20ce40d8e9b. * Revert "New and improved version!" This reverts commit ea053f3b9421740efc3d73a2f864c5828dfa817e. * Properly rerolls objectives at handout * Adds trifecta support * Contra and Sirryan review * Forgor this * Fix CI * Final fixes * Update code/game/gamemodes/traitor/traitor.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Update code/game/gamemodes/trifecta/trifecta.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Steel review * Wow I was silly * Less whitespace * Dumbest bug in a while * No more lavaland player getting autobalanced * Streamlines a proc + another fix * AAAAA FUCK * Update code/game/gamemodes/objective.dm Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Update code/game/gamemodes/objective.dm Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Contra review * Faire review * Forgor to push this * Farie + Contra review * Hal + Contra review * Uses `replaceobjective` now + Fix * Farie review * Farie review * Farie review 2: electric bogaloo * Fix * Update code/game/gamemodes/objective_holder.dm Co-authored-by: Farie82 Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Contra review * Update code/game/gamemodes/objective_holder.dm Co-authored-by: Farie82 Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> --------- Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com> Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Co-authored-by: Farie82 --- code/__DEFINES/antag_defines.dm | 10 ++ code/controllers/subsystem/SSticker.dm | 2 +- code/datums/mind.dm | 31 +++--- code/game/gamemodes/game_mode.dm | 102 +++++++++++++----- code/game/gamemodes/objective.dm | 41 +++++-- code/game/gamemodes/objective_holder.dm | 8 +- code/game/gamemodes/steal_items.dm | 3 - code/game/gamemodes/traitor/traitor.dm | 38 +++++-- code/game/gamemodes/trifecta/trifecta.dm | 32 +++++- .../antagonists/_common/antag_datum.dm | 20 ++-- .../antagonists/traitor/datum_traitor.dm | 33 ++++-- 11 files changed, 239 insertions(+), 81 deletions(-) diff --git a/code/__DEFINES/antag_defines.dm b/code/__DEFINES/antag_defines.dm index 24cfb19d97e45..01a9b291e6a2d 100644 --- a/code/__DEFINES/antag_defines.dm +++ b/code/__DEFINES/antag_defines.dm @@ -62,6 +62,16 @@ GLOBAL_LIST(contractors) */ #define PULSEDEMON_SOURCE_DRAIN_INVALID (-1) +/** + * Objectives + */ +#define THEFT_FLAG_SPECIAL 1 // Unused, maybe someone will use it some day, I'll leave it here for the children +#define THEFT_FLAG_UNIQUE 2 + +/** + * IS_ANTAG defines + */ #define IS_CHANGELING(mob) (isliving(mob) && mob?:mind?:has_antag_datum(/datum/antagonist/changeling)) #define IS_MINDSLAVE(mob) (ishuman(mob) && mob?:mind?:has_antag_datum(/datum/antagonist/mindslave, FALSE)) + diff --git a/code/controllers/subsystem/SSticker.dm b/code/controllers/subsystem/SSticker.dm index 4a197abfffa79..bc19b6c93fd33 100644 --- a/code/controllers/subsystem/SSticker.dm +++ b/code/controllers/subsystem/SSticker.dm @@ -225,7 +225,7 @@ SUBSYSTEM_DEF(ticker) P.ready = FALSE //Configure mode and assign player to special mode stuff - mode.pre_pre_setup() + var/can_continue = FALSE can_continue = mode.pre_setup() //Setup special modes. This also does the antag fishing checks. diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 6b4aab83cb3f0..5fbe68087c42a 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1540,29 +1540,30 @@ * Create and/or add the `datum_type_or_instance` antag datum to the src mind. * * Arguments: - * * datum_type - an antag datum typepath or instance + * * antag_datum - an antag datum typepath or instance. If it's a typepath, it will create a new antag datum * * datum/team/team - the antag team that the src mind should join, if any */ -/datum/mind/proc/add_antag_datum(datum_type_or_instance, datum/team/team = null) - var/datum/antagonist/A +/datum/mind/proc/add_antag_datum(datum_type_or_instance, datum/team/team) + var/datum/antagonist/antag_datum if(!ispath(datum_type_or_instance)) - A = datum_type_or_instance - if(!istype(A)) + antag_datum = datum_type_or_instance + if(!istype(antag_datum)) return else - A = new datum_type_or_instance() - if(!A.can_be_owned(src)) - qdel(A) + antag_datum = new datum_type_or_instance() + + if(!antag_datum.can_be_owned(src)) + qdel(antag_datum) return - A.owner = src - LAZYADD(antag_datums, A) - A.create_team(team) - var/datum/team/antag_team = A.get_team() + antag_datum.owner = src + LAZYADD(antag_datums, antag_datum) + antag_datum.create_team(team) + var/datum/team/antag_team = antag_datum.get_team() if(antag_team) antag_team.add_member(src) - ASSERT(A.owner && A.owner.current) - A.on_gain() - return A + ASSERT(antag_datum.owner && antag_datum.owner.current) + antag_datum.on_gain() + return antag_datum /** * Remove the specified `datum_type` antag datum from the src mind. diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 416f7f9272d80..866d264108f0e 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -104,23 +104,17 @@ /datum/game_mode/proc/can_start() var/playerC = 0 for(var/mob/new_player/player in GLOB.player_list) - if((player.client)&&(player.ready)) + if((player.client) && (player.ready)) playerC++ if(!GLOB.configuration.gamemode.enable_gamemode_player_limit || (playerC >= required_players)) - return 1 - return 0 - -//pre_pre_setup() For when you really don't want certain jobs ingame. -/datum/game_mode/proc/pre_pre_setup() - - return 1 + return TRUE + return FALSE ///pre_setup() ///Attempts to select players for special roles the mode might have. /datum/game_mode/proc/pre_setup() - - return 1 + return TRUE ///post_setup() @@ -137,12 +131,12 @@ GLOB.start_state = new /datum/station_state() GLOB.start_state.count() - return 1 + return TRUE ///process() ///Called by the gameticker /datum/game_mode/process() - return 0 + return FALSE // I wonder what this could do guessing by the name /datum/game_mode/proc/set_mode_in_db() @@ -264,11 +258,9 @@ if(rev_team) rev_team.check_all_victory() -/datum/game_mode/proc/get_players_for_role(role, override_jobbans=0) +/datum/game_mode/proc/get_players_for_role(role, override_jobbans = FALSE) var/list/players = list() var/list/candidates = list() - //var/list/drafted = list() - //var/datum/mind/applicant = null var/roletext = get_roletext(role) @@ -293,29 +285,56 @@ // Remove candidates who want to be antagonist but have a job that precludes it if(restricted_jobs) for(var/datum/mind/player in candidates) - for(var/job in restricted_jobs) - if(player.assigned_role == job) - candidates -= player + if(player.assigned_role in restricted_jobs) + candidates -= player return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, // Less if there are not enough valid players in the game entirely to make recommended_enemies. +// Just the above proc but for alive players +/// Gets all alive players for a specific role. Disables offstation roles by default +/datum/game_mode/proc/get_alive_players_for_role(role, override_jobbans = FALSE, allow_offstation_roles = FALSE) + var/list/players = list() + var/list/candidates = list() + + var/roletext = get_roletext(role) + + // Assemble a list of active players without jobbans. + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!player.client || (locate(player) in SSafk.afk_players)) + continue + if(!jobban_isbanned(player, ROLE_SYNDICATE) && !jobban_isbanned(player, roletext)) + players += player + + // Shuffle the players list so that it becomes ping-independent. + players = shuffle(players) + + // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species + for(var/mob/living/carbon/human/player in players) + if(player.client.skip_antag || !(allow_offstation_roles || !player.mind?.offstation_role)) + continue + + if(!(role in player.client.prefs.be_special) || (player.client.prefs.active_character.species in protected_species)) + continue + + player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them." + candidates += player.mind + players -= player + + // Remove candidates who want to be antagonist but have a job that precludes it + if(restricted_jobs) + for(var/datum/mind/player in candidates) + if(player.assigned_role in restricted_jobs) + candidates -= player + return candidates /datum/game_mode/proc/latespawn(mob) if(rev_team) rev_team.update_team_objectives() rev_team.process_promotion(REVOLUTION_PROMOTION_OPTIONAL) - -/* -/datum/game_mode/proc/check_player_role_pref(role, mob/player) - if(player.preferences.be_special & role) - return 1 - return 0 -*/ - /datum/game_mode/proc/num_players() . = 0 for(var/mob/new_player/P in GLOB.player_list) @@ -606,3 +625,34 @@ . += auto_declare_completion_revolution() . += auto_declare_completion_abduction() listclearnulls(.) + +/// Returns how many traitors should be added to the round +/datum/game_mode/proc/traitors_to_add() + return 0 + +/datum/game_mode/proc/fill_antag_slots() + var/traitors_to_add = 0 + + traitors_to_add += traitors_to_add() + + if(length(traitors) < traitors_to_add()) + traitors_to_add += (traitors_to_add() - length(traitors)) + + if(!traitors_to_add) + return + + var/list/potential_recruits = get_alive_players_for_role(ROLE_TRAITOR) + for(var/datum/mind/candidate as anything in potential_recruits) + if(candidate.special_role) // no traitor vampires or changelings or traitors or wizards or ... yeah you get the deal + potential_recruits.Remove(candidate) + + if(!length(potential_recruits)) + return + + log_admin("Attempting to add [traitors_to_add] traitors to the round. There are [length(potential_recruits)] potential recruits.") + + for(var/i in 1 to traitors_to_add) + var/datum/mind/traitor = pick_n_take(potential_recruits) + traitor.special_role = SPECIAL_ROLE_TRAITOR + traitor.restricted_roles = restricted_jobs + traitor.add_antag_datum(/datum/antagonist/traitor) // They immediately get a new objective diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 235845fc80962..931e04b8f30fa 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -35,7 +35,10 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) var/datum/objective_holder/holder -/datum/objective/New(text, datum/team/team_to_join) + /// What is the text we show when our objective is delayed? + var/delayed_objective_text = "This is a bug! Report it on the github and ask an admin what type of objective" + +/datum/objective/New(text, datum/team/team_to_join, datum/mind/_owner) . = ..() SHOULD_CALL_PARENT(TRUE) GLOB.all_objectives += src @@ -43,6 +46,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) explanation_text = text if(team_to_join) team = team_to_join + if(_owner) + owner = _owner /datum/objective/Destroy() GLOB.all_objectives -= src @@ -120,7 +125,6 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) possible_targets += possible_target - if(length(possible_targets) > 0) target = pick(possible_targets) @@ -160,6 +164,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/assassinate name = "Assassinate" martyr_compatible = TRUE + delayed_objective_text = "Your objective is to assassinate another crewmember. You will receive further information in a few minutes." /datum/objective/assassinate/update_explanation_text() if(target?.current) @@ -181,6 +186,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/assassinateonce name = "Assassinate once" martyr_compatible = TRUE + delayed_objective_text = "Your objective is to teach another crewmember a lesson. You will receive further information in a few minutes." var/won = FALSE /datum/objective/assassinateonce/update_explanation_text() @@ -244,6 +250,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/maroon name = "Maroon" martyr_compatible = FALSE + delayed_objective_text = "Your objective is to make sure another crewmember doesn't leave on the Escape Shuttle. You will receive further information in a few minutes." /datum/objective/maroon/update_explanation_text() if(target?.current) @@ -265,11 +272,11 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) return TRUE return TRUE - /// I want braaaainssss /datum/objective/debrain name = "Debrain" martyr_compatible = FALSE + delayed_objective_text = "Your objective is to steal another crewmember's brain. You will receive further information in a few minutes." /datum/objective/debrain/is_invalid_target(datum/mind/possible_target) . = ..() @@ -433,7 +440,6 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) return TRUE - /datum/objective/escape/escape_with_identity name = null /// Stored because the target's `[mob/var/real_name]` can change over the course of the round. @@ -526,9 +532,10 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/steal name = "Steal Item" - var/datum/theft_objective/steal_target martyr_compatible = FALSE + delayed_objective_text = "Your objective is to steal a high-value item. You will receive further information in a few minutes." var/theft_area + var/datum/theft_objective/steal_target /datum/objective/steal/found_target() return steal_target @@ -552,7 +559,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) continue if(!O.check_objective_conditions()) continue - if(O.flags & 2) // THEFT_FLAG_UNIQUE + if(O.flags & THEFT_FLAG_UNIQUE) continue steal_target = O @@ -649,7 +656,6 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) to_chat(failed_receiver, "Unfortunately, you weren't able to get a stealing kit. This is very bad and you should adminhelp immediately (press F1).") message_admins("[ADMIN_LOOKUPFLW(failed_receiver)] Failed to spawn with their [item_path] theft kit.") - /datum/objective/absorb name = "Absorb DNA" needs_target = FALSE @@ -691,6 +697,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/destroy name = "Destroy AI" martyr_compatible = TRUE + delayed_objective_text = "Your objective is to destroy an Artificial Intelligence. You will receive further information in a few minutes." /datum/objective/destroy/find_target(list/target_blacklist) var/list/possible_targets = active_ais(1) @@ -823,3 +830,23 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) explanation_text = "Hunger grows within us, we need to feast on the brains of the uninfected. Scratch, bite, and spread the plague." needs_target = FALSE completed = TRUE + +// Placeholder objectives that will replace themselves + +/datum/objective/delayed + needs_target = FALSE + var/datum/objective/objective_to_replace_with + +/datum/objective/delayed/New(datum/objective/delayed_objective) + ..() + if(!ispath(delayed_objective)) + stack_trace("A delayed objective has been given a non-path. Given was instead [delayed_objective]") + return + objective_to_replace_with = delayed_objective + explanation_text = initial(delayed_objective.delayed_objective_text) + +/datum/objective/delayed/update_explanation_text() + return + +/datum/objective/delayed/proc/reveal_objective() + return holder.replace_objective(src, objective_to_replace_with) diff --git a/code/game/gamemodes/objective_holder.dm b/code/game/gamemodes/objective_holder.dm index 2b65e1eb7b47a..057d07d6def4f 100644 --- a/code/game/gamemodes/objective_holder.dm +++ b/code/game/gamemodes/objective_holder.dm @@ -59,9 +59,11 @@ * Replace old_objective with new_objective */ /datum/objective_holder/proc/replace_objective(datum/objective/old_objective, datum/objective/new_objective) + if(ispath(new_objective)) + new_objective = new(null, old_objective.team, old_objective.owner) + new_objective = add_objective(new_objective, add_to_list = FALSE) - new_objective.owner = old_objective.owner - new_objective.team = old_objective.team + // Replace where the old objective was, with the new one objectives.Insert(objectives.Find(old_objective), new_objective) remove_objective(old_objective) @@ -106,7 +108,7 @@ /datum/objective_holder/proc/handle_objective(datum/objective/Objective) for(var/loop in 1 to 5) Objective.find_target(assigned_targets) - if(Objective.found_target()) // handles normal objectives, and steal objectives + if(Objective.found_target()) // Handles normal objectives, and steal objectives return // We failed to find any target. Oh well... diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index c74a0200a685e..1122ec65ef190 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -2,9 +2,6 @@ // // Separated into datums so we can prevent roles from getting certain objectives. -#define THEFT_FLAG_SPECIAL 1//unused, maybe someone will use it some day, I'll leave it here for the children -#define THEFT_FLAG_UNIQUE 2 - /datum/theft_objective var/name = "this objective is impossible, yell at a coder" var/typepath=/obj/effect/debugging diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index e8c2249efdd83..9dda5bec3f58b 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -17,7 +17,6 @@ to_chat(world, "The current game mode is - Traitor!") to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") - /datum/game_mode/traitor/pre_setup() if(GLOB.configuration.gamemode.prevent_mindshield_antags) @@ -35,10 +34,7 @@ var/num_traitors = 1 - if(GLOB.configuration.gamemode.traitor_scaling) - num_traitors = max(1, round((num_players())/(traitor_scaling_coeff))) - else - num_traitors = max(1, min(num_players(), traitors_possible)) + num_traitors = traitors_to_add() for(var/i in 1 to num_traitors) if(!length(possible_traitors)) @@ -52,13 +48,35 @@ return FALSE return TRUE - /datum/game_mode/traitor/post_setup() - for(var/t in pre_traitors) - var/datum/mind/traitor = t - traitor.add_antag_datum(/datum/antagonist/traitor) - ..() + . = ..() + + var/random_time = rand(5 MINUTES, 15 MINUTES) + if(length(pre_traitors)) + addtimer(CALLBACK(src, PROC_REF(fill_antag_slots)), random_time) + + for(var/datum/mind/traitor in pre_traitors) + var/datum/antagonist/traitor/traitor_datum = new(src) + if(ishuman(traitor.current)) + traitor_datum.delayed_objectives = TRUE + traitor_datum.addtimer(CALLBACK(traitor_datum, TYPE_PROC_REF(/datum/antagonist/traitor, reveal_delayed_objectives)), random_time, TIMER_DELETE_ME) + + traitor.add_antag_datum(traitor_datum) + +/datum/game_mode/traitor/traitors_to_add() + if(GLOB.configuration.gamemode.traitor_scaling) + . = max(1, round(num_players() / traitor_scaling_coeff)) + else + . = max(1, min(num_players(), traitors_possible)) + + if(!length(traitors)) + return + for(var/datum/mind/traitor_mind as anything in traitors) + if(!QDELETED(traitor_mind) && traitor_mind.current) // Explicitly no client check in case you happen to fall SSD when this gets ran + continue + .++ + traitors -= traitor_mind /datum/game_mode/traitor/declare_completion() ..() diff --git a/code/game/gamemodes/trifecta/trifecta.dm b/code/game/gamemodes/trifecta/trifecta.dm index 61bd9188ffac9..13ed99be84a5b 100644 --- a/code/game/gamemodes/trifecta/trifecta.dm +++ b/code/game/gamemodes/trifecta/trifecta.dm @@ -20,14 +20,16 @@ var/amount_vamp = 1 var/amount_cling = 1 var/amount_tot = 1 + /// How many points did we get at roundstart + var/cost_at_roundstart /datum/game_mode/trifecta/announce() to_chat(world, "The current game mode is - Trifecta") to_chat(world, "Vampires, traitors, and changelings, oh my! Stay safe as these forces work to bring down the station.") - /datum/game_mode/trifecta/pre_setup() calculate_quantities() + cost_at_roundstart = num_players() if(GLOB.configuration.gamemode.prevent_mindshield_antags) restricted_jobs += protected_jobs var/list/datum/mind/possible_vampires = get_players_for_role(ROLE_VAMPIRE) @@ -101,12 +103,38 @@ /datum/game_mode/trifecta/post_setup() for(var/datum/mind/vampire as anything in pre_vampires) vampire.add_antag_datum(/datum/antagonist/vampire) + for(var/datum/mind/changeling as anything in pre_changelings) changeling.add_antag_datum(/datum/antagonist/changeling) + for(var/datum/mind/traitor as anything in pre_traitors) - traitor.add_antag_datum(/datum/antagonist/traitor) + var/datum/antagonist/traitor/tot_datum = new() + tot_datum.delayed_objectives = TRUE + traitor.add_antag_datum(tot_datum) + + if(length(pre_traitors)) + var/random_time = rand(5 MINUTES, 15 MINUTES) + addtimer(CALLBACK(src, PROC_REF(fill_antag_slots)), random_time) + ..() +/datum/game_mode/trifecta/traitors_to_add() + . = 0 + for(var/datum/mind/traitor_mind as anything in traitors) + if(!QDELETED(traitor_mind) && traitor_mind.current) // Explicitly no client check in case you happen to fall SSD when this gets ran + continue + .++ + traitors -= traitor_mind + + var/extra_points = num_players_started() - cost_at_roundstart + if(extra_points - TOT_COST < 0) + return 0 // Not enough new players to add extra tots + + while(extra_points) + .++ + if(extra_points < TOT_COST) // The leftover change is enough for us to buy another traitor with, what a deal! + return + extra_points -= TOT_COST #undef TOT_COST #undef VAMP_COST diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 28c6d0ce9ec72..fa4f1eb694d68 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -55,6 +55,9 @@ GLOBAL_LIST_EMPTY(antagonists) var/blurb_b = 0 var/blurb_a = 0 + /// Do we have delayed objective giving? + var/delayed_objectives = FALSE + /datum/antagonist/New() GLOB.antagonists += src objective_holder = new(src) @@ -236,14 +239,15 @@ GLOBAL_LIST_EMPTY(antagonists) * * explanation_text - the explanation text that will be passed into the objective's `New()` proc * * mob/target_override - a target for the objective */ -/datum/antagonist/proc/add_antag_objective(datum/objective/O, explanation_text, mob/target_override) - if(ispath(O)) - O = new O() - if(O.owner) - stack_trace("[O], [O.type] was assigned as an objective to [owner] (mind), but already had an owner: [O.owner] (mind). Overriding.") - O.owner = owner - - return objective_holder.add_objective(O, explanation_text, target_override) +/datum/antagonist/proc/add_antag_objective(datum/objective/objective_to_add, explanation_text, mob/target_override) + if(ispath(objective_to_add)) + objective_to_add = new objective_to_add() + + if(objective_to_add.owner) + stack_trace("[objective_to_add], [objective_to_add.type] was assigned as an objective to [owner] (mind), but already had an owner: [objective_to_add.owner] (mind). Overriding.") + objective_to_add.owner = owner + + return objective_holder.add_objective(objective_to_add, explanation_text, target_override) /** * Complement to add_antag_objective that removes the objective. diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 3657a61b69785..f4ad986352783 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -136,19 +136,29 @@ RESTRICT_TYPE(/datum/antagonist/traitor) * Create and assign a single randomized human traitor objective. */ /datum/antagonist/traitor/proc/forge_single_human_objective() + var/datum/objective/objective_to_add + if(prob(50)) if(length(active_ais()) && prob(100 / length(GLOB.player_list))) - add_antag_objective(/datum/objective/destroy) + objective_to_add = /datum/objective/destroy + else if(prob(5)) - add_antag_objective(/datum/objective/debrain) + objective_to_add = /datum/objective/debrain + else if(prob(30)) - add_antag_objective(/datum/objective/maroon) + objective_to_add = /datum/objective/maroon + else if(prob(30)) - add_antag_objective(/datum/objective/assassinateonce) + objective_to_add = /datum/objective/assassinateonce + else - add_antag_objective(/datum/objective/assassinate) + objective_to_add = /datum/objective/assassinate else - add_antag_objective(/datum/objective/steal) + objective_to_add = /datum/objective/steal + + if(delayed_objectives) + objective_to_add = new /datum/objective/delayed(objective_to_add) + add_antag_objective(objective_to_add) /** * Give human traitors their uplink, and AI traitors their law 0. Play the traitor an alert sound. @@ -267,3 +277,14 @@ RESTRICT_TYPE(/datum/antagonist/traitor) /datum/antagonist/traitor/custom_blurb() return "[GLOB.current_date_string], [station_time_timestamp()]\n[station_name()], [get_area_name(owner.current, TRUE)]\nBEGIN_MISSION" + +/datum/antagonist/traitor/proc/reveal_delayed_objectives() + for(var/datum/objective/delayed/delayed_obj in objective_holder.objectives) + delayed_obj.reveal_objective() + + if(!owner?.current) + return + SEND_SOUND(owner.current, sound('sound/ambience/alarm4.ogg')) + var/list/messages = owner.prepare_announce_objectives() + to_chat(owner.current, chat_box_red(messages.Join("
"))) + delayed_objectives = FALSE