diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm index 3e56784bdbb..500f9aa419f 100644 --- a/code/game/gamemodes/dynamic/antag_rulesets.dm +++ b/code/game/gamemodes/dynamic/antag_rulesets.dm @@ -49,6 +49,8 @@ var/antag_amount = 0 /// All of the minds that we will make into our antagonist type var/list/datum/mind/pre_antags = list() + /// If non-zero, how long from the start of the game should a latespawn for this role occur? + var/latespawn_time /datum/ruleset/Destroy(force, ...) stack_trace("[src] ([type]) was destroyed.") @@ -63,7 +65,7 @@ /datum/ruleset/proc/antagonist_possible(budget) return budget >= antag_cost -/datum/ruleset/proc/pre_setup() +/datum/ruleset/proc/roundstart_pre_setup() if(antag_amount == 0) return if(antag_amount < 0) @@ -81,7 +83,7 @@ for(var/datum/mind/antag as anything in possible_antags) if(antag_amount <= 0) break - if(!can_apply(antag)) + if(!roundstart_can_apply(antag)) continue pre_antags += antag if(assign_job_role) @@ -94,7 +96,7 @@ refund("Missing [antag_amount] antagonists for [src] ruleset.") return antag_cost * antag_amount // shitty refund for now -/datum/ruleset/proc/can_apply(datum/mind/antag) +/datum/ruleset/proc/roundstart_can_apply(datum/mind/antag) if(EXCLUSIVE_OR(antag.current.client.prefs.active_character.species in banned_species, banned_species_only)) SEND_SIGNAL(src, COMSIG_RULESET_FAILED_SPECIES) return FALSE @@ -102,7 +104,7 @@ return FALSE return TRUE -/datum/ruleset/proc/post_setup(datum/game_mode/dynamic) +/datum/ruleset/proc/roundstart_post_setup(datum/game_mode/dynamic) for(var/datum/mind/antag as anything in pre_antags) antag.add_antag_datum(antagonist_type) @@ -113,6 +115,47 @@ // Currently unimplemented. Will be useful for a possible future PR where latejoin antagonists are factored in. return +/datum/ruleset/proc/get_latejoin_players() + var/list/candidates = list() + + // Assemble a list of active players without jobbans. + for(var/mob/living/carbon/human/player in GLOB.player_list) + // Has a mind + if(!player.mind) + continue + // Connected and not AFK + if(!player.client || (locate(player) in SSafk.afk_players)) + continue + // Not antag-banned and not specific antag banned + if(jobban_isbanned(player, ROLE_SYNDICATE) || jobban_isbanned(player, antagonist_type::job_rank)) + continue + // Make sure they want to play antag, and that they're not already something (off station or antag) + if(player.client.skip_antag || player.mind.offstation_role || player.mind.special_role) + continue + // Make sure they actually want to be this antagonist + if(!(antagonist_type::job_rank in player.client.prefs.be_special)) + continue + // Make sure their species CAN be this antagonist + if(EXCLUSIVE_OR(player.dna.species.name in banned_species, banned_species_only)) + continue + // Make sure they're not in a banned job + if(player.mind.assigned_role in banned_jobs) + continue + + candidates += player.mind + + return shuffle(candidates) + +/datum/ruleset/proc/latespawn(datum/game_mode/dynamic/dynamic) + // latespawning is only used by traitors at this point, so we're just going to be naive and allocate all budget when this proc is called. + var/late_antag_amount = floor(dynamic.budget_overflow / antag_cost) + dynamic.budget_overflow -= (late_antag_amount * antag_cost) + + var/list/datum/mind/possible_antags = get_latejoin_players() + for(var/i in 1 to late_antag_amount) + var/datum/mind/antag = pick_n_take(possible_antags) + antag.add_antag_datum(antagonist_type) + /datum/ruleset/traitor name = "Traitor" ruleset_weight = 11 @@ -120,15 +163,14 @@ antag_weight = 2 antagonist_type = /datum/antagonist/traitor -/datum/ruleset/traitor/post_setup(datum/game_mode/dynamic) - var/random_time = rand(5 MINUTES, 15 MINUTES) +/datum/ruleset/traitor/roundstart_post_setup(datum/game_mode/dynamic) + latespawn_time = rand(5 MINUTES, 15 MINUTES) for(var/datum/mind/antag as anything in pre_antags) var/datum/antagonist/traitor/traitor_datum = new antagonist_type() if(ishuman(antag.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_datum.addtimer(CALLBACK(traitor_datum, TYPE_PROC_REF(/datum/antagonist/traitor, reveal_delayed_objectives)), latespawn_time, TIMER_DELETE_ME) antag.add_antag_datum(traitor_datum) - addtimer(CALLBACK(dynamic, TYPE_PROC_REF(/datum/game_mode, fill_antag_slots)), random_time) /datum/ruleset/vampire name = "Vampire" diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 1e0e1ec4ad2..dfff3aa7aae 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -3,7 +3,6 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) /datum/game_mode/dynamic name = "Dynamic" config_tag = "dynamic" - secondary_restricted_jobs = list("AI") required_players = 10 /// Non-implied rulesets in play var/list/datum/ruleset/rulesets = list() @@ -126,7 +125,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets // log_dynamic("Applying [ruleset.antag_amount] [ruleset.name]\s.") - budget_overflow += ruleset.pre_setup() + budget_overflow += ruleset.roundstart_pre_setup() // log_dynamic("Budget overflow: [budget_overflow].") // for the future, maybe try readding antagonists with apply_antag_budget(budget_overflow) @@ -137,13 +136,11 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // if(length(ruleset.pre_antags)) // log_dynamic("Making antag datums for [ruleset.name] ruleset.") - ruleset.post_setup(src) + ruleset.roundstart_post_setup(src) + if(ruleset.latespawn_time) + addtimer(CALLBACK(ruleset, TYPE_PROC_REF(/datum/ruleset, latespawn), src), ruleset.latespawn_time) ..() -/datum/game_mode/dynamic/traitors_to_add() - . = floor(budget_overflow / /datum/ruleset/traitor::antag_cost) - budget_overflow -= (. * /datum/ruleset/traitor::antag_cost) - /datum/game_mode/dynamic/latespawn(mob) . = ..() budget_overflow++ diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index fe838e735d0..d072659317c 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -319,7 +319,10 @@ // 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 +/** + * DEPRECATED! + * 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() @@ -645,6 +648,9 @@ /datum/game_mode/proc/traitors_to_add() return 0 +/** + * DEPRECATED! + */ /datum/game_mode/proc/fill_antag_slots() var/traitors_to_add = 0