Skip to content

Commit

Permalink
Cult is now part of dynamic (#28236)
Browse files Browse the repository at this point in the history
* Adds cult to dynamic

* declare result

* more dynamic stuff

* config

* comment

* tweaks

* numbers changes
  • Loading branch information
Contrabang authored Feb 8, 2025
1 parent 8e4dc61 commit 2d998b8
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 28 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/gamemode.dm
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,7 @@
#define DYNAMIC_RULESET_BANNED "<b>Banned</b>"

#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"
78 changes: 74 additions & 4 deletions code/game/gamemodes/dynamic/antag_rulesets.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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, "<span class='danger'><FONT size=3>The cult wins! It has succeeded in summoning [GET_CULT_DATA(entity_name, "their god")]!</FONT></span>")
else if(SSticker.mode.cult_team.cult_status == NARSIE_HAS_FALLEN)
SSticker.mode_result = "cult draw - narsie died, nobody wins"
to_chat(world, "<span class='danger'><FONT size = 3>Nobody wins! [GET_CULT_DATA(entity_name, "the cult god")] was summoned, but banished!</FONT></span>")
else
SSticker.mode_result = "cult loss - staff stopped the cult"
to_chat(world, "<span class='warning'><FONT size = 3>The staff managed to stop the cult!</FONT></span>")
47 changes: 25 additions & 22 deletions code/game/gamemodes/dynamic/dynamic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -27,7 +27,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
to_chat(world, "<b>Possible Rulesets:</b> [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))
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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

Expand All @@ -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()
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/admin/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {"<!DOCTYPE html><b>Possible Rulesets:</b><hr>"}
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: <a href='byond://?src=[UID()];f_dynamic2=budget'>[isnull(GLOB.dynamic_forced_rulesets["budget"]) ? "Random" : GLOB.dynamic_forced_rulesets["budget"]]</a><hr>"}
for(var/datum/ruleset/ruleset as anything in rulesets)
dat += {"[ruleset.name]: <a href='byond://?src=[UID()];f_dynamic2=[ruleset.type]'>[GLOB.dynamic_forced_rulesets[ruleset] || DYNAMIC_RULESET_NORMAL]</a><br>"}
Expand Down
2 changes: 1 addition & 1 deletion config/example/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down

0 comments on commit 2d998b8

Please sign in to comment.