diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 1c9dc46e73..068ef2c1c0 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -224,8 +224,6 @@ SUBSYSTEM_DEF(vote) GLOB.master_mode = "dynamic" var/datum/dynamic_storyteller/S = config.pick_storyteller(.) GLOB.dynamic_storyteller_type = S - GLOB.dynamic_curve_centre = initial(S.curve_centre) - GLOB.dynamic_curve_width = initial(S.curve_width) if("map") var/datum/map_config/VM = config.maplist[.] message_admins("The map has been voted for and will change to: [VM.map_name]") diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 82a32a537d..ae34669276 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -181,7 +181,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) dat += "[DR.ruletype] - [DR.name]
" else dat += "none.
" - dat += "
Injection Timers: ([get_injection_chance(TRUE)]% chance)
" + dat += "
Injection Timers: ([storyteller.get_injection_chance(TRUE)]% chance)
" dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] \[Now!\]
" dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] \[Now!\]
" dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] \[Now!\]
" @@ -339,6 +339,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful") /datum/game_mode/dynamic/can_start() + storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens + storyteller.on_start() + SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name) 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].") @@ -348,19 +351,12 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) 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)") + SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced)") else generate_threat() - var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min) - latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time + storyteller.start_injection_cooldowns() - var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max) - midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time - - 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) - log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!") initial_threat_level = threat_level return TRUE @@ -382,9 +378,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) if("Event") if(ruleset.weight) events += ruleset - storyteller = new GLOB.dynamic_storyteller_type - storyteller.on_start() - SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name) for(var/mob/dead/new_player/player in GLOB.player_list) if(player.ready == PLAYER_READY_TO_PLAY && player.mind) roundstart_pop_ready++ @@ -675,8 +668,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) // 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) + midround_injection_cooldown = storyteller.get_midround_cooldown() + world.time // Time to inject some threat into the round if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone @@ -685,19 +677,17 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) log_game("DYNAMIC: Checking for midround injection.") update_playercounts() - if (get_injection_chance()) + if (prob(storyteller.get_injection_chance())) SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections") var/list/drafted_rules = storyteller.midround_draft() 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 + // get_injection_chance can do things on fail 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) + event_injection_cooldown = storyteller.get_event_cooldown() + world.time message_admins("DYNAMIC: Doing event injection.") log_game("DYNAMIC: Doing event injection.") update_playercounts() @@ -727,31 +717,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) continue current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow) -/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection. -/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE) - if(forced_injection) - forced_injection = !dry_run - return 100 - 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 = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit) - var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5))) - if (!current_players[CURRENT_LIVING_ANTAGS].len) - chance += 80 // No antags at all? let's boost those odds! - else - var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_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 (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_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)) - /// Removes type from the list /datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) for(var/I in type_list) @@ -793,13 +758,12 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null) picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE) forced_latejoin_rule = null - else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance())) + else if (latejoin_injection_cooldown < world.time && prob(storyteller.get_injection_chance())) SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections") var/list/drafted_rules = storyteller.latejoin_draft(newPlayer) 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 + latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time /// Refund threat, but no more than threat_level. /datum/game_mode/dynamic/proc/refund_threat(regain) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index edc503b34e..54fbc75fe3 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -673,7 +673,7 @@ blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear) high_population_requirement = 15 var/datum/team/abductor_team/team - property_weights = list("story_potential" = 10, "extended" = 1, "valid" = 1, "trust" = -3) + property_weights = list("story_potential" = 10, "extended" = -2, "valid" = 1, "trust" = -3, "chaos" = 4) repeatable_weight_decrease = 4 repeatable = TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm index ea0b95a622..94bfc37020 100644 --- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm +++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm @@ -4,6 +4,7 @@ var/list/property_weights = list() var/curve_centre = 0 var/curve_width = 1.8 + var/forced_threat_level = -1 var/flags = 0 var/datum/game_mode/dynamic/mode = null @@ -22,6 +23,19 @@ Property weights are: ..() if (istype(SSticker.mode, /datum/game_mode/dynamic)) mode = SSticker.mode + GLOB.dynamic_curve_centre = curve_centre + GLOB.dynamic_curve_width = curve_width + GLOB.dynamic_forced_threat_level = forced_threat_level + +/datum/dynamic_storyteller/proc/start_injection_cooldowns() + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min) + mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time + + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max) + mode.midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time + + var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) + mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time) /datum/dynamic_storyteller/proc/do_process() return @@ -29,6 +43,42 @@ Property weights are: /datum/dynamic_storyteller/proc/on_start() return +/datum/dynamic_storyteller/proc/get_midround_cooldown() + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) + return round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + +/datum/dynamic_storyteller/proc/get_event_cooldown() + var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) + return round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + +/datum/dynamic_storyteller/proc/get_latejoin_cooldown() + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) + return round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + +/datum/dynamic_storyteller/proc/get_injection_chance(dry_run = FALSE) + if(mode.forced_injection) + mode.forced_injection = !dry_run + return 100 + 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 = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit) + var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5))) + if (!mode.current_players[CURRENT_LIVING_ANTAGS].len) + chance += 80 // No antags at all? let's boost those odds! + else + var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_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 (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len) + chance -= 30 // More than half the crew died? ew, let's calm down on antags + if (mode.threat > 70) + chance += 15 + if (mode.threat < 30) + chance -= 15 + return round(max(0,chance)) + /datum/dynamic_storyteller/proc/roundstart_draft() var/list/drafted_rules = list() for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules) @@ -106,6 +156,22 @@ Property weights are: curve_centre = 10 desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard." property_weights = list("extended" = -1, "chaos" = 10) + var/refund_cooldown + +/datum/dynamic_storyteller/cowabunga/get_midround_cooldown() + return ..() / 4 + +/datum/dynamic_storyteller/cowabunga/get_event_cooldown() + return ..() / 4 + +/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown() + return ..() / 4 + +/datum/dynamic_storyteller/cowabunga/do_process() + if(refund_cooldown < world.time) + mode.refund_threat(10) + mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].") + refund_cooldown = world.time + 300 SECONDS /datum/dynamic_storyteller/team name = "Teamwork" @@ -114,13 +180,16 @@ Property weights are: curve_width = 1.5 property_weights = list("valid" = 3, "trust" = 5) +/datum/dynamic_storyteller/team/get_injection_chance() + return (mode.current_players[CURRENT_LIVING_ANTAGS].len ? 0 : ..()) + /datum/dynamic_storyteller/classic name = "Classic" desc = "Chaos: medium. Variation: highest. Default dynamic." /datum/dynamic_storyteller/memes name = "Story" - desc = "Chaos: medium. Variation: high. Likely antags: abductors, nukies, wizard, traitor." + desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor." curve_width = 4 property_weights = list("story_potential" = 10, "extended" = 1) @@ -129,7 +198,7 @@ Property weights are: desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult." curve_centre = -2 curve_width = 4 - property_weights = list("trust" = -5) + property_weights = list("trust" = -5, "extended" = 3) /datum/dynamic_storyteller/liteextended name = "Calm" @@ -144,6 +213,6 @@ Property weights are: desc = "Chaos: none. Variation: none. Likely antags: none." curve_centre = -20 curve_width = 0.5 - + /datum/dynamic_storyteller/extended/on_start() GLOB.dynamic_forced_extended = TRUE