diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index b899d7f6ab..27f1a81fef 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -320,6 +320,8 @@ parts += "[FOURSPACES][FOURSPACES][str]" for(var/entry in mode.threat_tallies) parts += "[FOURSPACES][FOURSPACES][entry] added [mode.threat_tallies[entry]]" + SSblackbox.record_feedback("tally","dynamic_threat",mode.threat_level,"Final threat level") + SSblackbox.record_feedback("tally","dynamic_threat",mode.threat,"Threat left") return parts.Join("
") /client/proc/roundend_report_file() diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 67ea855466..075184046d 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -300,6 +300,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) /datum/game_mode/dynamic/proc/log_threat(var/log_str,var/verbose = FALSE) threat_log_verbose += ("[worldtime2text()]: "+log_str) + SSblackbox.record_feedback("tally","dynamic_threat_log",1,log_str) if(!verbose) threat_log += log_str @@ -329,6 +330,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100 threat = threat_level + SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level") + SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre") + SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width") + SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful") /datum/game_mode/dynamic/can_start() message_admins("Dynamic mode parameters for the round:") @@ -340,6 +345,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if(GLOB.dynamic_forced_threat_level >= 0) threat_level = round(GLOB.dynamic_forced_threat_level, 0.1) threat = threat_level + SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)") else generate_threat() @@ -384,7 +390,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if (roundstart_rules.len <= 0) log_game("DYNAMIC: [roundstart_rules.len] rules.") return TRUE - + SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered") + SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up") if(GLOB.dynamic_forced_roundstart_ruleset.len > 0) rigged_roundstart() else @@ -537,6 +544,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if(rule.execute()) if(rule.persistent) current_rules += rule + SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info()) return TRUE rule.clean_up() // Refund threat, delete teams and so on. executed_rules -= rule @@ -615,6 +623,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) else if(new_rule.flags & ONLY_RULESET) only_ruleset_executed = TRUE log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!") + SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info()) executed_rules += new_rule if (new_rule.persistent) current_rules += new_rule @@ -639,6 +648,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) 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 + SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info()) rule.candidates.Cut() if (rule.persistent) current_rules += rule @@ -655,6 +665,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) for (var/datum/dynamic_ruleset/rule in current_rules) if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing. current_rules -= rule + SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished") + SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info()) if (midround_injection_cooldown < world.time) if (GLOB.dynamic_forced_extended) @@ -673,6 +685,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) update_playercounts() if (get_injection_chance()) + SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections") var/cur_threat_frac = threat/threat_level var/list/drafted_rules = list() var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len @@ -688,12 +701,16 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac)) else drafted_rules[rule] = rule.get_weight() + else if(threat < rule.cost) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") if (drafted_rules.len > 0) + SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections") picking_midround_latejoin_rule(drafted_rules) else midround_injection_cooldown = (midround_injection_cooldown + world.time)/2 if(event_injection_cooldown < world.time) + SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections") var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time) message_admins("DYNAMIC: Doing event injection.") @@ -704,7 +721,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) if(rule.ready()) drafted_rules[rule] = rule.get_weight() + else if(threat < rule.cost) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") if(drafted_rules.len > 0) + SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections") picking_midround_latejoin_rule(drafted_rules) /// Updates current_players. @@ -795,6 +815,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) forced_latejoin_rule = null else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance())) + SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections") var/list/drafted_rules = list() for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules) if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) @@ -812,12 +833,14 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) drafted_rules[rule] = rule.get_weight() if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules)) + SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections") var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time /// Refund threat, but no more than threat_level. /datum/game_mode/dynamic/proc/refund_threat(regain) threat = min(threat_level,threat+regain) + SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat") log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE) /// Generate threat and increase the threat_level if it goes beyond, capped at 100 @@ -825,11 +848,13 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) threat = min(100, threat+gain) if(threat > threat_level) threat_level = threat + SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat") log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE) /// Expend threat, can't fall under 0. /datum/game_mode/dynamic/proc/spend_threat(cost) threat = max(threat-cost,0) + SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent") log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE) /// Turns the value generated by lorentz distribution to threat value between 0 and 100. diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index ddb26010b6..7f6ea088a1 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -125,12 +125,15 @@ /// 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) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to low pop") return FALSE if(maximum_players > 0 && population > maximum_players) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to high pop") return FALSE if (population >= GLOB.dynamic_high_pop_limit) indice_pop = 10 if(threat_level < high_population_requirement) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat level") log_game("DYNAMIC: [name] did not reach threat level threshold: [threat_level]/[high_population_requirement]") return FALSE else @@ -142,6 +145,7 @@ 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) if(threat_level < requirements[indice_pop]) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat level") log_game("DYNAMIC: [name] did not reach threat level threshold: [threat_level]/[requirements[indice_pop]]") return FALSE else @@ -188,6 +192,7 @@ /// 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) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough candidates") return FALSE return TRUE @@ -222,6 +227,24 @@ /datum/dynamic_ruleset/proc/check_finished() return FALSE +/// Returns a list to be displayed on statbus. +/datum/dynamic_ruleset/proc/get_blackbox_info() + var/list/ruleset_data = list() + ruleset_data["name"] = name + ruleset_data["rule_type"] = ruletype + ruleset_data["cost"] = total_cost + ruleset_data["weight"] = weight + ruleset_data["scaled_times"] = scaled_times + ruleset_data["antagonist_type"] = antag_datum + ruleset_data["population_tier"] = indice_pop + ruleset_data["assigned"] = list() + for (var/datum/mind/M in assigned) + var/assigned_data = list() + assigned_data["key"] = M.key + assigned_data["name"] = M.name + ruleset_data["assigned"] += list(assigned_data) + return ruleset_data + ////////////////////////////////////////////// // // // ROUNDSTART RULESETS // diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm index a1483f3477..b6cd313bfc 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm @@ -3,6 +3,17 @@ var/typepath // typepath of the event var/triggering +/datum/dynamic_ruleset/event/get_blackbox_info() + var/list/ruleset_data = list() + ruleset_data["name"] = name + ruleset_data["rule_type"] = ruletype + ruleset_data["cost"] = total_cost + ruleset_data["weight"] = weight + ruleset_data["scaled_times"] = scaled_times + ruleset_data["event_type"] = typepath + ruleset_data["population_tier"] = indice_pop + return ruleset_data + /datum/dynamic_ruleset/event/execute() var/datum/round_event/E = new typepath() E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) @@ -26,6 +37,7 @@ var/threat = round(mode.threat_level/10) if (job_check < required_enemies[threat]) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles") return FALSE return TRUE @@ -147,12 +159,15 @@ /datum/dynamic_ruleset/event/meteor_wave/ready() if(mode.threat_level > 40 && mode.threat >= 25 && prob(20)) + name = "Meteor Wave: Threatening" cost = 25 typepath = /datum/round_event/meteor_wave/threatening else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30)) + name = "Meteor Wave: Catastrophic" cost = 40 typepath = /datum/round_event/meteor_wave/catastrophic else + name = "Meteor Wave: Normal" cost = 15 typepath = /datum/round_event/meteor_wave return ..() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 38efc0e7d0..fe9347d9d5 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -36,9 +36,9 @@ continue // Dead players cannot count as opponents if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles))) job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it - var/threat = round(mode.threat_level/10) if (job_check < required_enemies[threat]) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles") return FALSE return ..() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 8543f452f1..91ac73d142 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -87,6 +87,7 @@ var/threat = round(mode.threat_level/10) if (job_check < required_enemies[threat]) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles") return FALSE return TRUE @@ -292,6 +293,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE if(GLOB.wizardstart.len == 0) log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") @@ -354,6 +356,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE return ..() @@ -390,6 +393,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE return ..() @@ -421,6 +425,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE return ..() @@ -520,6 +525,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE return ..() @@ -558,6 +564,7 @@ if(deadMobs < REVENANT_SPAWN_THRESHOLD) return FALSE if(required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies var/turf/T = get_turf(L) @@ -605,6 +612,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/slaughter_demon/ready(forced = FALSE) if(required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list) if(isturf(L.loc)) @@ -656,6 +664,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/abductors/ready(forced = FALSE) if(required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE team = new /datum/team/abductor_team if(team.team_number > ABDUCTOR_MAX_TEAMS) @@ -695,6 +704,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/ninja/ready(forced = FALSE) if(required_candidates > (dead_players.len + list_observers.len)) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE if(!spawn_loc) var/list/spawn_locs = list()