<commit_before>#define CURRENT_LIVING_PLAYERS	1
#define CURRENT_LIVING_ANTAGS	2
#define CURRENT_DEAD_PLAYERS	3
#define CURRENT_OBSERVERS	    4

#define ONLY_RULESET       1
#define HIGHLANDER_RULESET 2
#define TRAITOR_RULESET    4
#define MINOR_RULESET      8

#define RULESET_STOP_PROCESSING 1

// -- Injection delays
GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_latejoin_delay_max, (30 MINUTES))

GLOBAL_VAR_INIT(dynamic_midround_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_midround_delay_max, (30 MINUTES))

GLOBAL_VAR_INIT(dynamic_event_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_event_delay_max, (30 MINUTES)) // this is on top of regular events, so can't be quite as often


// -- Roundstart injection delays
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_min, (2 MINUTES))
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_max, (30 MINUTES))

GLOBAL_VAR_INIT(dynamic_first_midround_delay_min, (20 MINUTES))
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 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)
// A number between 0.5 and 4.
// 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.
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)
// If enabled does not accept or execute any rulesets.
GLOBAL_VAR_INIT(dynamic_forced_extended, FALSE)
// How high threat is required for HIGHLANDER_RULESETs stacking.
// This is independent of dynamic_no_stacking.
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.
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)

GLOBAL_VAR_INIT(dynamic_storyteller_type, null)

/datum/game_mode/dynamic
	name = "dynamic mode"
	config_tag = "dynamic"

	announce_span = "danger"
	announce_text = "Dynamic mode!" // This needs to be changed maybe

	reroll_friendly = FALSE;
	// Current storyteller
	var/datum/dynamic_storyteller/storyteller = null
	// Threat logging vars
	/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
	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.
	var/initial_threat_level = 0
	/// Things that cause a rolling threat adjustment to be displayed at roundend.
	var/list/threat_tallies = list()
	/// Running information about the threat. Can store text or datum entries.
	var/list/threat_log = list() 
	/// As above, but with info such as refunds.
	var/list/threat_log_verbose = list()
	/// List of roundstart rules used for selecting the rules.
	var/list/roundstart_rules = list()
	/// List of latejoin rules used for selecting the rules.
	var/list/latejoin_rules = list()
	/// List of midround rules used for selecting the rules.
	var/list/midround_rules = list()
	/// List of events used for reducing threat without causing antag injection (necessarily).
	var/list/events = list()
	/** # Pop range per requirement.
	  * If the value is five the range is:
	  * 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+
	  * If it is six the range is:
	  * 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+
	  * If it is seven the range is:
	  * 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. 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.
	var/list/candidates = list()
	/// Rules that are processed, rule_process is called on the rules in this list.
	var/list/current_rules = list()
	/// List of executed rulesets.
	var/list/executed_rules = list()
	/// Associative list of current players, in order: living players, living antagonists, dead players and observers.
	var/list/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
	/// When world.time is over this number the mode tries to inject a latejoin ruleset.
	var/latejoin_injection_cooldown = 0
	/// When world.time is over this number the mode tries to inject a midround ruleset.
	var/midround_injection_cooldown = 0
	/// When wor.dtime is over this number the mode tries to do an event.
	var/event_injection_cooldown = 0
	/// When TRUE GetInjectionChance returns 100.
	var/forced_injection = FALSE
	/// Forced ruleset to be executed for the next latejoin.
	var/datum/dynamic_ruleset/latejoin/forced_latejoin_rule = null
	/// When current_players was updated last time.
	var/pop_last_updated = 0
	/// How many percent of the rounds are more peaceful.
	var/peaceful_percentage = 50
	/// If a highlander executed.
	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.
	..()
	pop_per_requirement = CONFIG_GET(number/dynamic_pop_per_requirement)
	second_rule_req = CONFIG_GET(number_list/dynamic_second_rule_requirements)
	third_rule_req = CONFIG_GET(number_list/dynamic_third_rule_requirements)
	if(second_rule_req.len<10)
		second_rule_req = list(101, 101, 101, 101, 100, 90, 80, 70, 60, 50)
	if(third_rule_req.len<10)
		third_rule_req = list(101, 101, 101, 101, 101, 100, 90, 80, 70, 60)
	high_pop_second_rule_req = CONFIG_GET(number/dynamic_second_rule_high_pop_requirement)
	high_pop_third_rule_req = CONFIG_GET(number/dynamic_third_rule_high_pop_requirement)
	GLOB.dynamic_high_pop_limit = CONFIG_GET(number/dynamic_high_pop_limit)
	GLOB.dynamic_latejoin_delay_min = CONFIG_GET(number/dynamic_latejoin_delay_min)*600
	GLOB.dynamic_latejoin_delay_max = CONFIG_GET(number/dynamic_latejoin_delay_max)*600
	GLOB.dynamic_midround_delay_min = CONFIG_GET(number/dynamic_midround_delay_min)*600
	GLOB.dynamic_midround_delay_max = CONFIG_GET(number/dynamic_midround_delay_max)*600
	GLOB.dynamic_first_latejoin_delay_min = CONFIG_GET(number/dynamic_first_latejoin_delay_min)*600
	GLOB.dynamic_first_latejoin_delay_max = CONFIG_GET(number/dynamic_first_latejoin_delay_max)*600
	GLOB.dynamic_first_midround_delay_min = CONFIG_GET(number/dynamic_first_midround_delay_min)*600
	GLOB.dynamic_first_midround_delay_max = CONFIG_GET(number/dynamic_first_midround_delay_max)*600

/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><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/>"
	dat += "<br/>"
	dat += "Storyteller: <b>[storyteller.name]</b>"
	dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
	dat += "<i>On average, <b>[peaceful_percentage]</b>% of the rounds are more peaceful.</i><br/>"
	dat += "Forced extended: <a href='?src=\ref[src];[HrefToken()];forced_extended=1'><b>[GLOB.dynamic_forced_extended ? "On" : "Off"]</b></a><br/>"
	dat += "Classic secret (only autotraitor): <a href='?src=\ref[src];[HrefToken()];classic_secret=1'><b>[GLOB.dynamic_classic_secret ? "On" : "Off"]</b></a><br/>"
	dat += "No stacking (only one round-ender): <a href='?src=\ref[src];[HrefToken()];no_stacking=1'><b>[GLOB.dynamic_no_stacking ? "On" : "Off"]</b></a><br/>"
	dat += "Stacking limit: [GLOB.dynamic_stacking_limit] <a href='?src=\ref[src];[HrefToken()];stacking_limit=1'>\[Adjust\]</A>"
	dat += "<br/>"
	dat += "Executed rulesets: "
	if (executed_rules.len > 0)
		dat += "<br/>"<commit_msg>and a line break goes there<commit_after>#define CURRENT_LIVING_PLAYERS	1
#define CURRENT_LIVING_ANTAGS	2
#define CURRENT_DEAD_PLAYERS	3
#define CURRENT_OBSERVERS	    4

#define ONLY_RULESET       1
#define HIGHLANDER_RULESET 2
#define TRAITOR_RULESET    4
#define MINOR_RULESET      8

#define RULESET_STOP_PROCESSING 1

// -- Injection delays
GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_latejoin_delay_max, (30 MINUTES))

GLOBAL_VAR_INIT(dynamic_midround_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_midround_delay_max, (30 MINUTES))

GLOBAL_VAR_INIT(dynamic_event_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_event_delay_max, (30 MINUTES)) // this is on top of regular events, so can't be quite as often


// -- Roundstart injection delays
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_min, (2 MINUTES))
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_max, (30 MINUTES))

GLOBAL_VAR_INIT(dynamic_first_midround_delay_min, (20 MINUTES))
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 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)
// A number between 0.5 and 4.
// 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.
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)
// If enabled does not accept or execute any rulesets.
GLOBAL_VAR_INIT(dynamic_forced_extended, FALSE)
// How high threat is required for HIGHLANDER_RULESETs stacking.
// This is independent of dynamic_no_stacking.
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.
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)

GLOBAL_VAR_INIT(dynamic_storyteller_type, null)

/datum/game_mode/dynamic
	name = "dynamic mode"
	config_tag = "dynamic"

	announce_span = "danger"
	announce_text = "Dynamic mode!" // This needs to be changed maybe

	reroll_friendly = FALSE;
	// Current storyteller
	var/datum/dynamic_storyteller/storyteller = null
	// Threat logging vars
	/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
	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.
	var/initial_threat_level = 0
	/// Things that cause a rolling threat adjustment to be displayed at roundend.
	var/list/threat_tallies = list()
	/// Running information about the threat. Can store text or datum entries.
	var/list/threat_log = list() 
	/// As above, but with info such as refunds.
	var/list/threat_log_verbose = list()
	/// List of roundstart rules used for selecting the rules.
	var/list/roundstart_rules = list()
	/// List of latejoin rules used for selecting the rules.
	var/list/latejoin_rules = list()
	/// List of midround rules used for selecting the rules.
	var/list/midround_rules = list()
	/// List of events used for reducing threat without causing antag injection (necessarily).
	var/list/events = list()
	/** # Pop range per requirement.
	  * If the value is five the range is:
	  * 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+
	  * If it is six the range is:
	  * 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+
	  * If it is seven the range is:
	  * 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. 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.
	var/list/candidates = list()
	/// Rules that are processed, rule_process is called on the rules in this list.
	var/list/current_rules = list()
	/// List of executed rulesets.
	var/list/executed_rules = list()
	/// Associative list of current players, in order: living players, living antagonists, dead players and observers.
	var/list/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
	/// When world.time is over this number the mode tries to inject a latejoin ruleset.
	var/latejoin_injection_cooldown = 0
	/// When world.time is over this number the mode tries to inject a midround ruleset.
	var/midround_injection_cooldown = 0
	/// When wor.dtime is over this number the mode tries to do an event.
	var/event_injection_cooldown = 0
	/// When TRUE GetInjectionChance returns 100.
	var/forced_injection = FALSE
	/// Forced ruleset to be executed for the next latejoin.
	var/datum/dynamic_ruleset/latejoin/forced_latejoin_rule = null
	/// When current_players was updated last time.
	var/pop_last_updated = 0
	/// How many percent of the rounds are more peaceful.
	var/peaceful_percentage = 50
	/// If a highlander executed.
	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.
	..()
	pop_per_requirement = CONFIG_GET(number/dynamic_pop_per_requirement)
	second_rule_req = CONFIG_GET(number_list/dynamic_second_rule_requirements)
	third_rule_req = CONFIG_GET(number_list/dynamic_third_rule_requirements)
	if(second_rule_req.len<10)
		second_rule_req = list(101, 101, 101, 101, 100, 90, 80, 70, 60, 50)
	if(third_rule_req.len<10)
		third_rule_req = list(101, 101, 101, 101, 101, 100, 90, 80, 70, 60)
	high_pop_second_rule_req = CONFIG_GET(number/dynamic_second_rule_high_pop_requirement)
	high_pop_third_rule_req = CONFIG_GET(number/dynamic_third_rule_high_pop_requirement)
	GLOB.dynamic_high_pop_limit = CONFIG_GET(number/dynamic_high_pop_limit)
	GLOB.dynamic_latejoin_delay_min = CONFIG_GET(number/dynamic_latejoin_delay_min)*600
	GLOB.dynamic_latejoin_delay_max = CONFIG_GET(number/dynamic_latejoin_delay_max)*600
	GLOB.dynamic_midround_delay_min = CONFIG_GET(number/dynamic_midround_delay_min)*600
	GLOB.dynamic_midround_delay_max = CONFIG_GET(number/dynamic_midround_delay_max)*600
	GLOB.dynamic_first_latejoin_delay_min = CONFIG_GET(number/dynamic_first_latejoin_delay_min)*600
	GLOB.dynamic_first_latejoin_delay_max = CONFIG_GET(number/dynamic_first_latejoin_delay_max)*600
	GLOB.dynamic_first_midround_delay_min = CONFIG_GET(number/dynamic_first_midround_delay_min)*600
	GLOB.dynamic_first_midround_delay_max = CONFIG_GET(number/dynamic_first_midround_delay_max)*600

/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><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/>"
	dat += "<br/>"
	dat += "Storyteller: <b>[storyteller.name]</b><br/>"
	dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
	dat += "<i>On average, <b>[peaceful_percentage]</b>% of the rounds are more peaceful.</i><br/>"
	dat += "Forced extended: <a href='?src=\ref[src];[HrefToken()];forced_extended=1'><b>[GLOB.dynamic_forced_extended ? "On" : "Off"]</b></a><br/>"
	dat += "Classic secret (only autotraitor): <a href='?src=\ref[src];[HrefToken()];classic_secret=1'><b>[GLOB.dynamic_classic_secret ? "On" : "Off"]</b></a><br/>"
	dat += "No stacking (only one round-ender): <a href='?src=\ref[src];[HrefToken()];no_stacking=1'><b>[GLOB.dynamic_no_stacking ? "On" : "Off"]</b></a><br/>"
	dat += "Stacking limit: [GLOB.dynamic_stacking_limit] <a href='?src=\ref[src];[HrefToken()];stacking_limit=1'>\[Adjust\]</A>"
	dat += "<br/>"
	dat += "Executed rulesets: "
	if (executed_rules.len > 0)
		dat += "<br/>"
{'fraction_non_alphanumeric': 0.11405668779797769, 'fraction_numerical': 0.03531199245559805, 'mean_word_length': 5.000628733102798, 'pattern_counts': {'":': 0, '<': 114, '<?xml version=': 0, '>': 112, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4996489', 'n_tokens_mistral': 7165, 'n_tokens_neox': 6538, 'n_words': 1790}