mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 18:32:03 +00:00
* Clean up scoreboard * Update scoreboard.dm * Update dynamic.dm * Update dynamic.dm * Update dynamic.dm * Update dynamic.dm * Update dynamic.dm * Update scoreboard.dm
901 lines
41 KiB
Plaintext
901 lines
41 KiB
Plaintext
var/list/forced_roundstart_ruleset = list()
|
|
|
|
// -- Distribution parameters chosen prior to roundstart --
|
|
var/dynamic_curve_centre = 0
|
|
var/dynamic_curve_width = 1.8
|
|
var/dynamic_chosen_mode = LORENTZ
|
|
|
|
// -- Dynamic tweaks chosen prior to roundstart --
|
|
var/dynamic_no_stacking = 1 // NO STACKING : only one "round-ender", except if we're above 80 threat
|
|
var/dynamic_classic_secret = 0 // Only one roundstart ruleset, and only autotraitor + minor rules allowed
|
|
var/dynamic_high_pop_limit = 45 // Will switch to "high pop override" if the roundstart population is above this
|
|
var/dynamic_forced_extended = 0 // No rulesets will be drated, ever
|
|
|
|
var/stacking_limit = 90
|
|
|
|
#define BASE_SOLO_REFUND 10
|
|
|
|
/datum/gamemode/dynamic
|
|
name = "Dynamic Mode"
|
|
|
|
//Threat logging vars
|
|
var/threat_level = 0//the "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
|
|
var/starting_threat = 0 //threat_level's initially rolled value. Threat_level isn't changed by many things.
|
|
var/threat = 0//set at the beginning of the round. Spent by the mode to "purchase" roundstart rules.
|
|
var/list/threat_log = list() //Running information about the threat. Can store text or datum entries.
|
|
|
|
// Midround threat
|
|
var/midround_threat_level = 0
|
|
var/midround_starting_threat = 0
|
|
var/midround_threat = 0
|
|
var/list/midround_threat_log = list()
|
|
|
|
var/list/roundstart_rules = list()
|
|
var/list/latejoin_rules = list()
|
|
var/list/midround_rules = list()
|
|
var/list/second_rule_req = list(100,100,100,80,60,40,20,0,0,0)//requirements for extra round start rules
|
|
var/list/third_rule_req = list(100,100,100,100,100,70,50,30,10,0)
|
|
var/roundstart_pop_ready = 0
|
|
var/list/candidates = list()
|
|
var/list/current_rules = list()
|
|
var/list/executed_rules = list()
|
|
var/list/previously_executed_rules = list()
|
|
var/list/rules_text = list()
|
|
|
|
var/list/living_players = list()
|
|
var/list/living_antags = list()
|
|
var/list/dead_players = list()
|
|
var/list/list_observers = list()
|
|
var/last_time_of_population = 0
|
|
|
|
var/latejoin_injection_cooldown = 0
|
|
var/midround_injection_cooldown = 0
|
|
|
|
var/datum/dynamic_ruleset/latejoin/forced_latejoin_rule = null
|
|
|
|
var/datum/stat/dynamic_mode/dynamic_stats = null
|
|
var/pop_last_updated = 0
|
|
|
|
var/distribution_mode = LORENTZ
|
|
var/relative_threat = 0 // Relative threat, Lorentz-distributed.
|
|
var/curve_centre_of_round = 0
|
|
var/curve_width_of_round = 1.8
|
|
|
|
var/peaceful_percentage = 50
|
|
var/highlander_rulesets_favoured = 0
|
|
|
|
// -- Special tweaks --
|
|
var/no_stacking = 1
|
|
var/classic_secret = 0
|
|
var/high_pop_limit = 45
|
|
var/forced_extended = 0
|
|
|
|
/datum/gamemode/dynamic/AdminPanelEntry()
|
|
var/dat = list()
|
|
dat += "Dynamic Mode <a href='?_src_=vars;Vars=\ref[src]'>\[VV\]</A><BR>"
|
|
dat += "Threat Level: <b>[threat_level]</b>, in-round injection threat level: <b>[midround_threat_level]</b><br/>"
|
|
dat += "Threat to Spend: <b>[midround_threat]</b> <a href='?_src_=holder;adjustthreat=1'>\[Adjust\]</A> <a href='?_src_=holder;threatlog=1'>\[View Log\]</a><br/>"
|
|
dat += "<br/>"
|
|
dat += "Parameters: centre = [curve_centre_of_round] ; width = [curve_width_of_round].<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];forced_extended=1'><b>[forced_extended ? "On" : "Off"]</b></a><br/>"
|
|
dat += "No stacking (only one round-ender): <a href='?src=\ref[src];no_stacking=1'><b>[no_stacking ? "On" : "Off"]</b></a><br/>"
|
|
dat += "Classic secret (only autotraitor): <a href='?src=\ref[src];classic_secret=1'><b>[classic_secret ? "On" : "Off"]</b></a><br/>"
|
|
dat += "Stacking limit: <a href='?src=\ref[usr.client.holder];stacking_limit=1'>[stacking_limit]</a>"
|
|
dat += "<br/>"
|
|
dat += "Executed rulesets: "
|
|
if (executed_rules.len > 0)
|
|
dat += "<br/>"
|
|
for (var/datum/dynamic_ruleset/DR in executed_rules)
|
|
var/ruletype = ""
|
|
if (istype (DR, /datum/dynamic_ruleset/roundstart))
|
|
ruletype = "Roundstart"
|
|
if (istype (DR, /datum/dynamic_ruleset/latejoin))
|
|
ruletype = "Latejoin"
|
|
if (istype (DR, /datum/dynamic_ruleset/midround))
|
|
ruletype = "Midround"
|
|
dat += "[ruletype] - <b>[DR.name]</b><br>"
|
|
else
|
|
dat += "none.<br>"
|
|
dat += "<br>Injection Timers: (<b>[GetInjectionChance()]%</b> chance)<BR>"
|
|
dat += "Latejoin: [latejoin_injection_cooldown>60 ? "[round(latejoin_injection_cooldown/60,0.1)] minutes" : "[latejoin_injection_cooldown] seconds"] <a href='?_src_=holder;injectnow=1'>\[Now!\]</A><BR>"
|
|
dat += "Midround: [midround_injection_cooldown>60 ? "[round(midround_injection_cooldown/60,0.1)] minutes" : "[midround_injection_cooldown] seconds"] <a href='?_src_=holder;injectnow=2'>\[Now!\]</A><BR>"
|
|
return jointext(dat, "")
|
|
|
|
/datum/gamemode/dynamic/Topic(href, href_list)
|
|
if (..()) // Sanity, maybe ?
|
|
return
|
|
if(!usr || !usr.client)
|
|
return
|
|
if(href_list["threatlog"]) //don't need admin for this
|
|
show_threatlog(usr)
|
|
return
|
|
if(!usr.check_rights(R_ADMIN))
|
|
return
|
|
if (href_list["forced_extended"])
|
|
forced_extended =! forced_extended
|
|
message_admins("[key_name(usr)] has set 'forced extended' to [forced_extended].")
|
|
else if (href_list["no_stacking"])
|
|
no_stacking =! no_stacking
|
|
message_admins("[key_name(usr)] has set 'no stacking' to [no_stacking].")
|
|
else if (href_list["classic_secret"])
|
|
classic_secret =! classic_secret
|
|
message_admins("[key_name(usr)] has set 'classic secret' to [classic_secret].")
|
|
|
|
usr.client.holder.check_antagonists() // Refreshes the window
|
|
|
|
/datum/gamemode/dynamic/proc/show_threatlog(mob/admin)
|
|
if(!ticker || !ticker.mode)
|
|
alert("Ticker and Game Mode aren't initialized yet!", "Alert")
|
|
return
|
|
|
|
if(!admin.check_rights(R_ADMIN) && (ticker.current_state != GAME_STATE_FINISHED))
|
|
return
|
|
|
|
var/out = "<TITLE>Threat Log</TITLE><B><font size='3'>Threat Log</font></B><br><B>Starting Threat:</B> [starting_threat], <b>midround</b>: [midround_starting_threat]<BR>"
|
|
|
|
for(var/entry in threat_log)
|
|
if(istext(entry))
|
|
out += "[entry]<BR>"
|
|
if(istype(entry,/datum/role/catbeast))
|
|
var/datum/role/catbeast/C = entry
|
|
out += "Catbeast threat regenerated/threat_level inflated: [C.threat_generated]/[C.threat_level_inflated]<BR>"
|
|
|
|
out += "<B>Remaining threat/threat_level:</B> [threat]/[threat_level]<br/>"
|
|
out += "<B>Remaining midround threat/threat_level:</B> [midround_threat]/[midround_threat_level]"
|
|
|
|
usr << browse(out, "window=threatlog;size=700x500")
|
|
|
|
/datum/gamemode/dynamic/GetScoreboard()
|
|
|
|
dat += "<h2>Dynamic Mode - Roundstart Threat = <font color='red'>[threat_level]%</font>, Midround Threat = <font color='red'>[midround_threat_level]%</font></h2>"
|
|
if (executed_rules.len > 0)
|
|
for (var/datum/dynamic_ruleset/DR in executed_rules)
|
|
var/ruletype = ""
|
|
if (istype (DR, /datum/dynamic_ruleset/roundstart))
|
|
ruletype = "roundstart"
|
|
if (istype (DR, /datum/dynamic_ruleset/latejoin))
|
|
ruletype = "latejoin"
|
|
if (istype (DR, /datum/dynamic_ruleset/midround))
|
|
ruletype = "midround"
|
|
dat += "([ruletype]) - <b>[DR.name]</b>[DR.calledBy ? " (called by [DR.calledBy])" : ""]<br>"
|
|
rules_text += "[ruletype] - **[DR.name]** [DR.calledBy ? " (called by [DR.calledBy])" : ""]"
|
|
dat += "<a href='?src=\ref[src];threatlog=1'>\[View Log\]</a>"
|
|
else
|
|
dat += "(extended)"
|
|
rules_text += "None"
|
|
dat += "<HR>"
|
|
. = ..()
|
|
|
|
/datum/gamemode/dynamic/send2servers()
|
|
send2mainirc("A round of [name] has ended - [living_players.len] survivors, [dead_players.len] ghosts. Final crew score: [score.crewscore]. ([score.rating])")
|
|
send2maindiscord("A round of **[name]** has ended - **[living_players.len]** survivors, **[dead_players.len]** ghosts. Final crew score: **[score.crewscore]**. ([score.rating])")
|
|
send2mainirc("Dynamic mode Roundstart Threat: [starting_threat][(starting_threat!=threat_level)?" ([threat_level])":""], Midround Threat: [midround_starting_threat][(midround_starting_threat!=midround_threat_level)?" ([midround_threat_level])":""], rulesets: [jointext(rules_text, ", ")].")
|
|
send2maindiscord("Dynamic mode Roundstart Threat: **[starting_threat][(starting_threat!=threat_level)?" ([threat_level])":""]**, Midround Threat: **[midround_starting_threat][(midround_starting_threat!=midround_threat_level)?" ([midround_threat_level])":""]**, rulesets: [jointext(rules_text, ", ")]")
|
|
|
|
/datum/gamemode/dynamic/can_start()
|
|
distribution_mode = dynamic_chosen_mode
|
|
message_admins("Distribution mode is : [dynamic_chosen_mode].")
|
|
curve_centre_of_round = dynamic_curve_centre
|
|
curve_width_of_round = dynamic_curve_width
|
|
message_admins("Curve centre and curve width are : [curve_centre_of_round], [curve_width_of_round]")
|
|
forced_extended = dynamic_forced_extended
|
|
if (forced_extended)
|
|
message_admins("The round will be forced to extended.")
|
|
no_stacking = dynamic_no_stacking
|
|
if (no_stacking)
|
|
message_admins("Round-ending rulesets won't stack, unless the threat is above stacking_limit ([stacking_limit]).")
|
|
classic_secret = dynamic_classic_secret
|
|
if (classic_secret)
|
|
message_admins("Classic secret mode active: only autotraitors will spawn, and we will only have one roundstart ruleset.")
|
|
log_admin("Dynamic mode parameters for the round: distrib mode = [distribution_mode], centre = [curve_centre_of_round], width is [curve_width_of_round]. Extended : [forced_extended], no stacking : [no_stacking], classic secret: [classic_secret].")
|
|
|
|
generate_threat()
|
|
|
|
|
|
var/latejoin_injection_cooldown_middle = 0.5*(LATEJOIN_DELAY_MAX + LATEJOIN_DELAY_MIN)
|
|
latejoin_injection_cooldown = round(clamp(exp_distribution(latejoin_injection_cooldown_middle), LATEJOIN_DELAY_MIN, LATEJOIN_DELAY_MAX))
|
|
|
|
var/midround_injection_cooldown_middle = 0.5*(MIDROUND_DELAY_MAX + MIDROUND_DELAY_MIN)
|
|
midround_injection_cooldown = round(clamp(exp_distribution(midround_injection_cooldown_middle), MIDROUND_DELAY_MIN, MIDROUND_DELAY_MAX))
|
|
|
|
message_admins("Dynamic Mode initialized with a Threat Level of... <font size='8'>[threat_level]</font> and <font size='8'>[midround_threat_level]</font> for midround!")
|
|
log_admin("Dynamic Mode initialized with a Threat Level of... [threat_level] and [midround_threat_level]</font> for midround!")
|
|
|
|
message_admins("Parameters were: centre = [curve_centre_of_round], width = [curve_width_of_round].")
|
|
log_admin("Parameters were: centre = [curve_centre_of_round], width = [curve_width_of_round].")
|
|
|
|
var/rst_pop = 0
|
|
for(var/mob/living/player in player_list)
|
|
if(player.mind)
|
|
rst_pop++
|
|
if (rst_pop >= high_pop_limit)
|
|
message_admins("DYNAMIC MODE: Mode: High Population Override is in effect! ([rst_pop]/[high_pop_limit]) Threat Level will have more impact on which roles will appear, and player population less.")
|
|
log_admin("DYNAMIC MODE: High Population Override is in effect! ([rst_pop]/[high_pop_limit]) Threat Level will have more impact on which roles will appear, and player population less.")
|
|
dynamic_stats = new
|
|
dynamic_stats.starting_threat_level = threat_level
|
|
|
|
if (round(threat_level*10) == 666)
|
|
forced_roundstart_ruleset += new /datum/dynamic_ruleset/roundstart/bloodcult()
|
|
forced_roundstart_ruleset += new /datum/dynamic_ruleset/roundstart/vampire()
|
|
log_admin("DYNAMIC MODE: 666 threat override.")
|
|
message_admins("DYNAMIC MODE: 666 threat override.", 1)
|
|
|
|
return 1
|
|
|
|
/datum/gamemode/dynamic/proc/read_previous_rounds_rulesets()
|
|
previously_executed_rules = list(
|
|
"one_round_ago" = list(),
|
|
"two_rounds_ago" = list(),
|
|
"three_rounds_ago" = list()
|
|
)
|
|
var/list/data = SSpersistence_misc.read_data(/datum/persistence_task/latest_dynamic_rulesets)
|
|
if(!length(data))
|
|
return
|
|
|
|
for (var/entries in data)
|
|
var/previous_rulesets_text = data[entries]
|
|
if(!length(previous_rulesets_text))
|
|
return
|
|
var/list/previous_rulesets = list()
|
|
for(var/entry in previous_rulesets_text)
|
|
var/entry_path = text2path(entry)
|
|
if(entry_path) // It's possible that a ruleset that existed last round doesn't exist anymore
|
|
previous_rulesets += entry_path
|
|
previously_executed_rules[entries] = previous_rulesets
|
|
|
|
/datum/gamemode/dynamic/Setup()
|
|
read_previous_rounds_rulesets()
|
|
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart) - /datum/dynamic_ruleset/roundstart/delayed/)
|
|
roundstart_rules += new rule()
|
|
for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin))
|
|
latejoin_rules += new rule()
|
|
for (var/rule in subtypesof(/datum/dynamic_ruleset/midround))
|
|
var/datum/dynamic_ruleset/midround/DR = rule
|
|
if (initial(DR.weight))
|
|
midround_rules += new rule()
|
|
for(var/mob/living/player in player_list)
|
|
if(player.mind)
|
|
roundstart_pop_ready++
|
|
candidates.Add(player)
|
|
message_admins("DYNAMIC MODE: Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.")
|
|
log_admin("DYNAMIC MODE: Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.")
|
|
if (candidates.len <= 0)
|
|
message_admins("DYNAMIC MODE: Not a single player readied-up. The round will begin without any roles assigned.")
|
|
log_admin("DYNAMIC MODE: Not a single player readied-up. The round will begin without any roles assigned.")
|
|
return 1
|
|
if (roundstart_rules.len <= 0)
|
|
message_admins("DYNAMIC MODE: There are no roundstart rules within the code, what the fuck? The round will begin without any roles assigned.")
|
|
log_admin("DYNAMIC MODE: There are no roundstart rules within the code, what the fuck? The round will begin without any roles assigned.")
|
|
return 1
|
|
if (forced_roundstart_ruleset.len > 0)
|
|
rigged_roundstart()
|
|
else
|
|
roundstart()
|
|
|
|
var/starting_rulesets = ""
|
|
for (var/datum/dynamic_ruleset/roundstart/DR in executed_rules)
|
|
starting_rulesets += "[DR.name], "
|
|
dynamic_stats.round_start_pop = candidates.len
|
|
dynamic_stats.round_start_rulesets = starting_rulesets
|
|
dynamic_stats.measure_threat(threat)
|
|
candidates.Cut()
|
|
return 1
|
|
|
|
/datum/gamemode/dynamic/proc/rigged_roundstart()
|
|
message_admins("DYNAMIC MODE: [forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
|
|
log_admin("DYNAMIC MODE: [forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
|
|
var/forced_rules = 0
|
|
|
|
for (var/datum/forced_ruleset/forced_rule in forced_roundstart_ruleset)//By checking in this order we allow admins to set up priorities among the forced rulesets.
|
|
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
|
|
if (forced_rule.name == rule.name)
|
|
rule.candidates = candidates.Copy()
|
|
rule.trim_candidates()
|
|
if (rule.ready(TRUE))
|
|
forced_rules++
|
|
rule.calledBy = forced_rule.calledBy
|
|
if (!rule.choose_candidates())
|
|
stack_trace("rule [rule] failed to choose candidates despite ready() returning 1.")
|
|
|
|
message_admins("DYNAMIC MODE: <font size='3'>[rule.name]</font> successfully forced!")
|
|
log_admin("DYNAMIC MODE: <font size='3'>[rule.name]</font> successfully forced!")
|
|
|
|
//we don't spend threat on forced rulesets
|
|
threat_log += "[worldtime2text()]: Roundstart [rule.name] forced"
|
|
|
|
if (istype(rule, /datum/dynamic_ruleset/roundstart/delayed/))
|
|
message_admins("DYNAMIC MODE: with a delay of [rule:delay/10] seconds.")
|
|
log_admin("DYNAMIC MODE: with a delay of [rule:delay/10] seconds.")
|
|
return pick_delay(rule)
|
|
|
|
if (rule.execute())//this should never fail since ready() returned 1
|
|
rule.stillborn = IsRoundAboutToEnd()
|
|
executed_rules += rule
|
|
if (rule.persistent)
|
|
current_rules += rule
|
|
for(var/mob/M in rule.assigned)
|
|
candidates -= M
|
|
return 1
|
|
else
|
|
message_admins("DYNAMIC MODE: ....except not because whomever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
|
|
log_admin("DYNAMIC MODE: ....except not because whomever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
|
|
|
|
if (forced_rules == 0)
|
|
message_admins("DYNAMIC MODE: Not a single forced ruleset could be executed. Sad! Will now start a regular round of dynamic.")
|
|
log_admin("DYNAMIC MODE: Not a single forced ruleset could be executed. Sad! Will now start a regular round of dynamic.")
|
|
roundstart()
|
|
|
|
/datum/gamemode/dynamic/proc/roundstart()
|
|
if (forced_extended)
|
|
message_admins("DYNAMIC MODE: Starting a round of forced extended.")
|
|
log_admin("DYNAMIC MODE: Starting a round of forced extended.")
|
|
return 1
|
|
|
|
var/indice_pop = min(10,round(roundstart_pop_ready/5)+1)
|
|
var/extra_rulesets_amount = 0
|
|
|
|
if (classic_secret) // Classic secret experience : one & only one roundstart ruleset
|
|
extra_rulesets_amount = 0
|
|
else
|
|
var/rst_pop = 0
|
|
for(var/mob/living/player in player_list)
|
|
if(player.mind)
|
|
rst_pop++
|
|
if (rst_pop > high_pop_limit)
|
|
if (threat_level > 50)
|
|
extra_rulesets_amount++
|
|
if (threat_level > 75)
|
|
extra_rulesets_amount++
|
|
else
|
|
if (threat_level >= second_rule_req[indice_pop])
|
|
extra_rulesets_amount++
|
|
if (threat_level >= third_rule_req[indice_pop])
|
|
extra_rulesets_amount++
|
|
|
|
if (extra_rulesets_amount && prob(50))
|
|
message_admins("DYNAMIC MODE: Rather than extra rulesets, we'll try to draft spicier ones.")
|
|
log_admin("DYNAMIC MODE: Rather than extra rulesets, we'll try to draft spicier ones.")
|
|
highlander_rulesets_favoured = TRUE
|
|
extra_rulesets_amount = 0
|
|
|
|
var/i = 0
|
|
var/list/drafted_rules = list()
|
|
|
|
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
|
|
if (rule.acceptable(roundstart_pop_ready,threat_level) && threat >= rule.cost) //if we got the population and threat required
|
|
i++ //we check whether we've got eligible players
|
|
rule.candidates = candidates.Copy()
|
|
rule.trim_candidates()
|
|
if (rule.ready())
|
|
drafted_rules[rule] = rule.get_weight()
|
|
|
|
if (classic_secret)
|
|
message_admins("DYNAMIC MODE: Classic secret was forced.")
|
|
log_admin("DYNAMIC MODE: Classic secret was forced.")
|
|
else
|
|
message_admins("DYNAMIC MODE: [i] rulesets qualify for the current pop and threat level, including [drafted_rules.len] with eligible candidates.")
|
|
log_admin("DYNAMIC MODE: [i] rulesets qualify for the current pop and threat level, including [drafted_rules.len] with eligible candidates.")
|
|
|
|
var/list/datum/dynamic_ruleset/roundstart/candidate_rules = list()
|
|
|
|
for (var/j = 1 to (1 + extra_rulesets_amount))
|
|
// 1. Scrapping all the rules with cost above remaining threat level
|
|
for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules)
|
|
if (rule.cost > threat)
|
|
drafted_rules -= rule
|
|
|
|
// 2. No rules left? Abort.
|
|
if (drafted_rules.len <= 0)
|
|
break
|
|
|
|
// 3. Picking up the CHOSEN ONE.
|
|
var/datum/dynamic_ruleset/chosen_one = picking_roundstart_rule(drafted_rules, candidate_rules)
|
|
|
|
// 4. Adding to the LIST.
|
|
if (chosen_one)
|
|
message_admins("DYNAMIC MODE: Picking a [istype(chosen_one, /datum/dynamic_ruleset/roundstart/delayed/) ? " delayed " : ""] ruleset...<font size='3'>[chosen_one.name]</font>!")
|
|
log_admin("DYNAMIC MODE: Picking a [istype(chosen_one, /datum/dynamic_ruleset/roundstart/delayed/) ? " delayed " : ""] ruleset...<font size='3'>[chosen_one.name]</font>!")
|
|
candidate_rules += chosen_one
|
|
drafted_rules -= chosen_one
|
|
spend_threat(chosen_one.cost)
|
|
if(!chosen_one.choose_candidates())
|
|
stack_trace("rule [chosen_one] failed to choose candidates despite ready() returning 1.")
|
|
drafted_rules = trimming_remaining_rules(chosen_one, drafted_rules)
|
|
|
|
// Is THE LIST non-empty ?
|
|
if (candidate_rules.len > 0)
|
|
for (var/datum/dynamic_ruleset/roundstart/DR in candidate_rules)
|
|
executing_roundstart_rule(DR)
|
|
return 1
|
|
else
|
|
message_admins("DYNAMIC MODE: The mode failed to pick a first ruleset. The round will begin without any roles assigned.")
|
|
log_admin("DYNAMIC MODE: The mode failed to pick a first ruleset. The round will begin without any roles assigned.")
|
|
return 0
|
|
|
|
// -- PICKING a rule, which means checking if you can do it.
|
|
// drafted_rules : the eligible rules for this round, after the threat cost of other rules has been taken into account and they have enough candidates
|
|
// returns : the chosen dynamic ruleset.
|
|
/datum/gamemode/dynamic/proc/picking_roundstart_rule(var/list/drafted_rules = list(), var/list/candidate_rules = list())
|
|
var/datum/dynamic_ruleset/my_rule = null
|
|
while(!my_rule && drafted_rules.len > 0)
|
|
message_admins("DYNAMIC MODE: Drafted rules: [json_encode(drafted_rules)]")
|
|
log_admin("DYNAMIC MODE: Drafted rules: [json_encode(drafted_rules)]")
|
|
my_rule = pickweight(drafted_rules)
|
|
if (threat < stacking_limit && no_stacking)
|
|
for (var/datum/dynamic_ruleset/roundstart/DR in candidate_rules + executed_rules)
|
|
if ((DR.flags & HIGHLANDER_RULESET) && (my_rule.flags & HIGHLANDER_RULESET))
|
|
message_admins("DYNAMIC MODE: Ruleset [my_rule.name] refused as we already have a round-ending ruleset.")
|
|
log_admin("DYNAMIC MODE: Ruleset [my_rule.name] refused as we already have a round-ending ruleset.")
|
|
drafted_rules -= my_rule
|
|
my_rule = null
|
|
return my_rule
|
|
|
|
// -- A rule has been picked. We have to clean its assigned candidates from other rules to avoid someone getting multiple antags. We also check if that new rule has enough remaining candidates.
|
|
// choosen_one : the rule who has just been picked.
|
|
// drafted_rules : the rules currently drafted.
|
|
// returns : the new drafted rules.
|
|
/datum/gamemode/dynamic/proc/trimming_remaining_rules(var/datum/dynamic_ruleset/choosen_one, var/list/drafted_rules)
|
|
for(var/mob/M in choosen_one.assigned)
|
|
candidates -= M
|
|
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
|
|
rule.candidates -= M//removing the assigned players from the candidates for the other rules
|
|
if (!rule.ready())
|
|
drafted_rules -= rule//and removing rules that are no longer eligible
|
|
message_admins("[rule] no longer valid for picking.")
|
|
return drafted_rules
|
|
|
|
// -- Executing a rule, which means spawning the traitor, removing the threat cost, etc.
|
|
// the_rule: the rule being executed
|
|
// returns: 0 or 1 depending on success. (failure meaning something runtimed mid-code.)
|
|
/datum/gamemode/dynamic/proc/executing_roundstart_rule(var/datum/dynamic_ruleset/the_rule)
|
|
if (istype(the_rule, /datum/dynamic_ruleset/roundstart/delayed/))
|
|
message_admins("DYNAMIC MODE: Delayed ruleset, with a delay of [the_rule:delay/10] seconds.")
|
|
log_admin("DYNAMIC MODE: Delayed ruleset, with a delay of [the_rule:delay/10] seconds.")
|
|
threat_log += "[worldtime2text()]: Roundstart [the_rule.name] spent [the_rule.cost]"
|
|
return pick_delay(the_rule)
|
|
|
|
threat_log += "[worldtime2text()]: Roundstart [the_rule.name] spent [the_rule.cost]"
|
|
if (the_rule.execute())//this should never fail since ready() returned 1
|
|
the_rule.stillborn = IsRoundAboutToEnd()
|
|
executed_rules += the_rule
|
|
if (the_rule.persistent)
|
|
current_rules += the_rule
|
|
else
|
|
message_admins("DYNAMIC MODE: ....except not because whomever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
|
|
log_admin("DYNAMIC MODE: ....except not because whomever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
|
|
return 0
|
|
|
|
/datum/gamemode/dynamic/proc/pick_delay(var/datum/dynamic_ruleset/roundstart/delayed/rule)
|
|
spawn()
|
|
sleep(rule.delay)
|
|
rule.candidates = player_list.Copy()
|
|
rule.trim_candidates()
|
|
if (rule.execute())//this should never fail since ready() returned 1
|
|
rule.stillborn = IsRoundAboutToEnd()
|
|
executed_rules += rule
|
|
if (rule.persistent)
|
|
current_rules += rule
|
|
else
|
|
message_admins("DYNAMIC MODE: ....except not because whomever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
|
|
log_admin("DYNAMIC MODE: ....except not because whomever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
|
|
return 1
|
|
|
|
|
|
/datum/gamemode/dynamic/proc/picking_latejoin_rule(var/list/drafted_rules = list())
|
|
var/datum/dynamic_ruleset/latejoin/latejoin_rule = pickweight(drafted_rules)
|
|
if (latejoin_rule)
|
|
if (!latejoin_rule.repeatable)
|
|
latejoin_rules = remove_rule(latejoin_rules,latejoin_rule.type)
|
|
spend_midround_threat(latejoin_rule.cost)
|
|
threat_log += "[worldtime2text()]: Latejoin [latejoin_rule.name] spent [latejoin_rule.cost] (midround budget)"
|
|
dynamic_stats.measure_threat(threat)
|
|
if (latejoin_rule.execute())//this should never fail since ready() returned 1
|
|
latejoin_rule.stillborn = IsRoundAboutToEnd()
|
|
var/mob/M = pick(latejoin_rule.assigned)
|
|
message_admins("DYNAMIC MODE: [key_name(M)] joined the station, and was selected by the <font size='3'>[latejoin_rule.name]</font> ruleset.")
|
|
log_admin("DYNAMIC MODE: [key_name(M)] joined the station, and was selected by the [latejoin_rule.name] ruleset.")
|
|
executed_rules += latejoin_rule
|
|
dynamic_stats.successful_injection(latejoin_rule)
|
|
if (latejoin_rule.persistent)
|
|
current_rules += latejoin_rule
|
|
. = TRUE
|
|
for (var/datum/dynamic_ruleset/latejoin/non_executed in drafted_rules)
|
|
non_executed.assigned.Cut()
|
|
|
|
|
|
/datum/gamemode/dynamic/proc/picking_midround_rule(var/list/drafted_rules = list())
|
|
var/datum/dynamic_ruleset/midround/midround_rule = pickweight(drafted_rules)
|
|
if (midround_rule)
|
|
if (!midround_rule.repeatable)
|
|
midround_rules = remove_rule(midround_rules,midround_rule.type)
|
|
spend_midround_threat(midround_rule.cost)
|
|
threat_log += "[worldtime2text()]: Midround [midround_rule.name] spent [midround_rule.cost] (midround budget)"
|
|
dynamic_stats.measure_threat(threat)
|
|
if (midround_rule.execute())//this should never fail since ready() returned 1
|
|
midround_rule.stillborn = IsRoundAboutToEnd()
|
|
message_admins("DYNAMIC MODE: Injecting some threats...<font size='3'>[midround_rule.name]</font>!")
|
|
log_admin("DYNAMIC MODE: Injecting some threats...[midround_rule.name]!")
|
|
dynamic_stats.successful_injection(midround_rule)
|
|
executed_rules += midround_rule
|
|
if (midround_rule.persistent)
|
|
current_rules += midround_rule
|
|
return 1
|
|
return 0
|
|
|
|
/datum/gamemode/dynamic/proc/picking_specific_rule(var/ruletype,var/forced=0,var/caller)//an experimental proc to allow admins to call rules on the fly or have rules call other rules
|
|
var/datum/dynamic_ruleset/midround/new_rule
|
|
if(ispath(ruletype))
|
|
new_rule = new ruletype()//you should only use it to call midround rules though.
|
|
else if(istype(ruletype,/datum/dynamic_ruleset))
|
|
new_rule = ruletype
|
|
else
|
|
message_admins("DYNAMIC MODE: The specific ruleset failed beacuse a type other than a path or rule was sent.")
|
|
log_admin("DYNAMIC MODE: The specific ruleset failed beacuse a type other than a path or rule was sent.")
|
|
return
|
|
if(caller)
|
|
new_rule.calledBy = caller
|
|
update_playercounts()
|
|
var/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
|
|
current_players[CURRENT_LIVING_PLAYERS] = living_players.Copy()
|
|
current_players[CURRENT_LIVING_ANTAGS] = living_antags.Copy()
|
|
current_players[CURRENT_DEAD_PLAYERS] = dead_players.Copy()
|
|
current_players[CURRENT_OBSERVERS] = list_observers.Copy()
|
|
if (new_rule && (forced || (new_rule.acceptable(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)
|
|
new_rule.choose_candidates()
|
|
threat_log += "[worldtime2text()]: Forced rule [new_rule.name] spent [new_rule.cost]"
|
|
dynamic_stats.measure_threat(threat)
|
|
if (new_rule.execute())//this should never fail since ready() returned 1
|
|
new_rule.stillborn = IsRoundAboutToEnd()
|
|
message_admins("Making a call to a specific ruleset...<font size='3'>[new_rule.name]</font>!")
|
|
log_admin("Making a call to a specific ruleset...[new_rule.name]!")
|
|
executed_rules += new_rule
|
|
dynamic_stats.successful_injection(new_rule)
|
|
if (new_rule.persistent)
|
|
current_rules += new_rule
|
|
return 1
|
|
else if (forced)
|
|
message_admins("DYNAMIC MODE: The ruleset couldn't be executed due to lack of eligible players.")
|
|
log_admin("DYNAMIC MODE: The ruleset couldn't be executed due to lack of eligible players.")
|
|
return 0
|
|
|
|
/datum/gamemode/dynamic/process()
|
|
. = ..() // Making the factions & roles process.
|
|
|
|
if (pop_last_updated < world.time - (60 SECONDS))
|
|
pop_last_updated = world.time
|
|
update_playercounts()
|
|
dynamic_stats.update_population(src)
|
|
|
|
if (latejoin_injection_cooldown)
|
|
latejoin_injection_cooldown--
|
|
|
|
for (var/datum/dynamic_ruleset/rule in current_rules)
|
|
rule.process()
|
|
|
|
if (midround_injection_cooldown)
|
|
midround_injection_cooldown--
|
|
else
|
|
if (forced_extended)
|
|
return
|
|
//time to inject some threat into the round
|
|
if(emergency_shuttle.departed)//unless the shuttle is gone
|
|
return
|
|
|
|
message_admins("DYNAMIC MODE: Checking state of the round.")
|
|
log_admin("DYNAMIC MODE: Checking state of the round.")
|
|
|
|
update_playercounts()
|
|
|
|
if (injection_attempt())
|
|
var/midround_injection_cooldown_middle = 0.5*(MIDROUND_DELAY_MAX + MIDROUND_DELAY_MIN)
|
|
midround_injection_cooldown = round(clamp(exp_distribution(midround_injection_cooldown_middle), MIDROUND_DELAY_MIN, MIDROUND_DELAY_MAX))
|
|
var/list/drafted_rules = list()
|
|
var/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
|
|
current_players[CURRENT_LIVING_PLAYERS] = living_players.Copy()
|
|
current_players[CURRENT_LIVING_ANTAGS] = living_antags.Copy()
|
|
current_players[CURRENT_DEAD_PLAYERS] = dead_players.Copy()
|
|
current_players[CURRENT_OBSERVERS] = list_observers.Copy()
|
|
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
|
|
if (rule.acceptable(living_players.len,midround_threat_level) && midround_threat >= rule.cost)
|
|
// Classic secret : only autotraitor/minor roles
|
|
if (classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
|
message_admins("[rule] was refused because we're on classic secret mode.")
|
|
continue
|
|
// No stacking : only one round-enter, unless > stacking_limit threat.
|
|
if (midround_threat < stacking_limit && no_stacking)
|
|
var/skip_ruleset = 0
|
|
for (var/datum/dynamic_ruleset/DR in executed_rules)
|
|
if ((DR.flags & HIGHLANDER_RULESET) && (rule.flags & HIGHLANDER_RULESET))
|
|
skip_ruleset = 1
|
|
break
|
|
if (skip_ruleset)
|
|
message_admins("[rule] was refused because we already have a round-ender ruleset.")
|
|
continue
|
|
rule.candidates = list()
|
|
rule.candidates = current_players.Copy()
|
|
rule.trim_candidates()
|
|
if (rule.ready())
|
|
rule.choose_candidates()
|
|
drafted_rules[rule] = rule.get_weight()
|
|
|
|
if (drafted_rules.len > 0)
|
|
message_admins("DYNAMIC MODE: [drafted_rules.len] eligible rulesets.")
|
|
log_admin("DYNAMIC MODE: [drafted_rules.len] eligible rulesets.")
|
|
picking_midround_rule(drafted_rules)
|
|
else
|
|
message_admins("DYNAMIC MODE: Couldn't ready-up a single ruleset. Lack of eligible candidates, population, or threat.")
|
|
log_admin("DYNAMIC MODE: Couldn't ready-up a single ruleset. Lack of eligible candidates, population, or threat.")
|
|
else
|
|
midround_injection_cooldown = rand(600,1050)
|
|
|
|
|
|
/datum/gamemode/dynamic/proc/update_playercounts()
|
|
living_players = list()
|
|
living_antags = list()
|
|
dead_players = list()
|
|
list_observers = list()
|
|
for (var/mob/M in player_list)
|
|
if (!M.client)
|
|
continue
|
|
if (istype(M,/mob/new_player))
|
|
continue
|
|
if (M.stat != DEAD)
|
|
living_players.Add(M)
|
|
if (M.mind && (M.mind.antag_roles.len > 0))
|
|
living_antags.Add(M)
|
|
else
|
|
if (istype(M,/mob/dead/observer))
|
|
var/mob/dead/observer/O = M
|
|
if (O.started_as_observer)//Observers
|
|
list_observers.Add(M)
|
|
continue
|
|
if (O.mind && O.mind.current && O.mind.current.ajourn)//Cultists
|
|
living_players.Add(M)//yes we're adding a ghost to "living_players", so make sure to properly check for type when testing midround rules
|
|
continue
|
|
dead_players.Add(M)//Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
|
|
|
|
if(living_players.len) //if anybody is around and alive in the current round
|
|
last_time_of_population = world.time
|
|
else if(last_time_of_population && world.time - last_time_of_population > 5 MINUTES && world.time > 15 MINUTES) //if enough time has passed without it
|
|
ticker.station_nolife_cinematic()
|
|
|
|
/datum/gamemode/dynamic/proc/GetInjectionChance()
|
|
var/chance = 0
|
|
//if the high pop override is in effect, we reduce the impact of population on the antag injection chance
|
|
var/high_pop_factor = (player_list.len >= high_pop_limit)
|
|
var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(living_players.len/(high_pop_factor ? 10 : 5)))//https://docs.google.com/spreadsheets/d/1QLN_OBHqeL4cm9zTLEtxlnaJHHUu0IUPzPbsI-DFFmc/edit#gid=2053826290
|
|
if (!living_antags.len)
|
|
chance += 50//no antags at all? let's boost those odds!
|
|
else
|
|
var/current_pop_per_antag = living_players.len / living_antags.len
|
|
if (current_pop_per_antag > max_pop_per_antag)
|
|
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
|
|
else
|
|
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
|
|
if (dead_players.len > living_players.len)
|
|
chance -= 30//more than half the crew died? ew, let's calm down on antags
|
|
if (threat > 70)
|
|
chance += 15
|
|
if (threat < 30)
|
|
chance -= 15
|
|
return round(max(0,chance))
|
|
|
|
/datum/gamemode/dynamic/proc/injection_attempt()//will need to gather stats to refine those values later
|
|
var/chance = GetInjectionChance()
|
|
message_admins("DYNAMIC MODE: Chance of injection with the current player numbers and threat level is...[chance]%.")
|
|
log_admin("DYNAMIC MODE: Chance of injection with the current player numbers and threat level is...[chance]%.")
|
|
if (prob(chance))
|
|
message_admins("DYNAMIC MODE: Check passed! Looking for a valid ruleset to execute.")
|
|
log_admin("DYNAMIC MODE: Check passed! Looking for a valid ruleset to execute.")
|
|
return 1
|
|
message_admins("DYNAMIC MODE: Check failed!")
|
|
log_admin("DYNAMIC MODE: Check failed!")
|
|
return 0
|
|
|
|
/datum/gamemode/dynamic/proc/remove_rule(var/list/rule_list,var/rule_type)
|
|
for(var/datum/dynamic_ruleset/DR in rule_list)
|
|
if(istype(DR,rule_type))
|
|
rule_list -= DR
|
|
return rule_list
|
|
|
|
/datum/gamemode/dynamic/latespawn(var/mob/living/newPlayer)
|
|
if (forced_extended)
|
|
return
|
|
if(emergency_shuttle.departed)//no more rules after the shuttle has left
|
|
return
|
|
|
|
update_playercounts()
|
|
|
|
if (forced_latejoin_rule)
|
|
forced_latejoin_rule.candidates = list(newPlayer)
|
|
forced_latejoin_rule.trim_candidates()
|
|
message_admins("Forcing ruleset [forced_latejoin_rule]")
|
|
if (forced_latejoin_rule.ready(1))
|
|
if (forced_latejoin_rule.choose_candidates())
|
|
picking_latejoin_rule(list(forced_latejoin_rule))
|
|
forced_latejoin_rule = null
|
|
|
|
else if (!latejoin_injection_cooldown && injection_attempt())
|
|
var/list/drafted_rules = list()
|
|
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
|
|
if (rule.acceptable(living_players.len,midround_threat_level) && midround_threat >= rule.cost)
|
|
// Classic secret : only autotraitor/minor roles
|
|
if (classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
|
message_admins("[rule] was refused because we're on classic secret mode.")
|
|
continue
|
|
// No stacking : only one round-enter, unless > stacking_limit threat.
|
|
if (midround_threat < stacking_limit && no_stacking)
|
|
var/skip_ruleset = 0
|
|
for (var/datum/dynamic_ruleset/DR in executed_rules)
|
|
if ((DR.flags & HIGHLANDER_RULESET) && (rule.flags & HIGHLANDER_RULESET))
|
|
skip_ruleset = 1
|
|
break
|
|
if (skip_ruleset)
|
|
message_admins("[rule] was refused because we already have a round-ender ruleset.")
|
|
continue
|
|
rule.candidates = list(newPlayer)
|
|
rule.trim_candidates()
|
|
if (rule.ready())
|
|
if (rule.choose_candidates())
|
|
drafted_rules[rule] = rule.get_weight()
|
|
|
|
if (drafted_rules.len > 0 && picking_latejoin_rule(drafted_rules))
|
|
var/latejoin_injection_cooldown_middle = 0.5*(LATEJOIN_DELAY_MAX + LATEJOIN_DELAY_MIN)
|
|
latejoin_injection_cooldown = round(clamp(exp_distribution(latejoin_injection_cooldown_middle), LATEJOIN_DELAY_MIN, LATEJOIN_DELAY_MAX))
|
|
|
|
/datum/gamemode/dynamic/mob_destroyed(var/mob/M)
|
|
for (var/datum/dynamic_ruleset/DR in midround_rules)
|
|
DR.applicants -= M
|
|
|
|
//Regenerate threat, but no more than our original threat level.
|
|
/datum/gamemode/dynamic/proc/refund_threat(var/regain)
|
|
threat = min(threat_level,threat+regain)
|
|
|
|
//Generate threat and increase the threat_level if it goes beyond, capped at 100
|
|
/datum/gamemode/dynamic/proc/create_threat(var/gain)
|
|
threat = min(100, threat+gain)
|
|
if(threat>threat_level)
|
|
threat_level = threat
|
|
|
|
//Expend threat, but do not fall below 0.
|
|
/datum/gamemode/dynamic/proc/spend_threat(var/cost)
|
|
threat = max(threat-cost,0)
|
|
|
|
// Same as above, but for midround
|
|
/datum/gamemode/dynamic/proc/refund_midround_threat(var/regain)
|
|
threat = min(midround_threat_level,midround_threat+regain)
|
|
|
|
/datum/gamemode/dynamic/proc/create_midround_threat(var/gain)
|
|
midround_threat = min(100, midround_threat+gain)
|
|
if(midround_threat>midround_threat_level)
|
|
midround_threat_level = midround_threat
|
|
|
|
/datum/gamemode/dynamic/proc/spend_midround_threat(var/cost)
|
|
midround_threat = max(midround_threat-cost,0)
|
|
|
|
// -- For the purpose of testing & simulation.
|
|
/datum/gamemode/dynamic/proc/simulate_roundstart(var/mob/user = usr)
|
|
// Picking part
|
|
var/done = 0
|
|
var/list/rules_to_simulate = list()
|
|
var/list/choices = list()
|
|
for (var/datum/dynamic_ruleset/roundstart/DR in roundstart_rules)
|
|
choices[DR.name] = DR
|
|
choices["None"] = null
|
|
while (!done)
|
|
var/choice = input(user, "Which rule to you want to add to the simulated list? It has currently [rules_to_simulate.len] items.", "Midround rules to simulate") as null|anything in choices
|
|
if (!choice || choice == "None")
|
|
done = 1
|
|
var/datum/dynamic_ruleset/to_test = choices[choice]
|
|
if (threat < stacking_limit && no_stacking)
|
|
var/skip_ruleset = 0
|
|
for (var/datum/dynamic_ruleset/roundstart/DR in rules_to_simulate)
|
|
if ((DR.flags & HIGHLANDER_RULESET) && (to_test.flags & HIGHLANDER_RULESET))
|
|
skip_ruleset = 1
|
|
message_admins("Skipping ruleset")
|
|
break
|
|
if (skip_ruleset)
|
|
message_admins("The rule was not added, because we already have a round-ender.")
|
|
else
|
|
message_admins("The rule was accepted.")
|
|
rules_to_simulate += to_test
|
|
else
|
|
message_admins("The rule was accepted (no-stacking not active.)")
|
|
rules_to_simulate += to_test
|
|
|
|
/datum/gamemode/dynamic/proc/simulate_midround_injection(var/mob/user = usr)
|
|
// Picking part
|
|
var/done = 0
|
|
var/list/rules_to_simulate = list()
|
|
var/list/choices_a = list()
|
|
for (var/datum/dynamic_ruleset/DR in midround_rules + roundstart_rules)
|
|
choices_a[DR.name] = DR
|
|
choices_a["None"] = null
|
|
while (!done)
|
|
var/choice = input(user, "Which rule to you want to add to the simulated list? It has currently [rules_to_simulate.len] items.", "Midround rules to simulate") as null|anything in choices_a
|
|
if (!choice || choice == "None")
|
|
done = 1
|
|
else
|
|
rules_to_simulate += choices_a[choice]
|
|
|
|
var/list/choices_b = list()
|
|
for (var/datum/dynamic_ruleset/midround/DR in midround_rules)
|
|
choices_b[DR.name] = DR
|
|
choices_b["None"] = null
|
|
|
|
var/name_to_test = input(user, "What rule to you want to test?", "Midround rule to test") as null|anything in choices_b
|
|
if (!name_to_test || name_to_test == "None")
|
|
return
|
|
|
|
var/datum/dynamic_ruleset/midround/to_test = choices_b[name_to_test]
|
|
|
|
// Concrete testing
|
|
|
|
if (classic_secret && !((to_test.flags & TRAITOR_RULESET) || (to_test.flags & MINOR_RULESET)))
|
|
message_admins("[to_test] was refused because we're on classic secret mode.")
|
|
return
|
|
// No stacking : only one round-enter, unless > stacking_limit threat.
|
|
if (threat < stacking_limit && no_stacking)
|
|
var/skip_ruleset = 0
|
|
for (var/datum/dynamic_ruleset/DR in rules_to_simulate)
|
|
if ((DR.flags & HIGHLANDER_RULESET) && (to_test.flags & HIGHLANDER_RULESET))
|
|
skip_ruleset = 1
|
|
if (skip_ruleset)
|
|
message_admins("[to_test] was refused because we already have a round-ender ruleset.")
|
|
return
|
|
|
|
message_admins("The rule was accepted.")
|
|
|
|
/datum/gamemode/dynamic/proc/simulate_latejoin_injection(var/mob/user = usr)
|
|
// Picking part
|
|
var/done = 0
|
|
var/list/rules_to_simulate = list()
|
|
var/list/choices_a = list()
|
|
for (var/datum/dynamic_ruleset/DR in midround_rules + roundstart_rules)
|
|
choices_a[DR.name] = DR
|
|
choices_a["None"] = null
|
|
while (!done)
|
|
var/choice = input(user, "Which rule to you want to add to the simulated list? It has currently [rules_to_simulate.len] items.", "Midround rules to simulate") as null|anything in choices_a
|
|
if (!choice || choice == "None")
|
|
done = 1
|
|
else
|
|
rules_to_simulate += choices_a[choice]
|
|
|
|
var/list/choices_b = list()
|
|
for (var/datum/dynamic_ruleset/latejoin/DR in latejoin_rules)
|
|
choices_b[DR.name] = DR
|
|
choices_b["None"] = null
|
|
|
|
var/name_to_test = input(user, "What rule to you want to test?", "Midround rule to test") as null|anything in choices_b
|
|
if (!name_to_test || name_to_test == "None")
|
|
return
|
|
|
|
var/datum/dynamic_ruleset/latejoin/to_test = choices_b[name_to_test]
|
|
|
|
// Concrete testing
|
|
|
|
if (classic_secret && !((to_test.flags & TRAITOR_RULESET) || (to_test.flags & MINOR_RULESET)))
|
|
message_admins("[to_test] was refused because we're on classic secret mode.")
|
|
return
|
|
// No stacking : only one round-enter, unless > stacking_limit threat.
|
|
if (threat < stacking_limit && no_stacking)
|
|
var/skip_ruleset = 0
|
|
for (var/datum/dynamic_ruleset/DR in rules_to_simulate)
|
|
if ((DR.flags & HIGHLANDER_RULESET) && (to_test.flags & HIGHLANDER_RULESET))
|
|
skip_ruleset = 1
|
|
break
|
|
if (skip_ruleset)
|
|
message_admins("[to_test] was refused because we already have a round-ender ruleset.")
|
|
return
|
|
|
|
message_admins("The rule was accepted.")
|
|
|
|
/datum/gamemode/dynamic/proc/update_stillborn_rulesets()
|
|
for (var/datum/dynamic_ruleset/ruleset in executed_rules)
|
|
ruleset.stillborn = IsRoundAboutToEnd()
|