From 2d998b83172442eeb08dad7583041e965d5cb164 Mon Sep 17 00:00:00 2001 From: Contrabang <91113370+Contrabang@users.noreply.github.com> Date: Sat, 8 Feb 2025 12:10:21 -0500 Subject: [PATCH] Cult is now part of dynamic (#28236) * Adds cult to dynamic * declare result * more dynamic stuff * config * comment * tweaks * numbers changes --- code/__DEFINES/gamemode.dm | 2 + code/game/gamemodes/dynamic/antag_rulesets.dm | 78 ++++++++++++++++++- code/game/gamemodes/dynamic/dynamic.dm | 47 +++++------ code/modules/admin/topic.dm | 2 +- config/example/config.toml | 2 +- 5 files changed, 103 insertions(+), 28 deletions(-) diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 76332f7fb73..a6b8da39714 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -80,5 +80,7 @@ #define DYNAMIC_RULESET_BANNED "Banned" #define RULESET_FAILURE_BUDGET "Not enough budget" +#define RULESET_FAILURE_ANTAG_BUDGET "Not enough antag budget" #define RULESET_FAILURE_NO_PLAYERS "No drafted players" +#define RULESET_FAILURE_MUTUAL_RULESET "No banned mutual rulesets" #define RULESET_FAILURE_CHANGELING_SECONDARY_RULESET "Needs a secondary ruleset in rotation" diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm index 62293adf6cb..8918c7d20a2 100644 --- a/code/game/gamemodes/dynamic/antag_rulesets.dm +++ b/code/game/gamemodes/dynamic/antag_rulesets.dm @@ -42,7 +42,10 @@ /// If true, the species blacklist is now a species whitelist var/banned_species_only = FALSE - // var/list/banned_mutual_rulesets = list() // UNIMPLEMENTED: could be used to prevent nukies rolling while theres cultists, or wizards, etc + /// Rulesets that cannot be rolled while this ruleset is active. Used to prevent traitors from rolling while theres cultists, etc. + var/list/banned_mutual_rulesets = list( + /datum/ruleset/team/cult, + ) /* This stuff changes, all stuff above is static */ /// How many antagonists to spawn @@ -56,11 +59,17 @@ stack_trace("[src] ([type]) was destroyed.") return ..() -/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets) +/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets, antag_budget) if(ruleset_budget < ruleset_cost) return RULESET_FAILURE_BUDGET + if(antag_budget < antag_cost) + return RULESET_FAILURE_ANTAG_BUDGET if(!length(SSticker.mode.get_players_for_role(antagonist_type::job_rank))) // this specifically needs to be job_rank not special_rank return RULESET_FAILURE_NO_PLAYERS + if(length(banned_mutual_rulesets) && length(rulesets)) + for(var/datum/ruleset/ruleset in rulesets) + if(ruleset.type in banned_mutual_rulesets) + return RULESET_FAILURE_MUTUAL_RULESET /datum/ruleset/proc/antagonist_possible(budget) return budget >= antag_cost @@ -148,8 +157,8 @@ /datum/ruleset/proc/latespawn(datum/game_mode/dynamic/dynamic) // latespawning is only used by traitors at this point, so we're just going to be naive and allocate all budget when this proc is called. - var/late_antag_amount = floor(dynamic.budget_overflow / antag_cost) - dynamic.budget_overflow -= (late_antag_amount * antag_cost) + var/late_antag_amount = floor(dynamic.antag_budget / antag_cost) + dynamic.antag_budget -= (late_antag_amount * antag_cost) var/list/datum/mind/possible_antags = get_latejoin_players() for(var/i in 1 to late_antag_amount) @@ -158,6 +167,13 @@ log_dynamic("Latespawned [late_antag_amount] [name]s.") +/datum/ruleset/proc/automatic_deduct(budget) + . = antag_cost * antag_amount + log_dynamic("Automatic deduction: +[antag_amount] [name]\s. Remaining budget: [budget - .].") + +/datum/ruleset/proc/declare_completion() + return + /datum/ruleset/traitor name = "Traitor" ruleset_weight = 11 @@ -205,6 +221,7 @@ // This is the fucking worst, but its required to not change functionality with mindflayers. Cannot be rolled normally, this is applied by other methods. /datum/ruleset/implied + name = "BASE IMPLIED RULESET" // These 3 variables should never change ruleset_cost = 0 ruleset_weight = 0 @@ -234,3 +251,56 @@ implier.antag_amount -= 1 antag_amount += 1 was_triggered = TRUE + +/datum/ruleset/team + name = "BASE TEAM RULESET" + ruleset_weight = 0 + /// Whether there should only be one of this kind of team. This could be used for blood-brothers if false. + var/unique_team = TRUE + /// How many players on a team. + var/team_size = 1 + /// Team datum to create. + var/datum/team/team_type + +/datum/ruleset/team/roundstart_post_setup(datum/game_mode/dynamic) + if(unique_team) + new team_type(pre_antags) + return + stack_trace("Undefined behavior for dynamic non-unique teams!") + +/datum/ruleset/team/automatic_deduct(budget) + antag_amount = team_size + . = ..() + +/datum/ruleset/team/antagonist_possible(budget) + if(unique_team) // we're given our size at the start, no more please! + return FALSE + return ..() + +/datum/ruleset/team/cult + name = "Cultist" + ruleset_cost = 1 + ruleset_weight = 3 + // antag_weight doesnt matter, since we've already allocated our budget for 4 cultists only + antag_cost = 30 + antagonist_type = /datum/antagonist/cultist + banned_mutual_rulesets = list( + /datum/ruleset/traitor, + /datum/ruleset/vampire, + /datum/ruleset/changeling + ) + banned_jobs = list("Cyborg", "AI", "Chaplain", "Head of Personnel") + + team_size = 4 + team_type = /datum/team/cult + +/datum/ruleset/team/cult/declare_completion() + if(SSticker.mode.cult_team.cult_status == NARSIE_HAS_RISEN) + SSticker.mode_result = "cult win - cult win" + to_chat(world, "The cult wins! It has succeeded in summoning [GET_CULT_DATA(entity_name, "their god")]!") + else if(SSticker.mode.cult_team.cult_status == NARSIE_HAS_FALLEN) + SSticker.mode_result = "cult draw - narsie died, nobody wins" + to_chat(world, "Nobody wins! [GET_CULT_DATA(entity_name, "the cult god")] was summoned, but banished!") + else + SSticker.mode_result = "cult loss - staff stopped the cult" + to_chat(world, "The staff managed to stop the cult!") diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index d16bea19608..b75cdc5fca9 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -10,7 +10,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) var/list/datum/ruleset/implied_rulesets = list() /// How much budget is left after roundstart antagonists roll - var/budget_overflow = 0 + var/antag_budget = 0 /// Log for what happens in a dynamic round var/list/dynamic_log = list() @@ -27,7 +27,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) to_chat(world, "Possible Rulesets: [english_list(possible_rulesets)]") /datum/game_mode/dynamic/proc/allocate_ruleset_budget() - var/ruleset_budget = text2num(GLOB.dynamic_forced_rulesets["budget"] || pickweight(list("0" = 3, "1" = 5, "2" = 12, "3" = 3))) // more likely to or 2 + var/ruleset_budget = text2num(GLOB.dynamic_forced_rulesets["budget"] || pickweight(list("0" = 3, "1" = 8, "2" = 12, "3" = 3))) + antag_budget = num_players() log_dynamic("Allocated gamemode budget: [ruleset_budget]") var/list/possible_rulesets = list() for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset)) @@ -68,9 +69,9 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) if(!ruleset) return if(!force) - var/failure_reason = ruleset.ruleset_possible(ruleset_budget, rulesets) + var/failure_reason = ruleset.ruleset_possible(ruleset_budget, rulesets, antag_budget) if(failure_reason) - log_dynamic("Failed [ruleset.name] ruleset: [failure_reason]") + log_dynamic("Failed [ruleset.name] ruleset: [failure_reason].") return log_dynamic("Rolled ruleset: [ruleset.name]") rulesets[ruleset] = ruleset.antag_weight @@ -89,33 +90,30 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) if(!length(rulesets)) log_dynamic("No rulesets in play.") return - var/budget = num_players() - log_dynamic("Allocated antagonist budget: [budget].") + log_dynamic("Allocated antagonist budget: [antag_budget].") for(var/datum/ruleset/ruleset in rulesets) ruleset.antag_amount = 1 - budget -= ruleset.antag_cost - log_dynamic("Automatic deduction: +1 [ruleset.name]. Remaining budget: [budget].") + antag_budget -= ruleset.automatic_deduct(antag_budget) log_dynamic("Rulesets in play: [english_list((rulesets + implied_rulesets))]") - apply_antag_budget(budget) + apply_antag_budget() -/datum/game_mode/dynamic/proc/apply_antag_budget(budget) // todo, can be called later in the game to apply more budget. That also means there has to be shit done for latejoins. +/datum/game_mode/dynamic/proc/apply_antag_budget() // todo, can be called later in the game to apply more budget. That also means there has to be shit done for latejoins. var/list/temp_rulesets = rulesets.Copy() - while(budget >= 0) + while(antag_budget >= 0) var/datum/ruleset/ruleset = pickweight(temp_rulesets) if(!ruleset) - log_dynamic("No rulesets remaining. Remaining budget: [budget].") - budget_overflow = budget + log_dynamic("No rulesets remaining. Remaining budget: [antag_budget].") return - if(!ruleset.antagonist_possible(budget)) + if(!ruleset.antagonist_possible(antag_budget)) log_dynamic("Rolled [ruleset.name]: failed, removing [ruleset.name] ruleset.") temp_rulesets -= ruleset continue ruleset.antag_amount++ - budget -= ruleset.antag_cost - log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].") + antag_budget -= ruleset.antag_cost + log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [antag_budget].") log_dynamic("No more antagonist budget remaining.") /datum/game_mode/dynamic/pre_setup() @@ -128,10 +126,10 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets log_dynamic("Applying [ruleset.antag_amount] [ruleset.name]\s.") - budget_overflow += ruleset.roundstart_pre_setup() + antag_budget += ruleset.roundstart_pre_setup() - log_dynamic("Budget overflow: [budget_overflow].") - // for the future, maybe try readding antagonists with apply_antag_budget(budget_overflow) + log_dynamic("Budget overflow: [antag_budget].") + // for the future, maybe try readding antagonists with apply_antag_budget(antag_budget) log_dynamic("Finished dynamic setup in [stop_watch(watch)]s.") return TRUE @@ -147,19 +145,19 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) /datum/game_mode/dynamic/latespawn(mob) . = ..() - budget_overflow++ + antag_budget++ /datum/game_mode/dynamic/on_mob_cryo(mob/sleepy_mob, obj/machinery/cryopod/cryopod) var/turf/T = get_turf(cryopod) if(!T || is_admin_level(T.z)) return - budget_overflow-- + antag_budget-- if(!sleepy_mob.mind || !length(sleepy_mob.mind.antag_datums)) return for(var/datum/antagonist/antag in sleepy_mob.mind.antag_datums) for(var/datum/ruleset/possible_ruleset as anything in subtypesof(/datum/ruleset)) if(istype(antag, possible_ruleset.antagonist_type)) - budget_overflow += possible_ruleset.antag_cost + antag_budget += possible_ruleset.antag_cost log_dynamic("[possible_ruleset] cryo. +[possible_ruleset.antag_cost] budget.") /datum/game_mode/dynamic/get_webhook_name() @@ -169,6 +167,11 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets) implied_and_used += implied return "[name] ([english_list(rulesets + implied_and_used, nothing_text = "Extended")])" +/datum/game_mode/dynamic/declare_completion() + for(var/datum/ruleset/ruleset in rulesets) + ruleset.declare_completion() + . = ..() + /proc/log_dynamic(text) log_game("Dynamic: [text]") var/datum/game_mode/dynamic/dynamic = SSticker.mode diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 711f9636d04..1d0afd9fbfc 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1056,7 +1056,7 @@ if(GLOB.master_mode != "dynamic" && !(GLOB.master_mode == "secret" && GLOB.secret_force_mode == "dynamic")) return alert(usr, "The game mode has to be dynamic!", null, null, null, null) var/dat = {"Possible Rulesets:
"} - var/list/rulesets = subtypesof(/datum/ruleset) - typesof(/datum/ruleset/implied) + var/list/rulesets = subtypesof(/datum/ruleset) - typesof(/datum/ruleset/implied) - /datum/ruleset/team dat += {"Budget: [isnull(GLOB.dynamic_forced_rulesets["budget"]) ? "Random" : GLOB.dynamic_forced_rulesets["budget"]]
"} for(var/datum/ruleset/ruleset as anything in rulesets) dat += {"[ruleset.name]: [GLOB.dynamic_forced_rulesets[ruleset] || DYNAMIC_RULESET_NORMAL]
"} diff --git a/config/example/config.toml b/config/example/config.toml index 5819604446c..3d9648a31a8 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -261,7 +261,7 @@ antag_account_age_restrictions = false gamemode_probabilities = [ { gamemode = "abduction", probability = 0 }, { gamemode = "changeling", probability = 0 }, - { gamemode = "cult", probability = 3 }, + { gamemode = "cult", probability = 0 }, { gamemode = "extend-a-traitormongous", probability = 2 }, # Autotraitor { gamemode = "extended", probability = 0 }, { gamemode = "nuclear", probability = 2 },