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