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 ea29aa92b2.

* Revert "New and improved version!"

This reverts commit ea053f3b94.

* 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 <farie82@users.noreply.github.com>
Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com>

* Contra review

* Update code/game/gamemodes/objective_holder.dm

Co-authored-by: Farie82 <farie82@users.noreply.github.com>
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 <farie82@users.noreply.github.com>
This commit is contained in:
DGamerL
2024-07-24 08:09:14 +00:00
committed by GitHub
co-authored by SteelSlayer Ryan Contrabang Farie82
parent 98f0931c14
commit 3f8aff4374
11 changed files with 238 additions and 80 deletions
+10
View File
@@ -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))
+1 -1
View File
@@ -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.
+16 -15
View File
@@ -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.
+76 -26
View File
@@ -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
+34 -7
View File
@@ -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, "<span class='userdanger'>Unfortunately, you weren't able to get a stealing kit. This is very bad and you should adminhelp immediately (press F1).</span>")
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)
+5 -3
View File
@@ -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...
-3
View File
@@ -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
+28 -10
View File
@@ -17,7 +17,6 @@
to_chat(world, "<B>The current game mode is - Traitor!</B>")
to_chat(world, "<B>There is a syndicate traitor on the station. Do not let the traitor succeed!</B>")
/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()
..()
+30 -2
View File
@@ -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, "<b>The current game mode is - Trifecta</b>")
to_chat(world, "<b>Vampires, traitors, and changelings, oh my! Stay safe as these forces work to bring down the station.</b>")
/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
@@ -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
/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()
return objective_holder.add_objective(O, explanation_text, target_override)
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.
@@ -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("<br>")))
delayed_objectives = FALSE