diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index 974a0c0e88..f1db37d9fa 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -371,19 +371,18 @@ for(var/T in storyteller_cache) var/datum/dynamic_storyteller/S = T var/config_tag = initial(S.config_tag) - var/final_weight = initial(S.weight) - if(probabilities[config_tag]<=0) + var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight) + if(probability <= 0) continue - final_weight = probabilities[config_tag] - if(SSpersistence.saved_storytellers.len == 3 && repeated_mode_adjust.len == 3) + if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len) var/name = initial(S.name) var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3) var/adjustment = 0 while(recent_round) adjustment += repeated_mode_adjust[recent_round] recent_round = SSpersistence.saved_modes.Find(name,recent_round+1,0) - final_weight *= ((100-adjustment)/100) - runnable_storytellers[S] = final_weight + probability *= ((100-adjustment)/100) + runnable_storytellers[S] = probability return runnable_storytellers diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index a42c08fc5a..ac0acfb61d 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -356,7 +356,7 @@ SUBSYSTEM_DEF(vote) var/list/runnable_storytellers = config.get_runnable_storytellers() for(var/T in runnable_storytellers) var/datum/dynamic_storyteller/S = T - runnable_storytellers[S] *= stored_gamemode_votes[initial(S.name)] + runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1) var/datum/dynamic_storyteller/S = pickweightAllowZero(runnable_storytellers) GLOB.dynamic_storyteller_type = S if("map") diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm index 23e94c4065..1ee226875b 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm @@ -2,6 +2,7 @@ ruletype = "Event" var/typepath // typepath of the event var/triggering + var/earliest_start = 20 MINUTES /datum/dynamic_ruleset/event/get_blackbox_info() var/list/ruleset_data = list() @@ -25,8 +26,10 @@ return E -/datum/dynamic_ruleset/event/ready(forced = FALSE) // same as midround cause we're still using enemy system +/datum/dynamic_ruleset/event/ready(forced = FALSE) if (!forced) + if(earliest_start >= world.time-SSticker.round_start_time) + return FALSE var/job_check = 0 if (enemy_roles.len > 0) for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS]) @@ -56,6 +59,7 @@ required_enemies = list(2,2,1,1,0,0,0,0,0,0) weight = 5 cost = 10 + earliest_start = 30 MINUTES blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear) requirements = list(70,60,50,50,40,40,40,30,20,15) property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1) @@ -157,17 +161,18 @@ required_enemies = list(3,3,3,3,3,3,3,3,3,3) cost = 15 weight = 3 + earliest_start = 25 MINUTES repeatable_weight_decrease = 2 requirements = list(60,50,40,30,30,30,30,30,30,30) high_population_requirement = 30 property_weights = list("extended" = -2) /datum/dynamic_ruleset/event/meteor_wave/ready() - if(mode.threat_level > 40 && mode.threat >= 25 && prob(20)) + if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30)) name = "Meteor Wave: Threatening" cost = 25 typepath = /datum/round_event/meteor_wave/threatening - else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30)) + else if(world.time-SSticker.round_start_time > 45 MINUTES && mode.threat_level > 50 && mode.threat >= 40 && prob(30)) name = "Meteor Wave: Catastrophic" cost = 40 typepath = /datum/round_event/meteor_wave/catastrophic @@ -280,6 +285,7 @@ cost = 4 requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 + earliest_start = 10 MINUTES repeatable = TRUE property_weights = list("extended" = 1) @@ -324,6 +330,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + earliest_start = 0 MINUTES property_weights = list("extended" = 1) always_max_weight = TRUE @@ -381,3 +388,67 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 property_weights = list("extended" = 1,"chaos" = 1) + +/datum/dynamic_ruleset/event/portal_storm_syndicate + name = "Portal Storm" + config_tag = "portal_storm" + typepath = /datum/round_event/portal_storm/syndicate_shocktroop + cost = 10 + weight = 1 + enemy_roles = list("Head of Security","Security Officer","AI","Captain","Shaft Miner") + required_enemies = list(2,2,2,2,2,2,2,2,2,2) + requirements = list(101,101,101,30,30,30,30,30,30,30) + high_population_requirement = 30 + earliest_start = 30 MINUTES + property_weights = list("teamwork" = 1,"chaos" = 1, "extended" = -1) + +/datum/dynamic_ruleset/event/wormholes + name = "Wormholes" + config_tag = "wormhole" + typepath = /datum/round_event/wormholes + cost = 3 + weight = 4 + enemy_roles = list("AI","Medical Doctor","Station Engineer","Head of Personnel","Captain") + required_enemies = list(2,2,2,2,2,2,2,2,2,2) + requirements = list(5,5,5,5,5,5,5,5,5,5) + high_population_requirement = 5 + property_weights = list("extended" = 1) + +/datum/dynamic_ruleset/event/swarmers + name = "Swarmers" + config_tag = "swarmer" + typepath = /datum/round_event/spawn_swarmer + cost = 10 + weight = 1 + earliest_start = 30 MINUTES + enemy_roles = list("AI","Security Officer","Head of Security","Captain","Station Engineer","Atmos Technician","Chief Engineer") + required_enemies = list(4,4,4,4,3,3,2,2,1,1) + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 5 + property_weights = list("extended" = -2) + +/datum/dynamic_ruleset/event/sentient_disease + name = "Sentient Disease" + config_tag = "sentient_disease" + typepath = /datum/round_event/ghost_role/sentient_disease + enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist") + required_enemies = list(2,1,1,1,0,0,0,0,0,0) + required_candidates = 1 + weight = 4 + cost = 5 + requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"! + property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2) + high_population_requirement = 5 + +/datum/dynamic_ruleset/event/revenant + name = "Revenant" + config_tag = "revenant" + typepath = /datum/round_event/ghost_role/revenant + enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI") + required_enemies = list(2,1,1,1,0,0,0,0,0,0) + required_candidates = 1 + weight = 4 + cost = 5 + requirements = list(30,30,30,30,20,15,15,15,15,15) + high_population_requirement = 15 + property_weights = list("story_potential" = -2, "extended" = -1) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 6d1933c0fd..a2639a7ece 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -523,88 +523,6 @@ log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.") return S -////////////////////////////////////////////// -// // -// SENTIENT DISEASE // -// // -////////////////////////////////////////////// - - -/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease - name = "Sentient Disease" - config_tag = "sentient_disease" - antag_flag = ROLE_ALIEN - enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist") - required_enemies = list(2,1,1,1,0,0,0,0,0,0) - required_candidates = 1 - weight = 4 - cost = 5 - requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"! - property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2) - high_population_requirement = 5 - -/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant) - var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center()) - applicant.transfer_ckey(virus, FALSE) - INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name) - message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by the midround ruleset.") - log_game("[key_name(virus)] was spawned as a sentient disease by the midround ruleset.") - return virus - -////////////////////////////////////////////// -// // -// REVENANT // -// // -////////////////////////////////////////////// - - -/datum/dynamic_ruleset/midround/from_ghosts/revenant - name = "Revenant" - config_tag = "revenant" - antag_flag = ROLE_REVENANT - enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI") - required_enemies = list(2,1,1,1,0,0,0,0,0,0) - required_candidates = 1 - weight = 4 - cost = 5 - requirements = list(30,30,30,30,20,15,15,15,15,15) - high_population_requirement = 15 - property_weights = list("story_potential" = -2, "extended" = -1) - var/list/spawn_locs = list() - -/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0) - var/deadMobs = 0 - for(var/mob/M in GLOB.dead_mob_list) - deadMobs++ - if(deadMobs < REVENANT_SPAWN_THRESHOLD) - return FALSE - return ..() - -/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE) - for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies - var/turf/T = get_turf(L) - if(T && is_station_level(T.z)) - spawn_locs += T - if(!spawn_locs.len || spawn_locs.len < 15) //look for any morgue trays, crematoriums, ect if there weren't alot of dead bodies on the station to pick from - for(var/obj/structure/bodycontainer/bc in GLOB.bodycontainers) - var/turf/T = get_turf(bc) - if(T && is_station_level(T.z)) - spawn_locs += T - if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns - for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list) - if(isturf(L.loc)) - spawn_locs += L.loc - if(!spawn_locs.len) //If we can't find THAT, then just give up and cry - return FALSE - return ..() - -/datum/dynamic_ruleset/midround/from_ghosts/revenant/generate_ruleset_body(mob/applicant) - var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs)) - applicant.transfer_ckey(revvie, FALSE) - message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by the midround ruleset.") - log_game("[key_name(revvie)] was spawned as a revenant by the midround ruleset.") - return revvie - ////////////////////////////////////////////// // // // SLAUGHTER DEMON // @@ -668,11 +586,11 @@ required_candidates = 2 weight = 8 cost = 10 - requirements = list(101,101,70,50,40,30,30,20,15,15) + requirements = list(101,101,70,50,40,30,30,30,30,30) 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" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2) + property_weights = list("extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2) repeatable_weight_decrease = 4 repeatable = TRUE @@ -742,7 +660,8 @@ var/datum/antagonist/ninja/ninjadatum = new ninjadatum.helping_station = pick(TRUE,FALSE) if(ninjadatum.helping_station) - mode.refund_threat(5) + mode.refund_threat(cost+5) + mode.log_threat("Ninja was helping station; [cost+5] cost refunded.") Mind.add_antag_datum(ninjadatum) if(Ninja.mind != Mind) //something has gone wrong! diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index abd40a1756..2527c83077 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -623,8 +623,7 @@ config_tag = "clownops" antag_datum = /datum/antagonist/nukeop/clownop antag_leader_datum = /datum/antagonist/nukeop/leader/clownop - requirements = list(101,101,101,101,101,101,101,101,101,101) - high_population_requirement = 101 + weight = 1 property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2) diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm index 069c73a421..dec7b921db 100644 --- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm +++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm @@ -1,16 +1,22 @@ /datum/dynamic_storyteller - var/name = "none" - var/config_tag = null - var/desc = "A coder's idiocy." - var/list/property_weights = list() - var/curve_centre = 0 - var/curve_width = 1.8 + var/name = "none" // Name for voting. + var/config_tag = null // Config tag for config weights. + var/desc = "A coder's idiocy." // Description for voting. + var/list/property_weights = list() // See below. + var/curve_centre = 0 // As GLOB.dynamic_curve_centre. + var/curve_width = 1.8 // As GLOB.dynamic_curve_width. var/forced_threat_level = -1 + /* + NO_ASSASSIN: Will not have permanent assassination targets. + WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level. + USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre. + */ var/flags = 0 - var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote - var/event_frequency_lower = 6 MINUTES - var/event_frequency_upper = 20 MINUTES - var/datum/game_mode/dynamic/mode = null + var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting. + var/event_frequency_lower = 6 MINUTES // How rare events will be, at least. + var/event_frequency_upper = 20 MINUTES // How rare events will be, at most. + var/pop_antag_ratio = 5 // How many non-antags there should be vs antags. + var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic. /** Property weights are: @@ -80,7 +86,7 @@ Property weights are: 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))) + var/max_pop_per_antag = max(pop_antag_ratio,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 @@ -173,12 +179,13 @@ Property weights are: name = "Chaotic" config_tag = "chaotic" curve_centre = 10 - desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard." + desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round." property_weights = list("extended" = -1, "chaos" = 10) weight = 1 event_frequency_lower = 2 MINUTES event_frequency_upper = 10 MINUTES flags = WAROPS_ALWAYS_ALLOWED + pop_antag_ratio = 4 var/refund_cooldown = 0 /datum/dynamic_storyteller/cowabunga/get_midround_cooldown() @@ -189,14 +196,14 @@ Property weights are: /datum/dynamic_storyteller/cowabunga/do_process() if(refund_cooldown < world.time) - mode.refund_threat(20) - mode.log_threat("Cowabunga it is. Refunded 20 threat. Threat is now [mode.threat].") - refund_cooldown = world.time + 600 SECONDS + mode.refund_threat(40) + mode.log_threat("Chaotic storyteller refunded 40 threat. Threat is now [mode.threat].") + refund_cooldown = world.time + 1200 SECONDS /datum/dynamic_storyteller/team name = "Teamwork" config_tag = "teamwork" - desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph." + desc = "Modes where the crew must band together. Nukies, xenos, blob. Only one antag threat at once." curve_centre = 2 curve_width = 1.5 weight = 2 @@ -209,56 +216,71 @@ Property weights are: /datum/dynamic_storyteller/conversion name = "Conversion" config_tag = "conversion" - desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs." + desc = "Conversion antags. Cults, revs." curve_centre = 3 curve_width = 1 - weight = 2 + weight = 0 flags = WAROPS_ALWAYS_ALLOWED property_weights = list("valid" = 1, "conversion" = 20) /datum/dynamic_storyteller/classic name = "Random" config_tag = "random" - desc = "Chaos: varies. Variation: highest. No special weights attached." - weight = 6 - flags = USE_PREF_WEIGHTS + desc = "No special weights attached. Anything goes." + weight = 4 curve_width = 4 + pop_antag_ratio = 7 + flags = USE_PREF_WEIGHTS /datum/dynamic_storyteller/memes name = "Story" config_tag = "story" - desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor." - weight = 4 - flags = USE_PREF_WEIGHTS - curve_width = 4 + desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies." + weight = 2 + curve_width = 2 + pop_antag_ratio = 7 property_weights = list("story_potential" = 10) /datum/dynamic_storyteller/suspicion name = "Intrigue" config_tag = "intrigue" - desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult." - weight = 4 - flags = USE_PREF_WEIGHTS - curve_width = 4 + desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers." + weight = 2 + curve_width = 2 + pop_antag_ratio = 7 property_weights = list("trust" = -5) /datum/dynamic_storyteller/liteextended name = "Calm" config_tag = "calm" - desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant." + desc = "Low-chaos round. Few antags. No conversion." + curve_centre = -3 + curve_width = 0.5 + flags = NO_ASSASSIN + weight = 1 + pop_antag_ratio = 10 + property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10) + +/datum/dynamic_storyteller/no_antag + name = "Extended" + config_tag = "semiextended" + desc = "No standard antags. Threatening events may still spawn." curve_centre = -5 curve_width = 0.5 flags = NO_ASSASSIN - weight = 2 - property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10) + weight = 1 + property_weights = list("extended" = 2) -/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE) - return ..()/2 +/datum/dynamic_storyteller/no_antag/roundstart_draft() + return list() + +/datum/dynamic_storyteller/no_antag/get_injection_chance(dry_run) + return 0 /datum/dynamic_storyteller/extended - name = "Extended" + name = "Super Extended" config_tag = "extended" - desc = "Chaos: none. Variation: none. Likely antags: none." + desc = "No antags. No dangerous events." curve_centre = -20 weight = 0 curve_width = 0.5 diff --git a/code/modules/antagonists/swarmer/swarmer_event.dm b/code/modules/antagonists/swarmer/swarmer_event.dm index 8e385f6674..6ef35fdbaa 100644 --- a/code/modules/antagonists/swarmer/swarmer_event.dm +++ b/code/modules/antagonists/swarmer/swarmer_event.dm @@ -5,6 +5,7 @@ max_occurrences = 0 earliest_start = 30 MINUTES min_players = 15 + gamemode_blacklist = list("dynamic") /datum/round_event/spawn_swarmer diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm index 5ef30d0030..457b5bd4ec 100644 --- a/code/modules/events/portal_storm.dm +++ b/code/modules/events/portal_storm.dm @@ -4,6 +4,7 @@ weight = 2 min_players = 15 earliest_start = 30 MINUTES + gamemode_blacklist = list("dynamic") /datum/round_event/portal_storm/syndicate_shocktroop boss_types = list(/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper = 2) diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 9dbe1443d1..b7f8b8f911 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -4,6 +4,7 @@ max_occurrences = 3 weight = 2 min_players = 2 + gamemode_blacklist = list("dynamic") /datum/round_event/wormholes