Files
GS13NG/code/game/gamemodes/dynamic/dynamic_rulesets.dm
2019-10-22 21:39:28 -07:00

225 lines
11 KiB
Plaintext

/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 = ""
/// 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
/// 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
/// 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
/// List of players that are being drafted for this rule
var/list/mob/candidates = list()
/// List of players that were selected for this rule
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
/// 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.
var/minimum_required_age = 7
/// 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()
/// If set, rule will only accept candidates from those roles, IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS.
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()
/// 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)
/// The rule needs this many candidates (post-trimming) to be executed (example: Cult needs 4 players at round start)
var/required_candidates = 0
/// 1 -> 9, probability for this rule to be picked against other rules
var/weight = 5
/// Threat cost for this rule, this is decreased from the mode's threat when the rule is executed.
var/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.
var/flags = 0
/// Pop range per requirement. If zero defaults to mode's pop_per_requirement.
var/pop_per_requirement = 0
/// 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.
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
/// 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.
var/minimum_players = 0
/// The maximum amount of players required for the rule to be considered.
/// Anything below zero or exactly zero is ignored.
var/maximum_players = 0
/datum/dynamic_ruleset/New()
..()
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_roles += protected_roles
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
restricted_roles += "Assistant"
var/weights = CONFIG_GET(keyed_list/dynamic_weight)
var/costs = CONFIG_GET(keyed_list/dynamic_cost)
var/requirementses = CONFIG_GET(keyed_list/dynamic_requirements) // can't damn well use requirements
var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
if(config_tag in weights)
weight = weights[config_tag]
if(config_tag in costs)
cost = costs[config_tag]
if(config_tag in requirementses)
requirements = requirementses[config_tag]
if(config_tag in high_population_requirements)
high_population_requirement = high_population_requirements[config_tag]
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
else if (GLOB.master_mode != "dynamic") // This is here to make roundstart forced ruleset function.
qdel(src)
/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"
/// By default, a rule is acceptable if it satisfies the threat level/population requirements.
/// If your rule has extra checks, such as counting security officers, do that in ready() instead
/datum/dynamic_ruleset/proc/acceptable(population = 0, threat_level = 0)
if(minimum_players > population)
return FALSE
if(maximum_players > 0 && population > maximum_players)
return FALSE
if (population >= GLOB.dynamic_high_pop_limit)
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)
return (threat_level >= requirements[indice_pop])
/// 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.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
/datum/dynamic_ruleset/proc/pre_execute()
return TRUE
/// Called on post_setup on roundstart and when the rule executes on midround and latejoin.
/// Give your candidates or assignees equipment and antag datum here.
/datum/dynamic_ruleset/proc/execute()
for(var/datum/mind/M in assigned)
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)
return FALSE
return TRUE
/// 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
/datum/dynamic_ruleset/proc/get_weight()
if(repeatable && weight > 1 && repeatable_weight_decrease > 0)
for(var/datum/dynamic_ruleset/DR in mode.executed_rules)
if(istype(DR, type))
weight = max(weight-repeatable_weight_decrease,1)
return weight
/// Here you can remove candidates that do not meet your requirements.
/// This means if their job is not correct or they have disconnected you can remove them from candidates here.
/// Usually this does not need to be changed unless you need some specific requirements from your candidates.
/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()
/// Checks if round is finished, return true to end the round.
/// Only called if ruleset is flagged as HIGHLANDER_RULESET
/datum/dynamic_ruleset/proc/check_finished()
return FALSE
//////////////////////////////////////////////
// //
// ROUNDSTART RULESETS //
// //
//////////////////////////////////////////////
/// Checks if candidates are connected and if they are banned or don't want to be the antagonist.
/datum/dynamic_ruleset/roundstart/trim_candidates()
for(var/mob/dead/new_player/P in candidates)
if (!P.client || !P.mind) // Are they connected?
candidates.Remove(P)
continue
if(!mode.check_age(P.client, minimum_required_age))
candidates.Remove(P)
continue
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
/// Do your checks if the ruleset is ready to be executed here.
/// Should ignore certain checks if forced is TRUE
/datum/dynamic_ruleset/roundstart/ready(forced = FALSE)
return ..()