diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index cd9500284b..6566d41e21 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -60,7 +60,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) announce_text = "Dynamic mode!" // This needs to be changed maybe reroll_friendly = FALSE; - + // Current storyteller + var/datum/dynamic_storyteller/storyteller = null // Threat logging vars /// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations var/threat_level = 0 diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index 0c8ec0a2b8..e26eaa03c4 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -80,6 +80,8 @@ /// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin). /// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure. var/delay = 0 + /// List of tags for use in storytellers. + var/list/property_weights = list() /datum/dynamic_ruleset/New() ..() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm index 4ac8cc91d3..0c0dbee820 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm @@ -46,6 +46,7 @@ cost = 10 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) high_population_requirement = 15 /datum/dynamic_ruleset/event/pirates/ready(forced = FALSE) @@ -69,6 +70,7 @@ cost = 10 requirements = list(70,60,50,50,40,40,40,30,20,15) high_population_requirement = 15 + property_weights = list("chaos" = 1, "valid" = 1) ////////////////////////////////////////////// // // @@ -88,6 +90,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended high_population_requirement = 5 repeatable = TRUE + property_weights = list("chaos" = 1, "extended" = 2) /datum/dynamic_ruleset/event/ventclog/ready() if(mode.threat_level > 30 && mode.threat >= 5 && prob(20)) @@ -125,6 +128,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("story_potential" = 5, "extended" = 1) ////////////////////////////////////////////// // // @@ -143,6 +147,7 @@ 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)) @@ -174,6 +179,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_flux name = "Anomaly: Hyper-Energetic Flux" @@ -187,6 +193,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_gravitational name = "Anomaly: Gravitational" @@ -198,6 +205,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_pyroclastic name = "Anomaly: Pyroclastic" @@ -211,6 +219,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_vortex name = "Anomaly: Vortex" @@ -224,6 +233,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) ////////////////////////////////////////////// // // @@ -243,6 +253,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = -1, "chaos" = 1) /datum/dynamic_ruleset/event/carp_migration name = "Carp Migration" @@ -254,6 +265,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/communications_blackout name = "Communications Blackout" @@ -267,6 +279,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1, "chaos" = 1) /datum/dynamic_ruleset/event/processor_overload name = "Processor Overload" @@ -280,6 +293,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1, "chaos" = 1) /datum/dynamic_ruleset/event/space_dust name = "Minor Space Dust" @@ -293,6 +307,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/major_dust name = "Major Space Dust" @@ -306,6 +321,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/electrical_storm name = "Electrical Storm" @@ -319,6 +335,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/heart_attack name = "Random Heart Attack" @@ -332,6 +349,7 @@ requirements = list(101,101,101,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/radiation_storm name = "Radiation Storm" @@ -343,3 +361,4 @@ required_enemies = list(1,1,1,1,1,1,1,1,1,1) requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 + property_weights = list("extended" = 1,"chaos" = 1) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 5810fd0ae0..8d9d14b473 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -69,6 +69,8 @@ high_population_requirement = 15 repeatable = TRUE flags = TRAITOR_RULESET + property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1) + ////////////////////////////////////////////// // // @@ -93,6 +95,7 @@ requirements = list(101,101,70,40,40,40,40,40,40,40) high_population_requirement = 40 flags = HIGHLANDER_RULESET + property_weights = list("trust" = -3, "chaos" = 5, "extended" = -5, "valid" = 2) var/required_heads_of_staff = 3 var/finished = FALSE var/datum/team/revolution/revolution diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 8cda402ebf..1c077aec55 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -189,6 +189,7 @@ repeatable = TRUE high_population_requirement = 15 flags = TRAITOR_RULESET + property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1) /datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0) var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len @@ -245,6 +246,7 @@ requirements = list(101,101,70,50,50,50,40,30,30,30) high_population_requirement = 30 required_type = /mob/living/silicon/ai + property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 3) var/ion_announce = 33 var/removeDontImproveChance = 10 @@ -297,6 +299,7 @@ requirements = list(90,90,70,50,50,50,50,40,30,30) high_population_requirement = 30 repeatable = TRUE + property_weights = list("story_potential" = 5, "trust" = 1, "chaos" = 3, "extended" = -2) var/datum/mind/wizard /datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE) @@ -344,6 +347,7 @@ cost = 35 requirements = list(90,90,90,80,70,60,50,40,40,40) high_population_requirement = 40 + property_weights = list("story_potential" = 3, "trust" = 5, "chaos" = 2, "extended" = -5, "valid" = 10) var/operative_cap = list(2,2,3,3,4,5,5,5,5,5) var/datum/team/nuclear/nuke_team flags = HIGHLANDER_RULESET @@ -396,6 +400,7 @@ requirements = list(101,101,101,80,60,50,50,50,50,50) high_population_requirement = 50 repeatable = TRUE + property_weights = list("story_potential" = -1, "trust" = 5, "chaos" = 3, "extended" = -5, "valid" = 4) /datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) @@ -426,6 +431,7 @@ high_population_requirement = 50 repeatable_weight_decrease = 2 repeatable = TRUE + property_weights = list("story_potential" = -1, "trust" = 5, "chaos" = 2, "extended" = -2, "valid" = 2) var/list/vents = list() /datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE) @@ -480,6 +486,7 @@ high_population_requirement = 50 repeatable_weight_decrease = 2 repeatable = TRUE + property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 5) var/list/spawn_locs = list() /datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute() @@ -525,6 +532,7 @@ 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" = 3, "extended" = 5, "valid" = -5, "integrity" = 2) high_population_requirement = 5 /datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE) @@ -558,6 +566,7 @@ cost = 5 requirements = list(30,30,30,30,20,15,15,15,15,15) high_population_requirement = 15 + property_weights = list("story_potential" = -3, "extended" = 5, "integrity" = 5) var/list/spawn_locs = list() /datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE) @@ -609,6 +618,7 @@ weight = 4 cost = 15 requirements = list(101,101,101,90,80,70,60,50,40,30) + property_weights = list("story_potential" = -3, "extended" = -5, "integrity" = 5, "valid" = 5, "trust" = 3) high_population_requirement = 30 var/list/spawn_locs = list() @@ -660,6 +670,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) repeatable_weight_decrease = 4 repeatable = TRUE @@ -699,6 +710,7 @@ cost = 15 requirements = list(101,101,101,90,80,70,60,50,40,30) high_population_requirement = 30 + property_weights = list("story_potential" = 1, "extended" = -3, "valid" = 3) var/list/spawn_locs = list() var/spawn_loc diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 56b02a1364..0a5fead01b 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -21,6 +21,7 @@ requirements = list(50,50,50,50,50,50,50,50,50,50) high_population_requirement = 40 antag_cap = list(1,1,1,1,2,2,2,2,3,3) + property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1, "valid" = 1) var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec) /datum/dynamic_ruleset/roundstart/traitor/pre_execute() @@ -60,6 +61,7 @@ requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 antag_cap = list(2,2,2,2,2,2,2,2,2,2) // Can pick 3 per team, but rare enough it doesn't matter. + property_weights = list("story_potential" = 1, "trust" = -1, "extended" = 1, "valid" = 1) var/list/datum/team/brother_team/pre_brother_teams = list() var/const/min_team_size = 2 @@ -107,6 +109,7 @@ cost = 15 scaling_cost = 15 requirements = list(101,101,101,101,101,101,101,101,101,101) + property_weights = list("trust" = -2, "valid" = 3) high_population_requirement = 10 antag_cap = list(1,1,1,1,1,2,2,2,2,3) var/team_mode_probability = 30 @@ -159,6 +162,7 @@ cost = 30 requirements = list(101,101,101,60,50,50,50,50,50,50) high_population_requirement = 50 + property_weights = list("story_potential" = 5, "trust" = 1, "chaos" = 3, "extended" = -2, "valid" = 10) var/list/roundstart_wizards = list() /datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0) @@ -221,6 +225,7 @@ weight = 3 cost = 30 requirements = list(101,101,101,80,70,60,50,50,50,50) + property_weights = list("story_potential" = -1, "trust" = -1, "chaos" = 1, "conversion" = 1, "extended" = -5, "valid" = 5) high_population_requirement = 50 flags = HIGHLANDER_RULESET antag_cap = list(2,2,2,3,3,4,4,4,4,4) @@ -282,6 +287,7 @@ high_population_requirement = 50 flags = HIGHLANDER_RULESET antag_cap = list(1,1,2,3,4,5,5,5,5,5) + property_weights = list("story_potential" = 3, "trust" = 5, "chaos" = 2, "extended" = -5, "valid" = 10) var/datum/team/nuclear/nuke_team /datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE) @@ -372,6 +378,7 @@ flags = HIGHLANDER_RULESET // I give up, just there should be enough heads with 35 players... minimum_players = 35 + property_weights = list("trust" = -3, "chaos" = 5, "extended" = -5, "valid" = 2) var/datum/team/revolution/revolution var/finished = FALSE @@ -489,6 +496,7 @@ weight = 3 cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) + property_weights = list("extended" = 1000) high_population_requirement = 101 /datum/dynamic_ruleset/roundstart/extended/pre_execute() @@ -516,6 +524,7 @@ high_population_requirement = 50 flags = HIGHLANDER_RULESET antag_cap = list(2,3,3,4,4,4,4,4,4,4) + property_weights = list("trust" = 3, "chaos" = 5, "extended" = -5, "conversion" = 1, "valid" = 10) var/ark_time /datum/dynamic_ruleset/roundstart/clockcult/pre_execute() @@ -615,6 +624,8 @@ antag_leader_datum = /datum/antagonist/nukeop/leader/clownop requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 + property_weights = list("trust" = 5, "chaos" = 5, "extended" = -5, "story_potential" = 10, "valid" = 10) + /datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute() . = ..() @@ -646,6 +657,7 @@ requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 antag_cap = list(1,1,1,2,2,2,3,3,3,4) + property_weights = list("extended" = 1) /datum/dynamic_ruleset/roundstart/devil/pre_execute() var/num_devils = antag_cap[indice_pop] @@ -697,6 +709,7 @@ cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 + property_weights = list("extended" = -10, "chaos" = 5, "conversion" = 1, "valid" = 10) var/players_per_carrier = 30 var/monkeys_to_win = 1 var/escaped_monkeys = 0 @@ -758,6 +771,7 @@ cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 + property_weights = list("extended" = -10, "chaos" = 5, "trust" = 5) var/meteordelay = 2000 var/nometeors = 0 var/rampupdelta = 5 diff --git a/code/game/gamemodes/dynamic/storytellers/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/storytellers/dynamic_storytellers.dm new file mode 100644 index 0000000000..c0a943a87e --- /dev/null +++ b/code/game/gamemodes/dynamic/storytellers/dynamic_storytellers.dm @@ -0,0 +1,93 @@ +/datum/dynamic_storyteller + var/name = "none" + var/desc = "A coder's idiocy." + var/list/property_weights = list() + var/datum/game_mode/dynamic/mode = null + +/** +Property weights are: +"story_potential" -- essentially how many different ways the antag can be played. +"trust" -- How much it makes the crew trust each other. Negative values means they're suspicious. Team antags are like this. +"chaos" -- How chaotic it makes the round. Has some overlap with "valid" and somewhat contradicts "extended". +"valid" -- How likely the non-antag-enemy crew are to get involved, e.g. nukies encouraging the warden to + let everyone into the armory, wizard moving around and being a nuisance, nightmare busting lights. +"extended" -- How much the antag is conducive to a long round. Nukies and cults are bad for this; Wizard is less bad; and so on. +"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason. +*/ + +/datum/dynamic_storyteller/New() + ..() + if (istype(SSticker.mode, /datum/game_mode/dynamic)) + mode = SSticker.mode + else if (GLOB.master_mode != "dynamic") + qdel(src) + +/datum/dynamic_storyteller/proc/do_process() + continue + +/datum/dynamic_storyteller/proc/midround_draft() + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules) + // if there are antags OR the rule is an antag rule, antag_acceptable will be true. + if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + rule.trim_candidates() + if (rule.ready()) + var/property_weight = 0 + for(var/property in property_weights) + if(property in rule.property_weights) // just treat it as 0 if it's not in there + property_weight += rule.property_weight[property] * property_weights[property] + drafted_rules[rule] = rule.get_weight() + property_weight + return drafted_rules + +/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer) + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules) + if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + // No stacking : only one round-ender, unless threat level > stacking_limit. + if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(rule.flags & HIGHLANDER_RULESET && highlander_executed) + continue + + rule.candidates = list(newPlayer) + rule.trim_candidates() + if (rule.ready()) + var/property_weight = 0 + for(var/property in property_weights) + if(property in rule.property_weights) // just treat it as 0 if it's not in there + property_weight += rule.property_weight[property] * property_weights[property] + drafted_rules[rule] = rule.get_weight() + property_weight + +/datum/dynamic_storyteller/team + name = "Teamwork" + desc = "Rules that are likely to get the crew to work together against a common enemy." + property_weights = list("valid" = 3, "trust" = 5) + +/datum/dynamic_storyteller/liteextended + name = "Extended-lite" + desc = "Rules that are likely to lead to rounds that reach their finish at the shuttle autocall." + property_weights = list("extended" = 5, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10) + +/datum/dynamic_storyteller/memes + name = "Story" + desc = "Rules that might lead to fun stories to tell." + property_weights = list("story_potential" = 10) + +/datum/dynamic_storyteller/cowabunga + name = "Cowabunga" + desc = "The most chaotic rules only. A validhunter's dream." + property_weights = list("extended" = -1, "chaos" = 10) + +/datum/dynamic_storyteller/suspicion + name = "Intrigue" + desc = "Rules that lead the crew to distrust one another." + property_weights = list("trust" = -5, "valid" = -1) + +/datum/dynamic_storyteller/classic + name = "Classic" + desc = "Uses default dynamic weights and nothing else. The most variety."