Initial stuff.

This commit is contained in:
Ghommie
2019-11-10 06:30:47 +01:00
parent c6550291b9
commit 3ddbee8878
5 changed files with 87 additions and 149 deletions
+21 -43
View File
@@ -118,7 +118,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("<html><head><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
dat += "Dynamic Mode <a href='?_src_=vars;[HrefToken()];Vars=[REF(src)]'>\[VV\]</A><BR>"
dat += "Dynamic Mode <a href='?_src_=vars;[HrefToken()];Vars=[REF(src)]'>\[VV\]</A><a href='?src=\ref[src];[HrefToken()]'>\[Refresh\]</A><BR>"
dat += "Threat Level: <b>[threat_level]</b><br/>"
dat += "Threat to Spend: <b>[threat]</b> <a href='?src=\ref[src];[HrefToken()];adjustthreat=1'>\[Adjust\]</A> <a href='?src=\ref[src];[HrefToken()];threatlog=1'>\[View Log\]</a><br/>"
@@ -273,15 +273,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
threat = threat_level
/datum/game_mode/dynamic/can_start()
/* Disabled for now, had some changes that need to be tested and this might interfere with that.
if(GLOB.dynamic_curve_centre == 0)
// 10 is when the centre starts to decrease
// 6 is just 1 + 5 (from the maximum value and the one decreased)
// 1 just makes the curve look better, I don't know.
// Limited between 1 and 5 then inverted and rounded
// With this you get a centre curve that stays at -5 until 10 then first rapidly decreases but slows down at the end
GLOB.dynamic_curve_centre = round(-CLAMP((10*6/GLOB.player_list.len)-1, 0, 5), 0.5)
*/
message_admins("Dynamic mode parameters for the round:")
message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
@@ -342,8 +333,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
rule.candidates.Cut() // The rule should not use candidates at this point as they all are null.
if(!rule.execute())
stack_trace("The starting rule \"[rule.name]\" failed to execute.")
if(rule.delay > 0)
addtimer(CALLBACK(rule, /datum/dynamic_ruleset/roundstart.proc/execute), rule.delay)
else
if(!rule.execute())
stack_trace("The starting rule \"[rule.name]\" failed to execute.")
..()
/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it.
@@ -427,16 +421,18 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(drafted_rules.len <= 0)
return FALSE
starting_rule = pickweight(drafted_rules)
// With low pop and high threat there might be rulesets that get executed with no valid candidates.
else if(starting_rule.ready())
drafted_rules -= starting_rule
if(drafted_rules.len <= 0)
return FALSE
starting_rule = pickweight(drafted_rules)
log_game("DYNAMIC: Picking a [istype(starting_rule, /datum/dynamic_ruleset/roundstart/delayed/) ? " delayed " : ""] ruleset [starting_rule.name]")
log_game("DYNAMIC: Picking a ruleset [starting_rule.name]")
roundstart_rules -= starting_rule
drafted_rules -= starting_rule
if (istype(starting_rule, /datum/dynamic_ruleset/roundstart/delayed/))
var/datum/dynamic_ruleset/roundstart/delayed/rule = starting_rule
addtimer(CALLBACK(src, .proc/execute_delayed, rule), rule.delay)
starting_rule.trim_candidates()
if (starting_rule.pre_execute())
spend_threat(starting_rule.cost)
@@ -448,29 +444,14 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
executed_rules += starting_rule
if (starting_rule.persistent)
current_rules += starting_rule
for(var/mob/M in starting_rule.assigned)
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
if (!rule.ready())
drafted_rules -= rule // And removing rules that are no longer elligible
for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules)
if (!rule.ready())
drafted_rules -= rule // And removing rules that are no longer elligible
return TRUE
else
stack_trace("The starting rule \"[starting_rule.name]\" failed to pre_execute.")
return FALSE
/// Executes delayed roundstart rules and has a hack in it.
/datum/game_mode/dynamic/proc/execute_delayed(datum/dynamic_ruleset/roundstart/delayed/rule)
update_playercounts()
rule.candidates = current_players[CURRENT_LIVING_PLAYERS].Copy()
rule.trim_candidates()
if(rule.execute())
executed_rules += rule
if (rule.persistent)
current_rules += rule
return TRUE
else
stack_trace("The delayed roundstart rule \"[rule.name]\" failed to execute.")
return FALSE
/// Picks a random midround OR latejoin rule from the list given as an argument and executes it.
/// Also this could be named better.
/datum/game_mode/dynamic/proc/picking_midround_latejoin_rule(list/drafted_rules = list(), forced = FALSE)
@@ -550,7 +531,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
update_playercounts()
if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat)))
new_rule.candidates = current_players.Copy()
new_rule.trim_candidates()
if (new_rule.ready(forced))
spend_threat(new_rule.cost)
@@ -582,7 +562,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if (GLOB.dynamic_forced_extended)
return
// Somehow it manages to trigger midround multiple times so this was moved here.
// Somehow it managed to trigger midround multiple times so this was moved here.
// There is no way this should be able to trigger an injection twice now.
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
midround_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time)
@@ -591,21 +571,19 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone
return
log_game("DYNAMIC: Checking state of the round.")
message_admins("DYNAMIC: Checking for midround injection.")
log_game("DYNAMIC: Checking for midround injection.")
update_playercounts()
if (prob(get_injection_chance()))
if (get_injection_chance())
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.candidates = list()
rule.candidates = current_players.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
if (rule.ready())
drafted_rules[rule] = rule.get_weight()
if (drafted_rules.len > 0)
picking_midround_latejoin_rule(drafted_rules)
+12 -45
View File
@@ -86,10 +86,9 @@
/datum/dynamic_ruleset/roundstart // One or more of those drafted at roundstart
ruletype = "Roundstart"
/datum/dynamic_ruleset/roundstart/delayed/ // Executed with a 30 seconds delay
var/delay = 30 SECONDS
var/required_type = /mob/living/carbon/human // No ghosts, new players or silicons allowed.
/// Delay for when execute will get called from the time of post_setup.
/// Make sure your ruleset works with execute being called during the game when using this.
var/delay = 0
// Can be drafted when a player joins the server
/datum/dynamic_ruleset/latejoin
@@ -113,7 +112,7 @@
/datum/dynamic_ruleset/proc/rule_process()
return
/// Called on game mode pre_setup, used for non-delayed roundstart rulesets only.
/// Called on game mode pre_setup for roundstart rulesets.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
/datum/dynamic_ruleset/proc/pre_execute()
@@ -126,12 +125,6 @@
M.add_antag_datum(antag_datum)
return TRUE
/// Called after delay set in ruleset.
/// Give your candidates or assignees equipment and antag datum here.
/datum/dynamic_ruleset/roundstart/delayed/execute()
if (SSticker && SSticker.current_state < GAME_STATE_PLAYING)
CRASH("The delayed ruleset [name] executed before the round started.")
/// Here you can perform any additional checks you want. (such as checking the map etc)
/// Remember that on roundstart no one knows what their job is at this point.
/// IMPORTANT: If ready() returns TRUE, that means pre_execute() or execute() should never fail!
@@ -156,14 +149,6 @@
/datum/dynamic_ruleset/proc/trim_candidates()
return
/// Counts how many players are ready at roundstart.
/// Used only by non-delayed roundstart rulesets.
/datum/dynamic_ruleset/proc/num_players()
. = 0
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.client && P.ready == PLAYER_READY_TO_PLAY)
. ++
/// Set mode result and news report here.
/// Only called if ruleset is flagged as HIGHLANDER_RULESET
/datum/dynamic_ruleset/proc/round_result()
@@ -191,32 +176,14 @@
if(P.mind.special_role) // We really don't want to give antag to an antag.
candidates.Remove(P)
continue
if (!(antag_flag in P.client.prefs.be_special) || jobban_isbanned(P.ckey, list(antag_flag, ROLE_SYNDICATE)) || (antag_flag_override && jobban_isbanned(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))))//are they willing and not antag-banned?
candidates.Remove(P)
continue
/// Checks if candidates are required mob type, connected, banned and if the job is exclusive to the role.
/datum/dynamic_ruleset/roundstart/delayed/trim_candidates()
. = ..()
for (var/mob/P in candidates)
if (!istype(P, required_type))
candidates.Remove(P) // Can be a new_player, etc.
continue
if(!mode.check_age(P.client, minimum_required_age))
candidates.Remove(P)
continue
if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected?
candidates.Remove(P)
continue
if(P.mind.special_role || P.mind.antag_datums?.len > 0) // Are they an antag already?
candidates.Remove(P)
continue
if (!(antag_flag in P.client.prefs.be_special) || jobban_isbanned(P.ckey, list(antag_flag, ROLE_SYNDICATE)) || (antag_flag_override && jobban_isbanned(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))))//are they willing and not antag-banned?
candidates.Remove(P)
continue
if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
candidates.Remove(P)
continue
if(antag_flag_override)
if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
candidates.Remove(P)
continue
else
if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE)))
candidates.Remove(P)
continue
/// Do your checks if the ruleset is ready to be executed here.
/// Should ignore certain checks if forced is TRUE
@@ -12,9 +12,14 @@
if(!mode.check_age(P.client, minimum_required_age))
candidates.Remove(P)
continue
if (!(antag_flag in P.client.prefs.be_special) || jobban_isbanned(P.ckey, list(antag_flag, ROLE_SYNDICATE)) || (antag_flag_override && jobban_isbanned(P.ckey, list(antag_flag_override))))//are they willing and not antag-banned?
candidates.Remove(P)
continue
if(antag_flag_override)
if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
candidates.Remove(P)
continue
else
if(!antag_flag in P.client.prefs.be_special || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE)))
candidates.Remove(P)
continue
if (P.mind.assigned_role in restricted_roles) // Does their job allow for it?
candidates.Remove(P)
continue
@@ -8,7 +8,7 @@
ruletype = "Midround"
/// If the ruleset should be restricted from ghost roles.
var/restrict_ghost_roles = TRUE
/// What type the ruleset is restricted to.
/// What mob type the ruleset is restricted to.
var/required_type = /mob/living/carbon/human
var/list/living_players = list()
var/list/living_antags = list()
@@ -17,26 +17,18 @@
/datum/dynamic_ruleset/midround/from_ghosts
weight = 0
required_type = /mob/dead/observer
/// Whether the ruleset should call generate_ruleset_body or not.
var/makeBody = TRUE
/datum/dynamic_ruleset/midround/trim_candidates()
// Unlike the previous two types, these rulesets are not meant for /mob/dead/new_player
// And since I want those rulesets to be as flexible as possible, I'm not gonna put much here,
//
// All you need to know is that here, the candidates list contains 4 lists itself, indexed with the following defines:
// Candidates = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
// So for example you can get the list of all current dead players with var/list/dead_players = candidates[CURRENT_DEAD_PLAYERS]
// Make sure to properly typecheck the mobs in those lists, as the dead_players list could contain ghosts, or dead players still in their bodies.
// We're still gonna trim the obvious (mobs without clients, jobbanned players, etc)
living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS])
dead_players = trim_list(mode.current_players[CURRENT_DEAD_PLAYERS])
list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS])
living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS])
dead_players = trim_list(mode.current_players[CURRENT_DEAD_PLAYERS])
list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS])
/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list())
var/list/trimmed_list = L.Copy()
var/antag_name = initial(antag_flag)
for(var/mob/M in trimmed_list)
if (!istype(M, required_type))
trimmed_list.Remove(M)
@@ -47,9 +39,14 @@
if(!mode.check_age(M.client, minimum_required_age))
trimmed_list.Remove(M)
continue
if (!(antag_name in M.client.prefs.be_special) || jobban_isbanned(M.ckey, list(antag_name, ROLE_SYNDICATE)))//are they willing and not antag-banned?
trimmed_list.Remove(M)
continue
if(antag_flag_override)
if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
candidates.Remove(M)
continue
else
if(!antag_flag in M.client.prefs.be_special || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE)))
candidates.Remove(M)
continue
if (M.mind)
if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role?
trimmed_list.Remove(M)
@@ -256,8 +253,7 @@
/datum/dynamic_ruleset/midround/malf/execute()
if(!candidates || !candidates.len)
return FALSE
var/mob/living/silicon/ai/M = pick(candidates)
candidates -= M
var/mob/living/silicon/ai/M = pick_n_take(candidates)
assigned += M.mind
var/datum/antagonist/traitor/AI = new
M.mind.special_role = antag_flag
@@ -25,8 +25,7 @@
var/traitor_scaling_coeff = 10 - max(0,round(mode.threat_level/10)-5) // Above 50 threat level, coeff goes down by 1 for every 10 levels
var/num_traitors = min(round(mode.candidates.len / traitor_scaling_coeff) + 1, candidates.len)
for (var/i = 1 to num_traitors)
var/mob/M = pick(candidates)
candidates -= M
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.special_role = ROLE_TRAITOR
M.mind.restricted_roles = restricted_roles
@@ -67,7 +66,7 @@
var/num_teams = team_amount
var/bsc = CONFIG_GET(number/brother_scaling_coeff)
if(bsc)
num_teams = max(1, round(num_players() / bsc))
num_teams = max(1, round(mode.roundstart_pop_ready / bsc))
for(var/j = 1 to num_teams)
if(candidates.len < min_team_size || candidates.len < required_candidates)
@@ -75,8 +74,7 @@
var/datum/team/brother_team/team = new
var/team_size = prob(10) ? min(3, candidates.len) : 2
for(var/k = 1 to team_size)
var/mob/bro = pick(candidates)
candidates -= bro
var/mob/bro = pick_n_take(candidates)
assigned += bro.mind
team.add_member(bro.mind)
bro.mind.special_role = "brother"
@@ -117,8 +115,7 @@
/datum/dynamic_ruleset/roundstart/changeling/pre_execute()
var/num_changelings = min(round(mode.candidates.len / 10) + 1, candidates.len)
for (var/i = 1 to num_changelings)
var/mob/M = pick(candidates)
candidates -= M
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.restricted_roles = restricted_roles
M.mind.special_role = ROLE_CHANGELING
@@ -175,9 +172,8 @@
if(GLOB.wizardstart.len == 0)
return FALSE
var/mob/M = pick(candidates)
var/mob/M = pick_n_take(candidates)
if (M)
candidates -= M
assigned += M.mind
M.mind.assigned_role = ROLE_WIZARD
M.mind.special_role = ROLE_WIZARD
@@ -225,8 +221,7 @@
for(var/cultists_number = 1 to cultists)
if(candidates.len <= 0)
break
var/mob/M = pick(candidates)
candidates -= M
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.special_role = ROLE_CULTIST
M.mind.restricted_roles = restricted_roles
@@ -288,8 +283,7 @@
for(var/operatives_number = 1 to operatives)
if(candidates.len <= 0)
break
var/mob/M = pick(candidates)
candidates -= M
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.assigned_role = "Nuclear Operative"
M.mind.special_role = "Nuclear Operative"
@@ -347,7 +341,7 @@
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/roundstart/delayed/revs
/datum/dynamic_ruleset/roundstart/revs
name = "Revolution"
config_tag = "revolution"
persistent = TRUE
@@ -358,47 +352,48 @@
restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_candidates = 3
weight = 2
delay = 7 MINUTES
cost = 35
requirements = list(101,101,70,40,30,20,10,10,10,10)
high_population_requirement = 10
delay = 5 MINUTES
flags = HIGHLANDER_RULESET
// I give up, just there should be enough heads with 35 players...
minimum_players = 35
var/datum/team/revolution/revolution
var/finished = 0
/datum/dynamic_ruleset/roundstart/delayed/revs/execute()
var/max_canditates = 4
revolution = new()
/datum/dynamic_ruleset/roundstart/revs/pre_execute()
var/max_canditates = 3
for(var/i = 1 to max_canditates)
if(candidates.len <= 0)
break
var/mob/M = pick(candidates)
candidates -= M
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.restricted_roles = restricted_roles
M.mind.special_role = antag_flag
return TRUE
/datum/dynamic_ruleset/roundstart/revs/execute()
revolution = new()
for(var/datum/mind/M in assigned)
var/datum/antagonist/rev/head/new_head = new antag_datum()
new_head.give_flash = TRUE
new_head.give_hud = TRUE
new_head.remove_clumsy = TRUE
M.mind.add_antag_datum(new_head,revolution)
M.add_antag_datum(new_head,revolution)
revolution.update_objectives()
revolution.update_heads()
SSshuttle.registerHostileEnvironment(src)
return TRUE
/datum/dynamic_ruleset/roundstart/delayed/revs/rule_process()
/datum/dynamic_ruleset/roundstart/revs/rule_process()
if(check_rev_victory())
finished = 1
else if(check_heads_victory())
finished = 2
return
/datum/dynamic_ruleset/roundstart/delayed/revs/check_finished()
/datum/dynamic_ruleset/roundstart/revs/check_finished()
if(CONFIG_GET(keyed_list/continuous)["revolution"])
if(finished)
SSshuttle.clearHostileEnvironment(src)
@@ -408,13 +403,13 @@
else
return ..()
/datum/dynamic_ruleset/roundstart/delayed/revs/proc/check_rev_victory()
/datum/dynamic_ruleset/roundstart/revs/proc/check_rev_victory()
for(var/datum/objective/mutiny/objective in revolution.objectives)
if(!(objective.check_completion()))
return FALSE
return TRUE
/datum/dynamic_ruleset/roundstart/delayed/revs/proc/check_heads_victory()
/datum/dynamic_ruleset/roundstart/revs/proc/check_heads_victory()
for(var/datum/mind/rev_mind in revolution.head_revolutionaries())
var/turf/T = get_turf(rev_mind.current)
if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(T.z))
@@ -422,7 +417,7 @@
return FALSE
return TRUE
/datum/dynamic_ruleset/roundstart/delayed/revs/round_result()
/datum/dynamic_ruleset/roundstart/revs/round_result()
if(finished == 1)
SSticker.mode_result = "win - heads killed"
SSticker.news_report = REVS_WIN
@@ -487,14 +482,13 @@
PM.initTemplateBounds()
var/starter_servants = 4
var/number_players = num_players()
var/number_players = mode.roundstart_pop_ready
if(number_players > 30)
number_players -= 30
starter_servants += round(number_players / 10)
starter_servants = min(starter_servants, 8)
for (var/i in 1 to starter_servants)
var/mob/servant = pick(candidates)
candidates -= servant
var/mob/servant = pick_n_take(candidates)
assigned += servant.mind
servant.mind.assigned_role = ROLE_SERVANT_OF_RATVAR
servant.mind.special_role = ROLE_SERVANT_OF_RATVAR
@@ -610,16 +604,15 @@
var/num_devils = 1
if(tsc)
num_devils = max(required_candidates, min(round(num_players() / (tsc * 3)) + 2, round(num_players() / (tsc * 1.5))))
num_devils = max(required_candidates, min(round(mode.roundstart_pop_ready / (tsc * 3)) + 2, round(mode.roundstart_pop_ready / (tsc * 1.5))))
else
num_devils = max(required_candidates, min(num_players(), devil_limit))
num_devils = max(required_candidates, min(mode.roundstart_pop_ready, devil_limit))
for(var/j = 0, j < num_devils, j++)
if (!candidates.len)
break
var/mob/devil = pick(candidates)
assigned += devil
candidates -= devil
var/mob/devil = pick_n_take(candidates)
assigned += devil.mind
devil.mind.special_role = ROLE_DEVIL
devil.mind.restricted_roles = restricted_roles
@@ -668,13 +661,12 @@
var/datum/team/monkey/monkey_team
/datum/dynamic_ruleset/roundstart/monkey/pre_execute()
var/carriers_to_make = max(round(num_players()/players_per_carrier, 1), 1)
var/carriers_to_make = max(round(mode.roundstart_pop_ready / players_per_carrier, 1), 1)
for(var/j = 0, j < carriers_to_make, j++)
if (!candidates.len)
break
var/mob/carrier = pick(candidates)
candidates -= carrier
var/mob/carrier = pick_n_take(candidates)
assigned += carrier.mind
carrier.mind.special_role = "Monkey Leader"
carrier.mind.restricted_roles = restricted_roles