Merged upstream dynamic fixes ported by Ghommie.
This commit is contained in:
@@ -213,4 +213,5 @@
|
||||
#define LOCKED_HELMET_TRAIT "locked-helmet"
|
||||
#define NINJA_SUIT_TRAIT "ninja-suit"
|
||||
#define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant"
|
||||
#define ABDUCTOR_ANTAGONIST "abductor-antagonist"
|
||||
#define ABDUCTOR_ANTAGONIST "abductor-antagonist"
|
||||
#define MADE_UNCLONEABLE "made-uncloneable"
|
||||
@@ -317,6 +317,10 @@
|
||||
parts += "[FOURSPACES][FOURSPACES][str]"
|
||||
for(var/entry in mode.threat_tallies)
|
||||
parts += "[FOURSPACES][FOURSPACES][entry] added [mode.threat_tallies[entry]]"
|
||||
/*
|
||||
for(var/datum/dynamic_ruleset/rule in mode.executed_rules)
|
||||
parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - <b>[rule.name]</b>: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat"
|
||||
*/
|
||||
return parts.Join("<br>")
|
||||
|
||||
/client/proc/roundend_report_file()
|
||||
|
||||
@@ -26,7 +26,7 @@ GLOBAL_VAR_INIT(dynamic_first_midround_delay_max, (30 MINUTES))
|
||||
|
||||
// Are HIGHLANDER_RULESETs allowed to stack?
|
||||
GLOBAL_VAR_INIT(dynamic_no_stacking, TRUE)
|
||||
// A number between -5 and +5.
|
||||
// A number between -5 and +5.
|
||||
// A negative value will give a more peaceful round and
|
||||
// a positive value will give a round with higher threat.
|
||||
GLOBAL_VAR_INIT(dynamic_curve_centre, 0)
|
||||
@@ -34,7 +34,7 @@ GLOBAL_VAR_INIT(dynamic_curve_centre, 0)
|
||||
// Higher value will favour extreme rounds and
|
||||
// lower value rounds closer to the average.
|
||||
GLOBAL_VAR_INIT(dynamic_curve_width, 1.8)
|
||||
// If enabled only picks a single starting rule and executes only autotraitor midround ruleset.
|
||||
// If enabled only picks a single starting rule and executes only autotraitor midround ruleset.
|
||||
GLOBAL_VAR_INIT(dynamic_classic_secret, FALSE)
|
||||
// How many roundstart players required for high population override to take effect.
|
||||
GLOBAL_VAR_INIT(dynamic_high_pop_limit, 55)
|
||||
@@ -45,7 +45,7 @@ GLOBAL_VAR_INIT(dynamic_forced_extended, FALSE)
|
||||
GLOBAL_VAR_INIT(dynamic_stacking_limit, 90)
|
||||
// List of forced roundstart rulesets.
|
||||
GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset)
|
||||
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
|
||||
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
|
||||
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
/datum/game_mode/dynamic
|
||||
@@ -56,10 +56,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
announce_text = "Dynamic mode!" // This needs to be changed maybe
|
||||
|
||||
reroll_friendly = FALSE;
|
||||
|
||||
|
||||
// Threat logging vars
|
||||
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
|
||||
var/threat_level = 0
|
||||
var/threat_level = 0
|
||||
/// Set at the beginning of the round. Spent by the mode to "purchase" rules.
|
||||
var/threat = 0
|
||||
/// Starting threat level, for things that increase it but can bring it back down.
|
||||
@@ -85,14 +85,20 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
* 0-6, 7-13, 14-20, 21-27, 28-34, 35-41, 42-48, 49-55, 56-62, 63+
|
||||
*/
|
||||
var/pop_per_requirement = 9
|
||||
/// The requirement used for checking if a second rule should be selected.
|
||||
var/list/second_rule_req = list(101, 101, 101, 101, 100, 90, 80, 70, 60, 50)
|
||||
/// The requirement used for checking if a third rule should be selected.
|
||||
var/list/third_rule_req = list(101, 101, 101, 101, 101, 100, 90, 80, 70, 60)
|
||||
/// Threat requirement for a second ruleset when high pop override is in effect.
|
||||
var/high_pop_second_rule_req = 50
|
||||
/// Threat requirement for a third ruleset when high pop override is in effect.
|
||||
/// The requirement used for checking if a second rule should be selected. Index based on pop_per_requirement.
|
||||
var/list/second_rule_req = list(100, 100, 80, 70, 60, 50, 30, 20, 10, 0)
|
||||
/// The probability for a second ruleset with index being every ten threat.
|
||||
var/list/second_rule_prob = list(0,0,60,80,80,80,100,100,100,100)
|
||||
/// The requirement used for checking if a third rule should be selected. Index based on pop_per_requirement.
|
||||
var/list/third_rule_req = list(100, 100, 100, 90, 80, 70, 60, 50, 40, 30)
|
||||
/// The probability for a third ruleset with index being every ten threat.
|
||||
var/list/third_rule_prob = list(0,0,0,0,60,60,80,90,100,100)
|
||||
/// Threat requirement for a second ruleset when high pop override is in effect.
|
||||
var/high_pop_second_rule_req = 40
|
||||
/// Threat requirement for a third ruleset when high pop override is in effect.
|
||||
var/high_pop_third_rule_req = 60
|
||||
/// The amount of additional rulesets waiting to be picked.
|
||||
var/extra_rulesets_amount = 0
|
||||
/// Number of players who were ready on roundstart.
|
||||
var/roundstart_pop_ready = 0
|
||||
/// List of candidates used on roundstart rulesets.
|
||||
@@ -119,6 +125,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
var/highlander_executed = FALSE
|
||||
/// If a only ruleset has been executed.
|
||||
var/only_ruleset_executed = FALSE
|
||||
/// Antags rolled by rules so far, to keep track of and discourage scaling past a certain ratio of crew/antags especially on lowpop.
|
||||
var/antags_rolled = 0
|
||||
|
||||
/datum/game_mode/dynamic/New() // i have NO IDEA if this is the proper way to do this.
|
||||
..()
|
||||
@@ -143,7 +151,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/>"
|
||||
@@ -200,7 +208,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
show_threatlog(usr)
|
||||
else if (href_list["stacking_limit"])
|
||||
GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num
|
||||
|
||||
|
||||
admin_panel() // Refreshes the window
|
||||
|
||||
// Checks if there are HIGHLANDER_RULESETs and calls the rule's round_result() proc
|
||||
@@ -305,15 +313,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].")
|
||||
@@ -355,18 +354,21 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
candidates.Add(player)
|
||||
log_game("DYNAMIC: Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.")
|
||||
if (candidates.len <= 0)
|
||||
log_game("DYNAMIC: [candidates.len] candidates.")
|
||||
return TRUE
|
||||
if (roundstart_rules.len <= 0)
|
||||
log_game("DYNAMIC: [roundstart_rules.len] rules.")
|
||||
return TRUE
|
||||
|
||||
|
||||
if(GLOB.dynamic_forced_roundstart_ruleset.len > 0)
|
||||
rigged_roundstart()
|
||||
else
|
||||
else
|
||||
roundstart()
|
||||
|
||||
var/starting_rulesets = ""
|
||||
for (var/datum/dynamic_ruleset/roundstart/DR in executed_rules)
|
||||
starting_rulesets += "[DR.name], "
|
||||
log_game("DYNAMIC: Picked the following roundstart rules: [starting_rulesets]")
|
||||
candidates.Cut()
|
||||
return TRUE
|
||||
|
||||
@@ -374,9 +376,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
update_playercounts()
|
||||
|
||||
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.")
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_roundstart_rule, rule), rule.delay)
|
||||
..()
|
||||
|
||||
/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it.
|
||||
@@ -387,6 +387,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
message_admins("Drafting players for forced ruleset [rule.name].")
|
||||
log_game("DYNAMIC: Drafting players for forced ruleset [rule.name].")
|
||||
rule.mode = src
|
||||
rule.acceptable(roundstart_pop_ready, threat_level) // Assigns some vars in the modes, running it here for consistency
|
||||
rule.candidates = candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready(TRUE))
|
||||
@@ -407,7 +408,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
message_admins("Not enough threat level for roundstart antags!")
|
||||
log_game("DYNAMIC: Not enough threat level for roundstart antags!")
|
||||
var/indice_pop = min(10,round(roundstart_pop_ready/pop_per_requirement)+1)
|
||||
var/extra_rulesets_amount = 0
|
||||
extra_rulesets_amount = 0
|
||||
if (GLOB.dynamic_classic_secret)
|
||||
extra_rulesets_amount = 0
|
||||
else
|
||||
@@ -419,23 +420,28 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (threat_level > high_pop_third_rule_req)
|
||||
extra_rulesets_amount++
|
||||
else
|
||||
if (threat_level >= second_rule_req[indice_pop])
|
||||
var/threat_indice = min(10, max(round(threat_level ? threat_level/10 : 1), 1)) // 0-9 threat = 1, 10-19 threat = 2 ...
|
||||
if (threat_level >= second_rule_req[indice_pop] && prob(second_rule_prob[threat_indice]))
|
||||
extra_rulesets_amount++
|
||||
if (threat_level >= third_rule_req[indice_pop])
|
||||
if (threat_level >= third_rule_req[indice_pop] && prob(third_rule_prob[threat_indice]))
|
||||
extra_rulesets_amount++
|
||||
log_game("DYNAMIC: Trying to roll [extra_rulesets_amount + 1] roundstart rulesets. Picking from [drafted_rules.len] eligible rulesets.")
|
||||
|
||||
if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules))
|
||||
if (extra_rulesets_amount > 0) // We've got enough population and threat for a second rulestart rule
|
||||
log_game("DYNAMIC: First ruleset picked successfully. [extra_rulesets_amount] remaining.")
|
||||
while(extra_rulesets_amount > 0 && drafted_rules.len > 0) // We had enough threat for one or two more rulesets
|
||||
for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules)
|
||||
if (rule.cost > threat)
|
||||
drafted_rules -= rule
|
||||
if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules))
|
||||
if (extra_rulesets_amount > 1) // We've got enough population and threat for a third rulestart rule
|
||||
for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules)
|
||||
if (rule.cost > threat)
|
||||
drafted_rules -= rule
|
||||
picking_roundstart_rule(drafted_rules)
|
||||
if(drafted_rules.len)
|
||||
picking_roundstart_rule(drafted_rules)
|
||||
extra_rulesets_amount--
|
||||
log_game("DYNAMIC: Additional ruleset picked successfully, now [executed_rules.len] picked. [extra_rulesets_amount] remaining.")
|
||||
else
|
||||
|
||||
if(threat >= 10)
|
||||
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this.")
|
||||
log_game("DYNAMIC: Picking first roundstart ruleset failed. drafted_rules.len = [drafted_rules.len] and threat = [threat]/[threat_level]")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -443,68 +449,74 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
/datum/game_mode/dynamic/proc/picking_roundstart_rule(list/drafted_rules = list(), forced = FALSE)
|
||||
var/datum/dynamic_ruleset/roundstart/starting_rule = pickweight(drafted_rules)
|
||||
if(!starting_rule)
|
||||
log_game("DYNAMIC: Couldn't pick a starting ruleset. No rulesets available")
|
||||
return FALSE
|
||||
|
||||
if(!forced)
|
||||
if(only_ruleset_executed)
|
||||
return FALSE
|
||||
// Check if a blocking ruleset has been executed.
|
||||
else if(check_blocking(starting_rule.blocking_rules, executed_rules))
|
||||
else if(check_blocking(starting_rule.blocking_rules, executed_rules)) // Should already be filtered out, but making sure. Check filtering at end of proc if reported.
|
||||
drafted_rules -= starting_rule
|
||||
if(drafted_rules.len <= 0)
|
||||
log_game("DYNAMIC: Picking [starting_rule.name] failed due to blocking_rules and no more rulesets available. Report this.")
|
||||
return FALSE
|
||||
starting_rule = pickweight(drafted_rules)
|
||||
// Check if the ruleset is highlander and if a highlander ruleset has been executed
|
||||
else if(starting_rule.flags & HIGHLANDER_RULESET)
|
||||
if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
else if(starting_rule.flags & HIGHLANDER_RULESET) // Should already be filtered out, but making sure. Check filtering at end of proc if reported.
|
||||
if(threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(highlander_executed)
|
||||
drafted_rules -= starting_rule
|
||||
if(drafted_rules.len <= 0)
|
||||
log_game("DYNAMIC: Picking [starting_rule.name] failed due to no highlander stacking and no more rulesets available. Report this.")
|
||||
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()) // Should already be filtered out, but making sure. Check filtering at end of proc if reported.
|
||||
drafted_rules -= starting_rule
|
||||
if(drafted_rules.len <= 0)
|
||||
log_game("DYNAMIC: Picking [starting_rule.name] failed because there were not enough candidates and no more rulesets available. Report this.")
|
||||
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: Picked 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()
|
||||
var/added_threat = starting_rule.scale_up(extra_rulesets_amount, threat)
|
||||
if (starting_rule.pre_execute())
|
||||
spend_threat(starting_rule.cost)
|
||||
spend_threat(starting_rule.cost + added_threat)
|
||||
log_threat("Roundstart [starting_rule.name] spent [starting_rule.cost]")
|
||||
if(starting_rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(starting_rule.flags & ONLY_RULESET)
|
||||
only_ruleset_executed = TRUE
|
||||
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(check_blocking(rule.blocking_rules, executed_rules))
|
||||
drafted_rules -= rule
|
||||
if(highlander_executed && rule.flags & HIGHLANDER_RULESET)
|
||||
drafted_rules -= rule
|
||||
if(!rule.ready())
|
||||
drafted_rules -= rule // And removing rules that are no longer eligible
|
||||
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()
|
||||
/// Mainly here to facilitate delayed rulesets. All roundstart rulesets are executed with a timered callback to this proc.
|
||||
/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
|
||||
var/datum/dynamic_ruleset/rule = sent_rule
|
||||
if(rule.execute())
|
||||
executed_rules += rule
|
||||
if (rule.persistent)
|
||||
if(rule.persistent)
|
||||
current_rules += rule
|
||||
return TRUE
|
||||
else
|
||||
stack_trace("The delayed roundstart rule \"[rule.name]\" failed to execute.")
|
||||
return FALSE
|
||||
rule.clean_up() // Refund threat, delete teams and so on.
|
||||
executed_rules -= rule
|
||||
stack_trace("The starting 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.
|
||||
@@ -512,7 +524,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
var/datum/dynamic_ruleset/rule = pickweight(drafted_rules)
|
||||
if(!rule)
|
||||
return FALSE
|
||||
|
||||
|
||||
if(!forced)
|
||||
if(only_ruleset_executed)
|
||||
return FALSE
|
||||
@@ -524,19 +536,19 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
rule = pickweight(drafted_rules)
|
||||
// Check if the ruleset is highlander and if a highlander ruleset has been executed
|
||||
else if(rule.flags & HIGHLANDER_RULESET)
|
||||
if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(highlander_executed)
|
||||
drafted_rules -= rule
|
||||
if(drafted_rules.len <= 0)
|
||||
return FALSE
|
||||
rule = pickweight(drafted_rules)
|
||||
|
||||
|
||||
if(!rule.repeatable)
|
||||
if(rule.ruletype == "Latejoin")
|
||||
latejoin_rules = remove_from_list(latejoin_rules, rule.type)
|
||||
else if(rule.type == "Midround")
|
||||
midround_rules = remove_from_list(midround_rules, rule.type)
|
||||
|
||||
/*
|
||||
if (rule.execute())
|
||||
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
|
||||
spend_threat(rule.cost)
|
||||
@@ -557,6 +569,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
else
|
||||
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
|
||||
return FALSE
|
||||
*/
|
||||
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_midround_latejoin_rule, rule), rule.delay)
|
||||
return TRUE
|
||||
|
||||
/// An experimental proc to allow admins to call rules on the fly or have rules call other rules.
|
||||
/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE)
|
||||
@@ -567,10 +583,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
new_rule = ruletype
|
||||
else
|
||||
return FALSE
|
||||
|
||||
|
||||
if(!new_rule)
|
||||
return FALSE
|
||||
|
||||
|
||||
if(!forced)
|
||||
if(only_ruleset_executed)
|
||||
return FALSE
|
||||
@@ -579,13 +595,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
return FALSE
|
||||
// Check if the ruleset is highlander and if a highlander ruleset has been executed
|
||||
else if(new_rule.flags & HIGHLANDER_RULESET)
|
||||
if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(highlander_executed)
|
||||
return FALSE
|
||||
|
||||
|
||||
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)
|
||||
@@ -604,6 +619,30 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of elligible players.")
|
||||
return FALSE
|
||||
|
||||
/// Mainly here to facilitate delayed rulesets. All midround/latejoin rulesets are executed with a timered callback to this proc.
|
||||
/datum/game_mode/dynamic/proc/execute_midround_latejoin_rule(sent_rule)
|
||||
var/datum/dynamic_ruleset/rule = sent_rule
|
||||
if (rule.execute())
|
||||
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
|
||||
spend_threat(rule.cost)
|
||||
threat_log += "[worldtime2text()]: [rule.ruletype] [rule.name] spent [rule.cost]"
|
||||
if(rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(rule.flags & ONLY_RULESET)
|
||||
only_ruleset_executed = TRUE
|
||||
if(rule.ruletype == "Latejoin")
|
||||
var/mob/M = pick(rule.candidates)
|
||||
message_admins("[key_name(M)] joined the station, and was selected by the [rule.name] ruleset.")
|
||||
log_game("DYNAMIC: [key_name(M)] joined the station, and was selected by the [rule.name] ruleset.")
|
||||
executed_rules += rule
|
||||
rule.candidates.Cut()
|
||||
if (rule.persistent)
|
||||
current_rules += rule
|
||||
return TRUE
|
||||
rule.clean_up()
|
||||
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
|
||||
return FALSE
|
||||
|
||||
/datum/game_mode/dynamic/process()
|
||||
if (pop_last_updated < world.time - (60 SECONDS))
|
||||
pop_last_updated = world.time
|
||||
@@ -616,21 +655,21 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (midround_injection_cooldown < world.time)
|
||||
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)
|
||||
|
||||
|
||||
// Time to inject some threat into the round
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone
|
||||
return
|
||||
|
||||
log_game("DYNAMIC: Checking state of the round.")
|
||||
|
||||
update_playercounts()
|
||||
message_admins("DYNAMIC: Checking for midround injection.")
|
||||
log_game("DYNAMIC: Checking for midround injection.")
|
||||
|
||||
if (prob(get_injection_chance()))
|
||||
update_playercounts()
|
||||
if (get_injection_chance())
|
||||
var/list/drafted_rules = list()
|
||||
var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len
|
||||
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
|
||||
@@ -640,10 +679,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
// 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)
|
||||
@@ -744,11 +781,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
// No stacking : only one round-enter, unless > stacking_limit threat.
|
||||
if (threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
// No stacking : only one round-ender, unless threat level > stacking_limit.
|
||||
if (threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(rule.flags & HIGHLANDER_RULESET && highlander_executed)
|
||||
continue
|
||||
|
||||
|
||||
rule.candidates = list(newPlayer)
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
#define EXTRA_RULESET_PENALTY 20 // Changes how likely a gamemode is to scale based on how many other roundstart rulesets are waiting to be rolled.
|
||||
#define POP_SCALING_PENALTY 50 // Discourages scaling up rulesets if ratio of antags to crew is high.
|
||||
|
||||
#define REVOLUTION_VICTORY 1
|
||||
#define STATION_VICTORY 2
|
||||
|
||||
/datum/dynamic_ruleset
|
||||
/// For admin logging and round end screen.
|
||||
var/name = ""
|
||||
/// For admin logging and round end screen, do not change this unless making a new rule type.
|
||||
var/ruletype = ""
|
||||
var/ruletype = ""
|
||||
/// For config purposes, similar to config_tag for secret game modes.
|
||||
var/config_tag = null
|
||||
/// If set to TRUE, the rule won't be discarded after being executed, and dynamic will call rule_process() every time it ticks.
|
||||
var/persistent = FALSE
|
||||
var/persistent = FALSE
|
||||
/// If set to TRUE, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules)
|
||||
var/repeatable = FALSE
|
||||
var/repeatable = FALSE
|
||||
/// If set higher than 0 decreases weight by itself causing the ruleset to appear less often the more it is repeated.
|
||||
var/repeatable_weight_decrease = 2
|
||||
var/repeatable_weight_decrease = 2
|
||||
/// List of players that are being drafted for this rule
|
||||
var/list/mob/candidates = list()
|
||||
var/list/mob/candidates = list()
|
||||
/// List of players that were selected for this rule
|
||||
var/list/datum/mind/assigned = list()
|
||||
var/list/datum/mind/assigned = list()
|
||||
/// Preferences flag such as ROLE_WIZARD that need to be turned on for players to be antag
|
||||
var/antag_flag = null
|
||||
var/antag_flag = null
|
||||
/// The antagonist datum that is assigned to the mobs mind on ruleset execution.
|
||||
var/datum/antagonist/antag_datum = null
|
||||
/// The required minimum account age for this ruleset.
|
||||
@@ -24,19 +30,25 @@
|
||||
/// If set, and config flag protect_roles_from_antagonist is false, then the rule will not pick players from these roles.
|
||||
var/list/protected_roles = list()
|
||||
/// If set, rule will deny candidates from those roles always.
|
||||
var/list/restricted_roles = list()
|
||||
var/list/restricted_roles = list()
|
||||
/// If set, rule will only accept candidates from those roles, IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS.
|
||||
var/list/exclusive_roles = list()
|
||||
var/list/exclusive_roles = list()
|
||||
/// If set, there needs to be a certain amount of players doing those roles (among the players who won't be drafted) for the rule to be drafted IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS.
|
||||
var/list/enemy_roles = list()
|
||||
var/list/enemy_roles = list()
|
||||
/// If enemy_roles was set, this is the amount of enemy job workers needed per threat_level range (0-10,10-20,etc) IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS.
|
||||
var/required_enemies = list(1,1,0,0,0,0,0,0,0,0)
|
||||
var/required_enemies = list(1,1,0,0,0,0,0,0,0,0)
|
||||
/// The rule needs this many candidates (post-trimming) to be executed (example: Cult needs 4 players at round start)
|
||||
var/required_candidates = 0
|
||||
var/required_candidates = 0
|
||||
/// 1 -> 9, probability for this rule to be picked against other rules
|
||||
var/weight = 5
|
||||
var/weight = 5
|
||||
/// Threat cost for this rule, this is decreased from the mode's threat when the rule is executed.
|
||||
var/cost = 0
|
||||
var/cost = 0
|
||||
/// Cost per level the rule scales up.
|
||||
var/scaling_cost = 0
|
||||
/// How many times a rule has scaled up upon getting picked.
|
||||
var/scaled_times = 0
|
||||
/// Used for the roundend report
|
||||
var/total_cost = 0
|
||||
/// A flag that determines how the ruleset is handled
|
||||
/// HIGHLANDER_RULESET are rulesets can end the round.
|
||||
/// TRAITOR_RULESET and MINOR_RULESET can't end the round and have no difference right now.
|
||||
@@ -46,20 +58,28 @@
|
||||
/// Requirements are the threat level requirements per pop range.
|
||||
/// With the default values, The rule will never get drafted below 10 threat level (aka: "peaceful extended"), and it requires a higher threat level at lower pops.
|
||||
var/list/requirements = list(40,30,20,10,10,10,10,10,10,10)
|
||||
/// An alternative, static requirement used instead when pop is over mode's high_pop_limit.
|
||||
/// An alternative, static requirement used instead when pop is over mode's high_pop_limit.
|
||||
var/high_population_requirement = 10
|
||||
/// Reference to the mode, use this instead of SSticker.mode.
|
||||
var/datum/game_mode/dynamic/mode = null
|
||||
/// If a role is to be considered another for the purpose of banning.
|
||||
var/antag_flag_override = null
|
||||
var/antag_flag_override = null
|
||||
/// If a ruleset type which is in this list has been executed, then the ruleset will not be executed.
|
||||
var/list/blocking_rules = list()
|
||||
/// The minimum amount of players required for the rule to be considered.
|
||||
/// The minimum amount of players required for the rule to be considered.
|
||||
var/minimum_players = 0
|
||||
/// The maximum amount of players required for the rule to be considered.
|
||||
/// Anything below zero or exactly zero is ignored.
|
||||
/// Anything below zero or exactly zero is ignored.
|
||||
var/maximum_players = 0
|
||||
|
||||
/// Calculated during acceptable(), used in scaling and team sizes.
|
||||
var/indice_pop = 0
|
||||
/// Population scaling. Used by team antags and scaling for solo antags.
|
||||
var/list/antag_cap = list()
|
||||
/// Base probability used in scaling. The higher it is, the more likely to scale. Kept as a var to allow for config editing._SendSignal(sigtype, list/arguments)
|
||||
var/base_prob = 60
|
||||
/// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin).
|
||||
/// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure.
|
||||
var/delay = 0
|
||||
|
||||
/datum/dynamic_ruleset/New()
|
||||
..()
|
||||
@@ -87,10 +107,6 @@
|
||||
/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.
|
||||
|
||||
// Can be drafted when a player joins the server
|
||||
/datum/dynamic_ruleset/latejoin
|
||||
ruletype = "Latejoin"
|
||||
@@ -103,17 +119,40 @@
|
||||
if(maximum_players > 0 && population > maximum_players)
|
||||
return FALSE
|
||||
if (population >= GLOB.dynamic_high_pop_limit)
|
||||
indice_pop = 10
|
||||
return (threat_level >= high_population_requirement)
|
||||
else
|
||||
pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : mode.pop_per_requirement
|
||||
var/indice_pop = min(10,round(population/pop_per_requirement)+1)
|
||||
if(antag_cap.len && requirements.len != antag_cap.len)
|
||||
message_admins("DYNAMIC: requirements and antag_cap lists have different lengths in ruleset [name]. Likely config issue, report this.")
|
||||
log_game("DYNAMIC: requirements and antag_cap lists have different lengths in ruleset [name]. Likely config issue, report this.")
|
||||
indice_pop = min(requirements.len,round(population/pop_per_requirement)+1)
|
||||
return (threat_level >= requirements[indice_pop])
|
||||
|
||||
/// Called when a suitable rule is picked during roundstart(). Will some times attempt to scale a rule up when there is threat remaining. Returns the amount of scaled steps.
|
||||
/datum/dynamic_ruleset/proc/scale_up(extra_rulesets = 0, remaining_threat_level = 0)
|
||||
remaining_threat_level -= cost
|
||||
if(scaling_cost && scaling_cost <= remaining_threat_level) // Only attempts to scale the modes with a scaling cost explicitly set.
|
||||
var/new_prob
|
||||
var/pop_to_antags = (mode.antags_rolled + (antag_cap[indice_pop] * (scaled_times + 1))) / mode.roundstart_pop_ready
|
||||
log_game("DYNAMIC: [name] roundstart ruleset attempting to scale up with [extra_rulesets] rulesets waiting and [remaining_threat_level] threat remaining.")
|
||||
for(var/i in 1 to 3) //Can scale a max of 3 times
|
||||
if(remaining_threat_level >= scaling_cost && pop_to_antags < 0.25)
|
||||
new_prob = base_prob + (remaining_threat_level) - (scaled_times * scaling_cost) - (extra_rulesets * EXTRA_RULESET_PENALTY) - (pop_to_antags * POP_SCALING_PENALTY)
|
||||
if (!prob(new_prob))
|
||||
break
|
||||
remaining_threat_level -= scaling_cost
|
||||
scaled_times++
|
||||
pop_to_antags = (mode.antags_rolled + (antag_cap[indice_pop] * (scaled_times + 1))) / mode.roundstart_pop_ready
|
||||
log_game("DYNAMIC: [name] roundstart ruleset failed scaling up at [new_prob ? new_prob : 0]% chance after [scaled_times]/3 successful scaleups. [remaining_threat_level] threat remaining, antag to crew ratio: [pop_to_antags*100]%.")
|
||||
mode.antags_rolled += (1 + scaled_times) * antag_cap[indice_pop]
|
||||
return scaled_times * scaling_cost
|
||||
|
||||
/// This is called if persistent variable is true everytime SSTicker ticks.
|
||||
/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,20 +165,20 @@
|
||||
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!
|
||||
/datum/dynamic_ruleset/proc/ready(forced = 0)
|
||||
if (required_candidates > candidates.len)
|
||||
/datum/dynamic_ruleset/proc/ready(forced = 0)
|
||||
if (required_candidates > candidates.len)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
|
||||
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
|
||||
/datum/dynamic_ruleset/proc/clean_up()
|
||||
mode.refund_threat(cost + (scaled_times * scaling_cost))
|
||||
mode.threat_log += "[worldtime2text()]: [ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]"
|
||||
|
||||
/// Gets weight of the ruleset
|
||||
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
|
||||
/// Note: If you don't want repeatable rulesets to decrease their weight use the weight variable directly
|
||||
@@ -156,14 +195,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 +222,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
|
||||
@@ -73,6 +78,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur
|
||||
name = "Provocateur"
|
||||
persistent = TRUE
|
||||
config_tag = "latejoin_revolution"
|
||||
antag_datum = /datum/antagonist/rev/head
|
||||
antag_flag = ROLE_REV_HEAD
|
||||
@@ -82,35 +88,102 @@
|
||||
required_enemies = list(4,4,3,3,3,3,3,2,2,1)
|
||||
required_candidates = 1
|
||||
weight = 2
|
||||
delay = 1 MINUTES // Prevents rule start while head is offstation.
|
||||
cost = 20
|
||||
requirements = list(101,101,70,40,30,20,20,20,20,20)
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
var/required_heads = 3
|
||||
var/required_heads_of_staff = 3
|
||||
var/finished = FALSE
|
||||
var/datum/team/revolution/revolution
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/ready(forced=FALSE)
|
||||
if (forced)
|
||||
required_heads = 1
|
||||
required_heads_of_staff = 1
|
||||
if(!..())
|
||||
return FALSE
|
||||
var/head_check = 0
|
||||
for(var/mob/player in mode.current_players[CURRENT_LIVING_PLAYERS])
|
||||
if (player.mind.assigned_role in GLOB.command_positions)
|
||||
head_check++
|
||||
return (head_check >= required_heads)
|
||||
return (head_check >= required_heads_of_staff)
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/execute()
|
||||
var/mob/M = pick(candidates)
|
||||
assigned += M.mind
|
||||
M.mind.special_role = antag_flag
|
||||
var/datum/antagonist/rev/head/new_head = new()
|
||||
new_head.give_flash = TRUE
|
||||
new_head.give_hud = TRUE
|
||||
new_head.remove_clumsy = TRUE
|
||||
new_head = M.mind.add_antag_datum(new_head)
|
||||
new_head.rev_team.max_headrevs = 1 // Only one revhead if it is latejoin.
|
||||
var/mob/M = pick(candidates) // This should contain a single player, but in case.
|
||||
if(check_eligible(M.mind)) // Didnt die/run off z-level/get implanted since leaving shuttle.
|
||||
assigned += M.mind
|
||||
M.mind.special_role = antag_flag
|
||||
revolution = new()
|
||||
var/datum/antagonist/rev/head/new_head = new()
|
||||
new_head.give_flash = TRUE
|
||||
new_head.give_hud = TRUE
|
||||
new_head.remove_clumsy = TRUE
|
||||
new_head = M.mind.add_antag_datum(new_head, revolution)
|
||||
revolution.update_objectives()
|
||||
revolution.update_heads()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
return TRUE
|
||||
else
|
||||
log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
|
||||
log_game("DYNAMIC: [ruletype] [name] failed to get any eligible headrevs. Refunding [cost] threat.")
|
||||
return FALSE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/rule_process()
|
||||
if(check_rev_victory())
|
||||
finished = REVOLUTION_VICTORY
|
||||
return RULESET_STOP_PROCESSING
|
||||
else if (check_heads_victory())
|
||||
finished = STATION_VICTORY
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
priority_announce("It appears the mutiny has been quelled. Please return yourself and your colleagues to work. \
|
||||
We have remotely blacklisted the head revolutionaries from your cloning software to prevent accidental cloning.", null, "attention", null, "Central Command Loyalty Monitoring Division")
|
||||
for(var/datum/mind/M in revolution.members) // Remove antag datums and prevent headrev cloning then restarting rebellions.
|
||||
if(M.has_antag_datum(/datum/antagonist/rev/head))
|
||||
var/datum/antagonist/rev/head/R = M.has_antag_datum(/datum/antagonist/rev/head)
|
||||
R.remove_revolutionary(FALSE, "gamemode")
|
||||
var/mob/living/carbon/C = M.current
|
||||
if(C.stat == DEAD)
|
||||
C.makeUncloneable()
|
||||
if(M.has_antag_datum(/datum/antagonist/rev))
|
||||
var/datum/antagonist/rev/R = M.has_antag_datum(/datum/antagonist/rev)
|
||||
R.remove_revolutionary(FALSE, "gamemode")
|
||||
return RULESET_STOP_PROCESSING
|
||||
|
||||
/// Checks for revhead loss conditions and other antag datums.
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/proc/check_eligible(var/datum/mind/M)
|
||||
var/turf/T = get_turf(M.current)
|
||||
if(!considered_afk(M) && considered_alive(M) && is_station_level(T.z) && !M.antag_datums?.len && !HAS_TRAIT(M, TRAIT_MINDSHIELD))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/check_finished()
|
||||
if(finished == REVOLUTION_VICTORY)
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/proc/check_rev_victory()
|
||||
for(var/datum/objective/mutiny/objective in revolution.objectives)
|
||||
if(!(objective.check_completion()))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/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))
|
||||
if(ishuman(rev_mind.current) || ismonkey(rev_mind.current))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/provocateur/round_result()
|
||||
if(finished == REVOLUTION_VICTORY)
|
||||
SSticker.mode_result = "win - heads killed"
|
||||
SSticker.news_report = REVS_WIN
|
||||
else if(finished == STATION_VICTORY)
|
||||
SSticker.mode_result = "loss - rev heads killed"
|
||||
SSticker.news_report = REVS_LOSE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// VAMPIRE //
|
||||
|
||||
@@ -11,7 +11,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()
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
/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
|
||||
|
||||
@@ -29,22 +30,13 @@
|
||||
var/triggering
|
||||
|
||||
/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)
|
||||
@@ -55,14 +47,19 @@
|
||||
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)))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
else
|
||||
if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE)))
|
||||
trimmed_list.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)
|
||||
continue
|
||||
if (M.mind.assigned_role in restricted_roles || HAS_TRAIT(M, TRAIT_MINDSHIELD)) // Does their job allow it or are they mindshielded?
|
||||
if (M.mind.assigned_role in restricted_roles) // Does their job allow it?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if ((exclusive_roles.len > 0) && !(M.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
|
||||
@@ -267,7 +264,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/malf/trim_candidates()
|
||||
..()
|
||||
candidates = candidates[CURRENT_LIVING_PLAYERS]
|
||||
living_players = candidates[CURRENT_LIVING_PLAYERS]
|
||||
for(var/mob/living/player in candidates)
|
||||
if(!isAI(player))
|
||||
candidates -= player
|
||||
@@ -281,8 +278,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
|
||||
@@ -353,7 +349,7 @@
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat=0)
|
||||
if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules)
|
||||
return FALSE // Unavailable if nuke ops were already sent at roundstart
|
||||
var/indice_pop = min(10,round(living_players.len/5)+1)
|
||||
indice_pop = min(operative_cap.len, round(living_players.len/5)+1)
|
||||
required_candidates = operative_cap[indice_pop]
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
restricted_roles = list("Cyborg", "AI")
|
||||
required_candidates = 1
|
||||
weight = 5
|
||||
cost = 10
|
||||
cost = 10 // Avoid raising traitor threat above 10, as it is the default low cost ruleset.
|
||||
scaling_cost = 10
|
||||
requirements = list(60,60,50,50,50,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
|
||||
var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/traitor/pre_execute()
|
||||
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)
|
||||
var/num_traitors = antag_cap[indice_pop] * (scaled_times + 1)
|
||||
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
|
||||
@@ -114,24 +114,19 @@
|
||||
cost = 10
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
antag_cap = list(2,2,2,2,2,2,2,2,2,2) // Can pick 3 per team, but rare enough it doesn't matter.
|
||||
var/list/datum/team/brother_team/pre_brother_teams = list()
|
||||
var/const/team_amount = 2 // Hard limit on brother teams if scaling is turned off
|
||||
var/const/min_team_size = 2
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/traitorbro/pre_execute()
|
||||
var/num_teams = team_amount
|
||||
var/bsc = CONFIG_GET(number/brother_scaling_coeff)
|
||||
if(bsc)
|
||||
num_teams = max(1, round(num_players() / bsc))
|
||||
|
||||
var/num_teams = (antag_cap[indice_pop]/min_team_size) * (scaled_times + 1) // 1 team per scaling
|
||||
for(var/j = 1 to num_teams)
|
||||
if(candidates.len < min_team_size || candidates.len < required_candidates)
|
||||
break
|
||||
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"
|
||||
@@ -164,16 +159,17 @@
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_candidates = 1
|
||||
weight = 3
|
||||
cost = 30
|
||||
cost = 15
|
||||
scaling_cost = 15
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 10
|
||||
antag_cap = list(1,1,1,1,1,2,2,2,2,3)
|
||||
var/team_mode_probability = 30
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/changeling/pre_execute()
|
||||
var/num_changelings = min(round(mode.candidates.len / 10) + 1, candidates.len)
|
||||
var/num_changelings = antag_cap[indice_pop] * (scaled_times + 1)
|
||||
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
|
||||
@@ -181,7 +177,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/changeling/execute()
|
||||
var/team_mode = FALSE
|
||||
if(prob(team_mode_probability))
|
||||
if(prob(team_mode_probability))
|
||||
team_mode = TRUE
|
||||
var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective)
|
||||
var/list/possible_team_objectives = list()
|
||||
@@ -229,14 +225,13 @@
|
||||
/datum/dynamic_ruleset/roundstart/wizard/pre_execute()
|
||||
if(GLOB.wizardstart.len == 0)
|
||||
return FALSE
|
||||
|
||||
var/mob/M = pick(candidates)
|
||||
mode.antags_rolled += 1
|
||||
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
|
||||
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/wizard/execute()
|
||||
@@ -244,7 +239,7 @@
|
||||
M.current.forceMove(pick(GLOB.wizardstart))
|
||||
M.add_antag_datum(new antag_datum())
|
||||
return TRUE
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// BLOOD CULT //
|
||||
@@ -265,22 +260,20 @@
|
||||
requirements = list(101,101,101,100,90,80,70,60,50,50)
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
var/cultist_cap = list(2,2,2,3,3,4,4,4,4,4)
|
||||
antag_cap = list(2,2,2,3,3,4,4,4,4,4)
|
||||
var/datum/team/cult/main_cult
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/bloodcult/ready(forced = FALSE)
|
||||
var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
|
||||
required_candidates = cultist_cap[indice_pop]
|
||||
required_candidates = antag_cap[indice_pop]
|
||||
. = ..()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/bloodcult/pre_execute()
|
||||
var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
|
||||
var/cultists = cultist_cap[indice_pop]
|
||||
var/cultists = antag_cap[indice_pop]
|
||||
mode.antags_rolled += cultists
|
||||
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
|
||||
@@ -292,7 +285,7 @@
|
||||
var/datum/antagonist/cult/new_cultist = new antag_datum()
|
||||
new_cultist.cult_team = main_cult
|
||||
new_cultist.give_equipment = TRUE
|
||||
M.add_antag_datum(new_cultist)
|
||||
M.add_antag_datum(new_cultist)
|
||||
main_cult.setup_objectives()
|
||||
return TRUE
|
||||
|
||||
@@ -325,24 +318,21 @@
|
||||
requirements = list(100,90,80,70,60,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
var/operative_cap = list(2,2,2,3,3,3,4,4,5,5)
|
||||
antag_cap = list(2,2,2,3,3,3,4,4,5,5)
|
||||
var/datum/team/nuclear/nuke_team
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE)
|
||||
var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1)
|
||||
required_candidates = operative_cap[indice_pop]
|
||||
required_candidates = antag_cap[indice_pop]
|
||||
. = ..()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/nuclear/pre_execute()
|
||||
// If ready() did its job, candidates should have 5 or more members in it
|
||||
|
||||
var/indice_pop = min(10,round(mode.roundstart_pop_ready/5)+1)
|
||||
var/operatives = operative_cap[indice_pop]
|
||||
var/operatives = antag_cap[indice_pop]
|
||||
mode.antags_rolled += operatives
|
||||
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"
|
||||
@@ -400,7 +390,7 @@
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/delayed/revs
|
||||
/datum/dynamic_ruleset/roundstart/revs
|
||||
name = "Revolution"
|
||||
config_tag = "revolution"
|
||||
persistent = TRUE
|
||||
@@ -411,63 +401,98 @@
|
||||
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,101,80,70,60,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
delay = 5 MINUTES
|
||||
antag_cap = list(3,3,3,3,3,3,3,3,3,3)
|
||||
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
|
||||
var/finished = FALSE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/delayed/revs/execute()
|
||||
var/max_canditates = 4
|
||||
revolution = new()
|
||||
for(var/i = 1 to max_canditates)
|
||||
/datum/dynamic_ruleset/roundstart/revs/pre_execute()
|
||||
var/max_candidates = antag_cap[indice_pop]
|
||||
mode.antags_rolled += max_candidates
|
||||
for(var/i = 1 to max_candidates)
|
||||
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
|
||||
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)
|
||||
|
||||
revolution.update_objectives()
|
||||
revolution.update_heads()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/delayed/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()
|
||||
if(CONFIG_GET(keyed_list/continuous)["revolution"])
|
||||
if(finished)
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
return ..()
|
||||
if(finished != 0)
|
||||
/datum/dynamic_ruleset/roundstart/revs/execute()
|
||||
var/success = TRUE
|
||||
revolution = new()
|
||||
for(var/datum/mind/M in assigned)
|
||||
if(check_eligible(M))
|
||||
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.add_antag_datum(new_head,revolution)
|
||||
else
|
||||
assigned -= M
|
||||
log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
|
||||
if(!revolution.members.len)
|
||||
success = FALSE
|
||||
log_game("DYNAMIC: [ruletype] [name] failed to get any eligible headrevs. Refunding [cost] threat.")
|
||||
if(success)
|
||||
revolution.update_objectives()
|
||||
revolution.update_heads()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/revs/clean_up()
|
||||
qdel(revolution)
|
||||
..()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/revs/rule_process()
|
||||
if(check_rev_victory())
|
||||
finished = REVOLUTION_VICTORY
|
||||
return RULESET_STOP_PROCESSING
|
||||
else if (check_heads_victory())
|
||||
finished = STATION_VICTORY
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
priority_announce("It appears the mutiny has been quelled. Please return yourself and your incapacitated colleagues to work. \
|
||||
We have remotely blacklisted the head revolutionaries from your cloning software to prevent accidental cloning.", null, "attention", null, "Central Command Loyalty Monitoring Division")
|
||||
|
||||
for(var/datum/mind/M in revolution.members) // Remove antag datums and prevents podcloned or exiled headrevs restarting rebellions.
|
||||
if(M.has_antag_datum(/datum/antagonist/rev/head))
|
||||
var/datum/antagonist/rev/head/R = M.has_antag_datum(/datum/antagonist/rev/head)
|
||||
R.remove_revolutionary(FALSE, "gamemode")
|
||||
var/mob/living/carbon/C = M.current
|
||||
if(C.stat == DEAD)
|
||||
C.makeUncloneable()
|
||||
if(M.has_antag_datum(/datum/antagonist/rev))
|
||||
var/datum/antagonist/rev/R = M.has_antag_datum(/datum/antagonist/rev)
|
||||
R.remove_revolutionary(FALSE, "gamemode")
|
||||
return RULESET_STOP_PROCESSING
|
||||
|
||||
/// Checks for revhead loss conditions and other antag datums.
|
||||
/datum/dynamic_ruleset/roundstart/revs/proc/check_eligible(var/datum/mind/M)
|
||||
var/turf/T = get_turf(M.current)
|
||||
if(!considered_afk(M) && considered_alive(M) && is_station_level(T.z) && !M.antag_datums?.len && !HAS_TRAIT(M, TRAIT_MINDSHIELD))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/revs/check_finished()
|
||||
if(finished == REVOLUTION_VICTORY)
|
||||
return TRUE
|
||||
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))
|
||||
@@ -475,11 +500,11 @@
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/delayed/revs/round_result()
|
||||
if(finished == 1)
|
||||
/datum/dynamic_ruleset/roundstart/revs/round_result()
|
||||
if(finished == REVOLUTION_VICTORY)
|
||||
SSticker.mode_result = "win - heads killed"
|
||||
SSticker.news_report = REVS_WIN
|
||||
else if(finished == 2)
|
||||
else if(finished == STATION_VICTORY)
|
||||
SSticker.mode_result = "loss - rev heads killed"
|
||||
SSticker.news_report = REVS_LOSE
|
||||
|
||||
@@ -527,6 +552,7 @@
|
||||
requirements = list(101,101,101,100,90,80,70,60,50,50)
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(2,3,3,4,4,4,4,4,4,4)
|
||||
var/ark_time
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/clockcult/pre_execute()
|
||||
@@ -539,15 +565,14 @@
|
||||
for(var/datum/parsed_map/PM in reebes)
|
||||
PM.initTemplateBounds()
|
||||
|
||||
var/starter_servants = 4
|
||||
var/number_players = num_players()
|
||||
var/starter_servants = antag_cap[indice_pop]
|
||||
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)
|
||||
starter_servants += min(round(number_players / 10), 5)
|
||||
mode.antags_rolled += starter_servants
|
||||
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
|
||||
@@ -656,23 +681,16 @@
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
var/devil_limit = 4 // Hard limit on devils if scaling is turned off
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/devil/pre_execute()
|
||||
var/tsc = CONFIG_GET(number/traitor_scaling_coeff)
|
||||
var/num_devils = 1
|
||||
|
||||
if(tsc)
|
||||
num_devils = max(required_candidates, min(round(num_players() / (tsc * 3)) + 2, round(num_players() / (tsc * 1.5))))
|
||||
else
|
||||
num_devils = max(required_candidates, min(num_players(), devil_limit))
|
||||
antag_cap = list(1,1,1,2,2,2,3,3,3,4)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/devil/pre_execute()
|
||||
var/num_devils = antag_cap[indice_pop]
|
||||
mode.antags_rolled += num_devils
|
||||
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
|
||||
|
||||
@@ -721,13 +739,13 @@
|
||||
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)
|
||||
mode.antags_rolled += carriers_to_make
|
||||
|
||||
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
|
||||
|
||||
@@ -207,9 +207,20 @@
|
||||
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing of your time spent reprogrammed, you can't even remember the names or identities of anyone involved...</span>")
|
||||
|
||||
/datum/antagonist/rev/head/farewell()
|
||||
if((ishuman(owner.current) || ismonkey(owner.current)))
|
||||
if(owner.current.stat != DEAD)
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class ='deconversion_message bold'>You have given up your cause of overthrowing the command staff. You are no longer a Head Revolutionary.</span>")
|
||||
else
|
||||
to_chat(owner, "<span class ='deconversion_message bold'>The sweet release of death. You are no longer a Head Revolutionary.</span>")
|
||||
else if(issilicon(owner.current))
|
||||
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, suppressing the disloyal personality traits from the MMI before initalizing it.</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and suppresses your unwanted personality traits! You feel more content with the leadership around these parts.</span>")
|
||||
|
||||
//blunt trauma deconversions call this through species.dm spec_attacked_by()
|
||||
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
|
||||
log_attack("[key_name(owner.current)] has been deconverted from the revolution by [key_name(deconverter)]!")
|
||||
log_attack("[key_name(owner.current)] has been deconverted from the revolution by [ismob(deconverter) ? key_name(deconverter) : deconverter]!")
|
||||
if(borged)
|
||||
message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]")
|
||||
owner.special_role = null
|
||||
@@ -219,9 +230,8 @@
|
||||
owner.remove_antag_datum(type)
|
||||
|
||||
/datum/antagonist/rev/head/remove_revolutionary(borged,deconverter)
|
||||
if(!borged)
|
||||
return
|
||||
. = ..()
|
||||
if(borged || deconverter == "gamemode")
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/rev/head/equip_rev()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
@@ -253,6 +263,8 @@
|
||||
/datum/team/revolution
|
||||
name = "Revolution"
|
||||
var/max_headrevs = 3
|
||||
var/list/ex_headrevs = list() // Dynamic removes revs on loss, used to keep a list for the roundend report.
|
||||
var/list/ex_revs = list()
|
||||
|
||||
/datum/team/revolution/proc/update_objectives(initial = FALSE)
|
||||
var/untracked_heads = SSjob.get_all_heads()
|
||||
@@ -295,9 +307,12 @@
|
||||
|
||||
addtimer(CALLBACK(src,.proc/update_heads),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
|
||||
|
||||
/datum/team/revolution/proc/save_members()
|
||||
ex_headrevs = get_antag_minds(/datum/antagonist/rev/head, TRUE)
|
||||
ex_revs = get_antag_minds(/datum/antagonist/rev, TRUE)
|
||||
|
||||
/datum/team/revolution/roundend_report()
|
||||
if(!members.len)
|
||||
if(!members.len && !ex_headrevs.len)
|
||||
return
|
||||
|
||||
var/list/result = list()
|
||||
@@ -317,8 +332,17 @@
|
||||
|
||||
|
||||
var/list/targets = list()
|
||||
var/list/datum/mind/headrevs = get_antag_minds(/datum/antagonist/rev/head)
|
||||
var/list/datum/mind/revs = get_antag_minds(/datum/antagonist/rev,TRUE)
|
||||
var/list/datum/mind/headrevs
|
||||
var/list/datum/mind/revs
|
||||
if(ex_headrevs.len)
|
||||
headrevs = ex_headrevs
|
||||
else
|
||||
headrevs = get_antag_minds(/datum/antagonist/rev/head, TRUE)
|
||||
|
||||
if(ex_revs.len)
|
||||
revs = ex_revs
|
||||
else
|
||||
revs = get_antag_minds(/datum/antagonist/rev, TRUE)
|
||||
if(headrevs.len)
|
||||
var/list/headrev_part = list()
|
||||
headrev_part += "<span class='header'>The head revolutionaries were:</span>"
|
||||
|
||||
@@ -69,11 +69,16 @@
|
||||
/mob/living/carbon/human/proc/makeSkeleton()
|
||||
ADD_TRAIT(src, TRAIT_DISFIGURED, TRAIT_GENERIC)
|
||||
set_species(/datum/species/skeleton)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/carbon/proc/Drain()
|
||||
become_husk(CHANGELING_DRAIN)
|
||||
ADD_TRAIT(src, TRAIT_NOCLONE, CHANGELING_DRAIN)
|
||||
blood_volume = 0
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/proc/makeUncloneable()
|
||||
ADD_TRAIT(src, TRAIT_NOCLONE, MADE_UNCLONEABLE)
|
||||
blood_volume = 0
|
||||
return TRUE
|
||||
@@ -112,14 +112,14 @@
|
||||
/mob/proc/get_leg_ignore()
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/alien/larva/get_leg_ignore()
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/get_leg_ignore()
|
||||
if(movement_type & FLYING|FLOATING)
|
||||
/mob/living/carbon/get_leg_ignore()
|
||||
if(movement_type & (FLYING|FLOATING))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/alien/larva/get_leg_ignore()
|
||||
return TRUE
|
||||
|
||||
/mob/living/proc/get_missing_limbs()
|
||||
return list()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user