From 95b3850ffb381bce4e222785f7e0ab0a746fabe1 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Tue, 29 Oct 2024 21:46:13 -0400
Subject: [PATCH 01/17] yeah
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 91 +++++++++++++++++++
code/game/gamemodes/dynamic/dynamic.dm | 80 ++++++++++++++++
2 files changed, 171 insertions(+)
create mode 100644 code/game/gamemodes/dynamic/antag_rulesets.dm
create mode 100644 code/game/gamemodes/dynamic/dynamic.dm
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
new file mode 100644
index 000000000000..88fb4563ba76
--- /dev/null
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -0,0 +1,91 @@
+#define MINDSHIELDED_JOBS list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General")
+
+/datum/antagonist_ruleset
+ var/ruleset_cost = 1
+ var/ruleset_weight = 1
+ var/cost = 1
+ var/weight = 1
+ var/list/banned_mutual_rulesets = list() // could be used to prevent nukies rolling while theres cultists, or wizards, etc
+
+ var/datum/antagonist/antagonist
+ var/mind_role
+ // these roles 100% cannot be this antagonist
+ var/list/banned_jobs = list("Cyborg")
+ // These roles can't be antagonists because mindshielding (this can be disabled via config)
+ var/list/protected_jobs = MINDSHIELDED_JOBS
+ var/list/banned_species = list()
+ /* This stuff changes, all stuff above is static */
+ var/antag_amount = 0
+ var/list/datum/mind/pre_antags = list()
+
+
+/datum/antagonist_ruleset/proc/ruleset_possible(gamerule_budget)
+ if(gamerule_budget < ruleset_cost)
+ return FALSE
+ if(!length(Ssticker.mode.get_players_for_role(mind_role)))
+ return FALSE
+ return TRUE
+
+/datum/antagonist_ruleset/proc/antagonist_possible(budget)
+ return budget >= cost
+
+/datum/antagonist_ruleset/proc/apply_ruleset()
+ var/list/datum/mind/possible_antags = get_players_for_role(mind_role)
+ if(!length(possible_antags))
+ stack_trace("FUUUUUUUUUUUUUUUUUUUUUUUCK") // we allocate antag budget before we allocate players, and previous rulesets can steal our players
+ return FALSE // oh shit this ruleset failed!!! not good
+
+ if(GLOB.configuration.gamemode.prevent_mindshield_antags)
+ banned_jobs += protected_jobs
+ for(var/datum/mind/antag as anything in shuffle(possible_antags))
+ if(antag_amount <= 0)
+ break
+ if(antag.current.client.prefs.active_character.species in banned_species)
+ continue
+ pre_antags += antag
+ antag.special_role = mind_role
+ antag.restricted_roles = banned_jobs
+ antag_amount -= 1
+
+ if(antag_amount > 0)
+ // not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
+ // OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
+ stack_trace("antag_amount: [antag_amount]. FUUUUUCK.")
+
+/datum/antagonist_ruleset/proc/post_setup()
+ for(var/datum/mind/antag as anything in pre_antags)
+ antag.add_antag_datum(antagonist)
+
+/datum/antagonist_ruleset/traitor
+ ruleset_weight = 13
+ cost = 5
+ weight = 2
+ antagonist = /datum/antagonist/traitor
+ mind_role = SPECIAL_ROLE_TRAITOR
+
+/datum/antagonist_ruleset/traitor/post_setup()
+ var/random_time = rand(5 MINUTES, 15 MINUTES)
+ for(var/datum/mind/antag as anything in pre_antags)
+ var/datum/antagonist/traitor/traitor_datum = new antagonist()
+ if(ishuman(antag.current))
+ traitor_datum.delayed_objectives = TRUE
+ traitor_datum.addtimer(CALLBACK(traitor_datum, TYPE_PROC_REF(/datum/antagonist/traitor, reveal_delayed_objectives)), random_time, TIMER_DELETE_ME)
+ antag.add_antag_datum(traitor_datum)
+
+/datum/antagonist_ruleset/vampire
+ ruleset_weight = 9
+ cost = 10
+ antagonist = /datum/antagonist/vampire
+ mind_role = ROLE_VAMPIRE
+
+ banned_jobs = list("Cyborg", "AI", "Chaplain")
+ banned_species = list("Machine")
+
+/datum/antagonist_ruleset/changeling
+ ruleset_weight = 9
+ cost = 10
+ antagonist = /datum/antagonist/changeling
+ mind_role = ROLE_CHANGELING
+
+ banned_jobs = list("Cyborg", "AI")
+ banned_species = list("Machine")
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
new file mode 100644
index 000000000000..3cc109b82c90
--- /dev/null
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -0,0 +1,80 @@
+/datum/game_mode/dynamic
+ name = "Dynamic"
+ config_tag = "dynamic"
+ secondary_restricted_jobs = list("AI")
+ required_players = 25
+ var/list/datum/antagonist_ruleset/rulesets = list()
+
+/datum/game_mode/dynamic/announce()
+ to_chat(world, "The current game mode is - Dynamic")
+ to_chat(world, "There could be anything lurking in the shadows.")
+
+/datum/game_mode/dynamic/proc/allocate_gamemode_budget()
+ var/gamemode_budget = text2num(pickweight(list("0" = 3, "1" = 8, "2" = 9, "3" = 3))) // more likely to be 1 or 2, than 3 or 0.
+ if(gamemode_budget <= 0)
+ return
+ var/list/all_possible_rulesets = list()
+ for(var/ruleset in subtypesof(/datum/antagonist_ruleset))
+ if(ruleset.ruleset_weight <= 0)
+ continue
+ var/datum/antagonist_ruleset/new_ruleset = new ruleset()
+ all_possible_rulesets[new_ruleset] = new_ruleset.ruleset_weight
+
+ var/minimum_ruleset_cost = INFINITY
+ for(var/datum/antagonist_ruleset/ruleset_key in all_possible_rulesets)
+ minimum_ruleset_cost = min(minimum_ruleset_cost, ruleset_key.ruleset_cost)
+
+ while(gamemode_budget >= minimum_ruleset_cost)
+ var/datum/antagonist_ruleset/ruleset in pickweight(all_possible_rulesets)
+ if(ruleset.ruleset_possible(gamemode_budget))
+ rulesets[ruleset] = ruleset.weight
+ gamemode_budget -= ruleset.ruleset_cost
+
+/datum/game_mode/dynamic/proc/allocate_antagonist_budget()
+ if(!length(rulesets))
+ return
+ var/budget = num_players()
+ for(var/datum/antagonist_ruleset/ruleset in rulesets)
+ ruleset.antag_amount = 1
+ budget -= ruleset.cost
+
+ var/minimum_cost = INFINITY
+ for(var/datum/antagonist_ruleset/ruleset_key in rulesets)
+ minimum_cost = min(minimum_cost, ruleset_key.cost)
+
+ while(budget >= minimum_cost)
+ var/datum/antagonist_ruleset/ruleset in pickweight(rulesets)
+ if(ruleset.antagonist_possible(budget))
+ ruleset.antag_amount++
+ budget -= ruleset.cost
+
+/datum/game_mode/dynamic/pre_setup()
+ allocate_gamemode_budget()
+ allocate_antagonist_budget()
+ for(var/datum/antagonist_ruleset/ruleset in rulesets)
+ ruleset.apply_ruleset()
+
+ return TRUE
+
+/datum/game_mode/dynamic/post_setup()
+ for(var/datum/antagonist_ruleset/ruleset in rulesets)
+ ruleset.post_setup()
+ ..()
+
+// /datum/game_mode/dynamic/traitors_to_add()
+// . = 0
+// for(var/datum/mind/traitor_mind as anything in traitors)
+// if(!QDELETED(traitor_mind) && traitor_mind.current) // Explicitly no client check in case you happen to fall SSD when this gets ran
+// continue
+// .++
+// traitors -= traitor_mind
+
+// var/extra_points = num_players_started() - cost_at_roundstart
+// if(extra_points - TOT_COST < 0)
+// return 0 // Not enough new players to add extra tots
+
+// while(extra_points)
+// .++
+// if(extra_points < TOT_COST) // The leftover change is enough for us to buy another traitor with, what a deal!
+// return
+// extra_points -= TOT_COST
From 4712d805f5127b6ba12b182c3c9f0aa1dc0d89a3 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Wed, 30 Oct 2024 18:33:03 -0400
Subject: [PATCH 02/17] yeah
---
code/__DEFINES/directions.dm | 2 +
code/game/gamemodes/dynamic/antag_rulesets.dm | 75 +++++++++++++++++--
code/game/gamemodes/dynamic/dynamic.dm | 58 ++++++++------
3 files changed, 106 insertions(+), 29 deletions(-)
diff --git a/code/__DEFINES/directions.dm b/code/__DEFINES/directions.dm
index b78423b7bcc2..718ebeedf14f 100644
--- a/code/__DEFINES/directions.dm
+++ b/code/__DEFINES/directions.dm
@@ -35,3 +35,5 @@
#define DIR_JUST_HORIZONTAL(dir) ((dir == EAST) || (dir == WEST))
/// returns TRUE if the direction is NORTH or SOUTH
#define DIR_JUST_VERTICAL(dir) ((dir == NORTH) || (dir == SOUTH))
+
+#define EXCLUSIVE_OR(thing_one, thing_two) ((thing_one)^(thing_two))
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 88fb4563ba76..b2792f9e6e6b 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -1,19 +1,27 @@
#define MINDSHIELDED_JOBS list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General")
/datum/antagonist_ruleset
+ var/name = "BASE RULESET"
var/ruleset_cost = 1
var/ruleset_weight = 1
var/cost = 1
var/weight = 1
- var/list/banned_mutual_rulesets = list() // could be used to prevent nukies rolling while theres cultists, or wizards, etc
+ // var/list/banned_mutual_rulesets = list() // could be used to prevent nukies rolling while theres cultists, or wizards, etc
+ var/datum/antagonist_ruleset/implied_ruleset
var/datum/antagonist/antagonist
var/mind_role
+
// these roles 100% cannot be this antagonist
var/list/banned_jobs = list("Cyborg")
// These roles can't be antagonists because mindshielding (this can be disabled via config)
var/list/protected_jobs = MINDSHIELDED_JOBS
+ // Applies the mind roll to assigned_role, preventing them from rolling a normal job. Good for wizards and nuclear operatives.
+ var/assign_job_role = FALSE
+
var/list/banned_species = list()
+ var/banned_species_only = FALSE
+
/* This stuff changes, all stuff above is static */
var/antag_amount = 0
var/list/datum/mind/pre_antags = list()
@@ -29,34 +37,52 @@
/datum/antagonist_ruleset/proc/antagonist_possible(budget)
return budget >= cost
-/datum/antagonist_ruleset/proc/apply_ruleset()
+/datum/antagonist_ruleset/proc/pre_setup()
var/list/datum/mind/possible_antags = get_players_for_role(mind_role)
if(!length(possible_antags))
- stack_trace("FUUUUUUUUUUUUUUUUUUUUUUUCK") // we allocate antag budget before we allocate players, and previous rulesets can steal our players
- return FALSE // oh shit this ruleset failed!!! not good
+ refund("No possible players for [src] ruleset.") // we allocate antag budget before we allocate players, and previous rulesets can steal our players
+ return cost*antag_amount // shitty refund for now
if(GLOB.configuration.gamemode.prevent_mindshield_antags)
banned_jobs += protected_jobs
+
for(var/datum/mind/antag as anything in shuffle(possible_antags))
if(antag_amount <= 0)
break
- if(antag.current.client.prefs.active_character.species in banned_species)
+ if(!can_apply(antag))
continue
pre_antags += antag
+ if(assign_job_role)
+ antag.assigned_role = mind_role
antag.special_role = mind_role
antag.restricted_roles = banned_jobs
antag_amount -= 1
if(antag_amount > 0)
- // not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
- // OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
- stack_trace("antag_amount: [antag_amount]. FUUUUUCK.")
+ refund("Missing [antag_amount] antagonists for [src] ruleset.")
+ return cost*antag_amount // shitty refund for now
+
+/datum/antagonist_ruleset/proc/can_apply(datum/mind/antag)
+ if(EXCLUSIVE_OR(antag.current.client.prefs.active_character.species in banned_species, banned_species_only))
+ SEND_SIGNAL(src, COMSIG_RULESET_FAILED_SPECIES)
+ return FALSE
+ if(antag.special_role) // You can only have 1 antag roll at a time, sorry
+ return FALSE
+ return TRUE
/datum/antagonist_ruleset/proc/post_setup()
for(var/datum/mind/antag as anything in pre_antags)
antag.add_antag_datum(antagonist)
+/datum/antagonist_ruleset/proc/refund(info)
+ // not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
+ // OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
+ stack_trace("[info] Refunding [cost*antag_amount] budget.")
+ stack_trace("REFUNDING NOT IMPLEMENTED!!")
+ // ctodo real refunding?
+
/datum/antagonist_ruleset/traitor
+ name = "Traitor"
ruleset_weight = 13
cost = 5
weight = 2
@@ -73,6 +99,7 @@
antag.add_antag_datum(traitor_datum)
/datum/antagonist_ruleset/vampire
+ name = "Vampire"
ruleset_weight = 9
cost = 10
antagonist = /datum/antagonist/vampire
@@ -80,8 +107,10 @@
banned_jobs = list("Cyborg", "AI", "Chaplain")
banned_species = list("Machine")
+ implied_ruleset = /datum/antagonist_ruleset/implied/mindflayer
/datum/antagonist_ruleset/changeling
+ name = "Changeling"
ruleset_weight = 9
cost = 10
antagonist = /datum/antagonist/changeling
@@ -89,3 +118,33 @@
banned_jobs = list("Cyborg", "AI")
banned_species = list("Machine")
+ implied_ruleset = /datum/antagonist_ruleset/implied/mindflayer
+
+/datum/antagonist_ruleset/implied
+ // cannot be rolled normally, this is applied by other methods.
+ ruleset_cost = 0
+ var/ruleset_weight = 0
+ var/cost = 0
+ var/weight = 0
+ // This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
+ var/target_signal
+
+/datum/antagonist_ruleset/implied/proc/on_implied(datum/antagonist/implier)
+ return
+
+/datum/antagonist_ruleset/implied/mindflayer
+ name = "Mindflayer"
+ antagonist = /datum/antagonist/mindflayer
+ mind_role = ROLE_MIND_FLAYER
+ target_signal = COMSIG_RULESET_FAILED_SPECIES
+
+ banned_jobs = list("Cyborg", "AI")
+ banned_species = list("Machine")
+ banned_species_only = TRUE
+
+/datum/antagonist_ruleset/implied/mindflayer/on_implied(datum/antagonist/implier)
+ implier.antag_amount -= 1
+ antag_amount += 1
+
+// /datum/antagonist_ruleset/team
+// var/datum/team/team_type
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 3cc109b82c90..07d1bc053907 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -4,6 +4,7 @@
secondary_restricted_jobs = list("AI")
required_players = 25
var/list/datum/antagonist_ruleset/rulesets = list()
+ var/list/datum/antagonist_ruleset/implied_rulesets = list()
/datum/game_mode/dynamic/announce()
to_chat(world, "The current game mode is - Dynamic")
@@ -13,22 +14,31 @@
var/gamemode_budget = text2num(pickweight(list("0" = 3, "1" = 8, "2" = 9, "3" = 3))) // more likely to be 1 or 2, than 3 or 0.
if(gamemode_budget <= 0)
return
- var/list/all_possible_rulesets = list()
+ var/list/possible_rulesets = list()
for(var/ruleset in subtypesof(/datum/antagonist_ruleset))
if(ruleset.ruleset_weight <= 0)
continue
var/datum/antagonist_ruleset/new_ruleset = new ruleset()
- all_possible_rulesets[new_ruleset] = new_ruleset.ruleset_weight
+ possible_rulesets[new_ruleset] = new_ruleset.ruleset_weight
- var/minimum_ruleset_cost = INFINITY
- for(var/datum/antagonist_ruleset/ruleset_key in all_possible_rulesets)
- minimum_ruleset_cost = min(minimum_ruleset_cost, ruleset_key.ruleset_cost)
+ while(gamemode_budget >= 0)
+ var/datum/antagonist_ruleset/ruleset in pickweight(possible_rulesets)
+ if(!ruleset)
+ return
+ if(!ruleset.ruleset_possible(gamemode_budget))
+ possible_rulesets -= ruleset
+ continue
+
+ rulesets[ruleset] = ruleset.weight
+ gamemode_budget -= ruleset.ruleset_cost
+ if(!ruleset.implied_ruleset)
+ continue
- while(gamemode_budget >= minimum_ruleset_cost)
- var/datum/antagonist_ruleset/ruleset in pickweight(all_possible_rulesets)
- if(ruleset.ruleset_possible(gamemode_budget))
- rulesets[ruleset] = ruleset.weight
- gamemode_budget -= ruleset.ruleset_cost
+ var/datum/antagonist_ruleset/implied = locate(ruleset.implied_ruleset) in implied_rulesets
+ if(!implied)
+ implied = new ruleset.implied_ruleset
+ implied_rulesets += implied
+ implied.RegisterSignal(ruleset, implied.target_signal, PROC_REF(on_implied))
/datum/game_mode/dynamic/proc/allocate_antagonist_budget()
if(!length(rulesets))
@@ -38,26 +48,32 @@
ruleset.antag_amount = 1
budget -= ruleset.cost
- var/minimum_cost = INFINITY
- for(var/datum/antagonist_ruleset/ruleset_key in rulesets)
- minimum_cost = min(minimum_cost, ruleset_key.cost)
+ apply_antag_budget(budget)
- while(budget >= minimum_cost)
- var/datum/antagonist_ruleset/ruleset in pickweight(rulesets)
- if(ruleset.antagonist_possible(budget))
- ruleset.antag_amount++
- budget -= ruleset.cost
+/datum/game_mode/dynamic/proc/apply_antag_budget(budget) // ctodo, 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)
+ var/datum/antagonist_ruleset/ruleset in pickweight(temp_rulesets)
+ if(!ruleset)
+ return
+ if(!ruleset.antagonist_possible(budget))
+ temp_rulesets -= ruleset
+ continue
+ ruleset.antag_amount++
+ budget -= ruleset.cost
/datum/game_mode/dynamic/pre_setup()
allocate_gamemode_budget()
allocate_antagonist_budget()
- for(var/datum/antagonist_ruleset/ruleset in rulesets)
- ruleset.apply_ruleset()
+ var/budget_overflow = 0
+ for(var/datum/antagonist_ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets
+ budget_overflow += ruleset.pre_setup()
+ apply_antag_budget(budget_overflow) // CTODO CURRENTLY, THIS INCREASES THE BUDGET AND DOES NOTHING!!!
return TRUE
/datum/game_mode/dynamic/post_setup()
- for(var/datum/antagonist_ruleset/ruleset in rulesets)
+ for(var/datum/antagonist_ruleset/ruleset in (rulesets + implied_rulesets))
ruleset.post_setup()
..()
From 92e419e8a5f2048ac8cce7269a034bbe7a026de2 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Mon, 9 Dec 2024 12:43:37 -0500
Subject: [PATCH 03/17] lock tf in
---
code/__DEFINES/dcs/datum_signals.dm | 5 ++
code/game/gamemodes/dynamic/antag_rulesets.dm | 53 +++++++-------
code/game/gamemodes/dynamic/dynamic.dm | 70 +++++++++++++------
code/modules/admin/player_panel.dm | 3 +
code/modules/admin/topic.dm | 36 +++++++++-
paradise.dme | 2 +
6 files changed, 117 insertions(+), 52 deletions(-)
diff --git a/code/__DEFINES/dcs/datum_signals.dm b/code/__DEFINES/dcs/datum_signals.dm
index 6850155f3451..d5ece8301bea 100644
--- a/code/__DEFINES/dcs/datum_signals.dm
+++ b/code/__DEFINES/dcs/datum_signals.dm
@@ -120,3 +120,8 @@
/// Sent when bodies transfer between shades/shards and constructs
/// from base of /datum/component/construct_held_body/proc/transfer_held_body()
#define COMSIG_SHADE_TO_CONSTRUCT_TRANSFER "shade_to_construct_transfer"
+
+// /datum/ruleset
+
+/// from base of /datum/ruleset/proc/can_apply()
+#define COMSIG_RULESET_FAILED_SPECIES "failed_species"
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index b2792f9e6e6b..31a47b3422e7 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -1,13 +1,13 @@
#define MINDSHIELDED_JOBS list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General")
-/datum/antagonist_ruleset
+/datum/ruleset
var/name = "BASE RULESET"
var/ruleset_cost = 1
var/ruleset_weight = 1
var/cost = 1
var/weight = 1
// var/list/banned_mutual_rulesets = list() // could be used to prevent nukies rolling while theres cultists, or wizards, etc
- var/datum/antagonist_ruleset/implied_ruleset
+ var/datum/ruleset/implied/implied_ruleset
var/datum/antagonist/antagonist
var/mind_role
@@ -27,18 +27,14 @@
var/list/datum/mind/pre_antags = list()
-/datum/antagonist_ruleset/proc/ruleset_possible(gamerule_budget)
- if(gamerule_budget < ruleset_cost)
- return FALSE
- if(!length(Ssticker.mode.get_players_for_role(mind_role)))
- return FALSE
- return TRUE
+/datum/ruleset/proc/ruleset_possible()
+ return length(SSticker.mode.get_players_for_role(mind_role))
-/datum/antagonist_ruleset/proc/antagonist_possible(budget)
+/datum/ruleset/proc/antagonist_possible(budget)
return budget >= cost
-/datum/antagonist_ruleset/proc/pre_setup()
- var/list/datum/mind/possible_antags = get_players_for_role(mind_role)
+/datum/ruleset/proc/pre_setup()
+ var/list/datum/mind/possible_antags = SSticker.mode.get_players_for_role(mind_role)
if(!length(possible_antags))
refund("No possible players for [src] ruleset.") // we allocate antag budget before we allocate players, and previous rulesets can steal our players
return cost*antag_amount // shitty refund for now
@@ -62,7 +58,7 @@
refund("Missing [antag_amount] antagonists for [src] ruleset.")
return cost*antag_amount // shitty refund for now
-/datum/antagonist_ruleset/proc/can_apply(datum/mind/antag)
+/datum/ruleset/proc/can_apply(datum/mind/antag)
if(EXCLUSIVE_OR(antag.current.client.prefs.active_character.species in banned_species, banned_species_only))
SEND_SIGNAL(src, COMSIG_RULESET_FAILED_SPECIES)
return FALSE
@@ -70,18 +66,18 @@
return FALSE
return TRUE
-/datum/antagonist_ruleset/proc/post_setup()
+/datum/ruleset/proc/post_setup()
for(var/datum/mind/antag as anything in pre_antags)
antag.add_antag_datum(antagonist)
-/datum/antagonist_ruleset/proc/refund(info)
+/datum/ruleset/proc/refund(info)
// not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
// OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
stack_trace("[info] Refunding [cost*antag_amount] budget.")
stack_trace("REFUNDING NOT IMPLEMENTED!!")
// ctodo real refunding?
-/datum/antagonist_ruleset/traitor
+/datum/ruleset/traitor
name = "Traitor"
ruleset_weight = 13
cost = 5
@@ -89,7 +85,7 @@
antagonist = /datum/antagonist/traitor
mind_role = SPECIAL_ROLE_TRAITOR
-/datum/antagonist_ruleset/traitor/post_setup()
+/datum/ruleset/traitor/post_setup()
var/random_time = rand(5 MINUTES, 15 MINUTES)
for(var/datum/mind/antag as anything in pre_antags)
var/datum/antagonist/traitor/traitor_datum = new antagonist()
@@ -98,7 +94,7 @@
traitor_datum.addtimer(CALLBACK(traitor_datum, TYPE_PROC_REF(/datum/antagonist/traitor, reveal_delayed_objectives)), random_time, TIMER_DELETE_ME)
antag.add_antag_datum(traitor_datum)
-/datum/antagonist_ruleset/vampire
+/datum/ruleset/vampire
name = "Vampire"
ruleset_weight = 9
cost = 10
@@ -107,9 +103,9 @@
banned_jobs = list("Cyborg", "AI", "Chaplain")
banned_species = list("Machine")
- implied_ruleset = /datum/antagonist_ruleset/implied/mindflayer
+ implied_ruleset = /datum/ruleset/implied/mindflayer
-/datum/antagonist_ruleset/changeling
+/datum/ruleset/changeling
name = "Changeling"
ruleset_weight = 9
cost = 10
@@ -118,21 +114,22 @@
banned_jobs = list("Cyborg", "AI")
banned_species = list("Machine")
- implied_ruleset = /datum/antagonist_ruleset/implied/mindflayer
+ implied_ruleset = /datum/ruleset/implied/mindflayer
-/datum/antagonist_ruleset/implied
+// This is the fucking worst, but its required to not change functionality with mindflayers
+/datum/ruleset/implied
// cannot be rolled normally, this is applied by other methods.
ruleset_cost = 0
- var/ruleset_weight = 0
- var/cost = 0
- var/weight = 0
+ ruleset_weight = 0
+ cost = 0
+ weight = 0
// This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
var/target_signal
-/datum/antagonist_ruleset/implied/proc/on_implied(datum/antagonist/implier)
+/datum/ruleset/implied/proc/on_implied(datum/antagonist/implier)
return
-/datum/antagonist_ruleset/implied/mindflayer
+/datum/ruleset/implied/mindflayer
name = "Mindflayer"
antagonist = /datum/antagonist/mindflayer
mind_role = ROLE_MIND_FLAYER
@@ -142,9 +139,9 @@
banned_species = list("Machine")
banned_species_only = TRUE
-/datum/antagonist_ruleset/implied/mindflayer/on_implied(datum/antagonist/implier)
+/datum/ruleset/implied/mindflayer/on_implied(datum/ruleset/implier)
implier.antag_amount -= 1
antag_amount += 1
-// /datum/antagonist_ruleset/team
+// /datum/ruleset/team
// var/datum/team/team_type
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 07d1bc053907..b3fee6cf96b5 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -1,50 +1,74 @@
+GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
+
/datum/game_mode/dynamic
name = "Dynamic"
config_tag = "dynamic"
secondary_restricted_jobs = list("AI")
- required_players = 25
- var/list/datum/antagonist_ruleset/rulesets = list()
- var/list/datum/antagonist_ruleset/implied_rulesets = list()
+ required_players = 10
+ var/list/forced_rulesets = list()
+ var/list/datum/ruleset/rulesets = list()
+ var/list/datum/ruleset/implied_rulesets = list()
/datum/game_mode/dynamic/announce()
to_chat(world, "The current game mode is - Dynamic")
- to_chat(world, "There could be anything lurking in the shadows.")
+ var/list/possible_rulesets = list()
+ for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
+ if(ruleset.ruleset_weight <= 0)
+ continue
+ possible_rulesets |= ruleset
+ if(ruleset.implied_ruleset)
+ possible_rulesets += ruleset.implied_ruleset
+ to_chat(world, "Possible Rulesets: [english_list(possible_rulesets)]")
/datum/game_mode/dynamic/proc/allocate_gamemode_budget()
var/gamemode_budget = text2num(pickweight(list("0" = 3, "1" = 8, "2" = 9, "3" = 3))) // more likely to be 1 or 2, than 3 or 0.
if(gamemode_budget <= 0)
return
var/list/possible_rulesets = list()
- for(var/ruleset in subtypesof(/datum/antagonist_ruleset))
+ for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
if(ruleset.ruleset_weight <= 0)
continue
- var/datum/antagonist_ruleset/new_ruleset = new ruleset()
+ var/datum/ruleset/new_ruleset = new ruleset()
possible_rulesets[new_ruleset] = new_ruleset.ruleset_weight
+ for(var/datum/ruleset/ruleset as anything in GLOB.dynamic_forced_rulesets)
+ if(!ispath(ruleset, /datum/ruleset))
+ stack_trace("Non-ruleset in GLOB.dynamic_forced_rulesets: \"[ruleset]\" ([ruleset?.type])")
+ continue
+ gamemode_budget -= pick_ruleset(new ruleset, gamemode_budget, ignore_budget = TRUE)
+
while(gamemode_budget >= 0)
- var/datum/antagonist_ruleset/ruleset in pickweight(possible_rulesets)
+ var/datum/ruleset/ruleset = pickweight(possible_rulesets)
if(!ruleset)
return
- if(!ruleset.ruleset_possible(gamemode_budget))
- possible_rulesets -= ruleset
- continue
+ gamemode_budget -= pick_ruleset(ruleset, gamemode_budget)
+ possible_rulesets -= ruleset
- rulesets[ruleset] = ruleset.weight
- gamemode_budget -= ruleset.ruleset_cost
- if(!ruleset.implied_ruleset)
- continue
+/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, gamemode_budget, ignore_budget)
+ if(!ruleset)
+ return
+ if(!ruleset.ruleset_possible())
+ return
+ if(!ignore_budget && (gamemode_budget < ruleset.ruleset_cost))
+ return
+
+ rulesets[ruleset] = ruleset.weight
+ . = ruleset.ruleset_cost // return the ruleset cost to be subtracted from the gamemode budget
+ if(!ruleset.implied_ruleset)
+ return
- var/datum/antagonist_ruleset/implied = locate(ruleset.implied_ruleset) in implied_rulesets
- if(!implied)
- implied = new ruleset.implied_ruleset
- implied_rulesets += implied
- implied.RegisterSignal(ruleset, implied.target_signal, PROC_REF(on_implied))
+ var/datum/ruleset/implied/implied = locate(ruleset.implied_ruleset) in implied_rulesets
+ if(!implied)
+ implied = new ruleset.implied_ruleset
+ implied_rulesets += implied
+ implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, on_implied))
+ return
/datum/game_mode/dynamic/proc/allocate_antagonist_budget()
if(!length(rulesets))
return
var/budget = num_players()
- for(var/datum/antagonist_ruleset/ruleset in rulesets)
+ for(var/datum/ruleset/ruleset in rulesets)
ruleset.antag_amount = 1
budget -= ruleset.cost
@@ -53,7 +77,7 @@
/datum/game_mode/dynamic/proc/apply_antag_budget(budget) // ctodo, 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)
- var/datum/antagonist_ruleset/ruleset in pickweight(temp_rulesets)
+ var/datum/ruleset/ruleset = pickweight(temp_rulesets)
if(!ruleset)
return
if(!ruleset.antagonist_possible(budget))
@@ -66,14 +90,14 @@
allocate_gamemode_budget()
allocate_antagonist_budget()
var/budget_overflow = 0
- for(var/datum/antagonist_ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets
+ for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets
budget_overflow += ruleset.pre_setup()
apply_antag_budget(budget_overflow) // CTODO CURRENTLY, THIS INCREASES THE BUDGET AND DOES NOTHING!!!
return TRUE
/datum/game_mode/dynamic/post_setup()
- for(var/datum/antagonist_ruleset/ruleset in (rulesets + implied_rulesets))
+ for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets))
ruleset.post_setup()
..()
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index eb6c435f6a80..ee5dd5f23d66 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -355,6 +355,9 @@
if(SSticker && SSticker.current_state >= GAME_STATE_PLAYING)
var/dat = "
Round StatusRound Status
"
dat += "Current Game Mode: [SSticker.mode.name]
"
+ if(istype(SSticker.mode, /datum/game_mode/dynamic))
+ var/datum/game_mode/dynamic/dynamic = SSticker.mode
+ dat += "Rulesets: [english_list(dynamic.rulesets + dynamic.implied_rulesets)]
"
dat += "Round Duration: [round(ROUND_TIME / 36000)]:[add_zero(num2text(ROUND_TIME / 600 % 60), 2)]:[add_zero(num2text(ROUND_TIME / 10 % 60), 2)]
"
dat += "Emergency shuttle
"
if(SSshuttle.emergency.mode < SHUTTLE_CALL)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 7f96ef12ef46..b19c479dd4cf 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -71,7 +71,7 @@
log_admin("[key_name(usr)] has spawned mindflayers.")
if(!makeMindflayers())
to_chat(usr, "Unfortunately there weren't enough candidates available.")
-
+
else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"] || href_list["dbsearchbantype"])
var/adminckey = href_list["dbsearchadmin"]
var/playerckey = href_list["dbsearchckey"]
@@ -1034,6 +1034,8 @@
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "secret")
return alert(usr, "The game mode has to be secret!", null, null, null, null)
+ if(GLOB.master_mode != "dynamic")
+ return alert(usr, "The game mode has to be secret!", null, null, null, null)
var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
"}
for(var/mode in GLOB.configuration.gamemode.gamemodes)
dat += {"[GLOB.configuration.gamemode.gamemode_names[mode]]
"}
@@ -1041,6 +1043,19 @@
dat += {"Now: [GLOB.secret_force_mode]"}
usr << browse(dat, "window=f_secret")
+ else if(href_list["f_dynamic"])
+ if(!check_rights(R_ADMIN)) return
+
+ if(SSticker && SSticker.mode)
+ return alert(usr, "The game has already started.", null, null, null, null)
+ if(GLOB.master_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)
+ for(var/datum/ruleset/ruleset as anything in rulesets)
+ dat += {"[ruleset]: [(ruleset in GLOB.dynamic_forced_rulesets) ? "Forced" : "Normal"]
"}
+ usr << browse(dat, "window=f_secret")
+
else if(href_list["c_mode2"])
if(!check_rights(R_ADMIN|R_SERVER)) return
@@ -1067,6 +1082,25 @@
Game() // updates the main game menu
.(href, list("f_secret"=1))
+ else if(href_list["f_dynamic2"])
+ if(!check_rights(R_ADMIN|R_SERVER)) return
+
+ if(SSticker && SSticker.mode)
+ return alert(usr, "The game has already started.", null, null, null, null)
+ if(GLOB.master_mode != "dynamic")
+ return alert(usr, "The game mode has to be dynamic!", null, null, null, null)
+ var/datum/ruleset/ruleset = href_list["f_dynamic2"]
+ if(ruleset in GLOB.dynamic_forced_rulesets)
+ log_admin("[key_name(usr)] un-forced the [ruleset.type] ruleset.")
+ message_admins("[key_name_admin(usr)] un-forced the [ruleset.name] (ruleset.type) ruleset].")
+ GLOB.dynamic_forced_rulesets -= ruleset
+ else
+ log_admin("[key_name(usr)] forced the [ruleset.type] ruleset.")
+ message_admins("[key_name_admin(usr)] forced the [ruleset.name] (ruleset.type) ruleset].")
+ GLOB.dynamic_forced_rulesets += ruleset
+ Game() // updates the main game menu
+ .(href, list("f_dynamic"=1))
+
else if(href_list["monkeyone"])
if(!check_rights(R_SPAWN)) return
diff --git a/paradise.dme b/paradise.dme
index 9f597f0c4fcd..dd7f5a69359b 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -739,6 +739,8 @@
#include "code\game\gamemodes\cult\cult_structures.dm"
#include "code\game\gamemodes\cult\ritual.dm"
#include "code\game\gamemodes\cult\runes.dm"
+#include "code\game\gamemodes\dynamic\antag_rulesets.dm"
+#include "code\game\gamemodes\dynamic\dynamic.dm"
#include "code\game\gamemodes\extended\extended.dm"
#include "code\game\gamemodes\malfunction\Malf_Modules.dm"
#include "code\game\gamemodes\miniantags\abduction\abductee_objectives.dm"
From 9d39ee03c384fb2c4e81494463b1cdd3a2973307 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Mon, 9 Dec 2024 17:24:18 -0500
Subject: [PATCH 04/17] aaaa
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 4 ++--
code/game/gamemodes/dynamic/dynamic.dm | 18 +++++++++---------
config/example/config.toml | 15 ++++++++-------
3 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 31a47b3422e7..aa042261e144 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -79,7 +79,7 @@
/datum/ruleset/traitor
name = "Traitor"
- ruleset_weight = 13
+ ruleset_weight = 11
cost = 5
weight = 2
antagonist = /datum/antagonist/traitor
@@ -96,7 +96,7 @@
/datum/ruleset/vampire
name = "Vampire"
- ruleset_weight = 9
+ ruleset_weight = 12
cost = 10
antagonist = /datum/antagonist/vampire
mind_role = ROLE_VAMPIRE
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index b3fee6cf96b5..fd08b2284840 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -20,9 +20,9 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
possible_rulesets += ruleset.implied_ruleset
to_chat(world, "Possible Rulesets: [english_list(possible_rulesets)]")
-/datum/game_mode/dynamic/proc/allocate_gamemode_budget()
- var/gamemode_budget = text2num(pickweight(list("0" = 3, "1" = 8, "2" = 9, "3" = 3))) // more likely to be 1 or 2, than 3 or 0.
- if(gamemode_budget <= 0)
+/datum/game_mode/dynamic/proc/allocate_ruleset_budget()
+ var/ruleset_budget = text2num(pickweight(list("0" = 3, "1" = 5, "2" = 12, "3" = 3))) // more likely to or 2
+ if(ruleset_budget <= 0)
return
var/list/possible_rulesets = list()
for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
@@ -35,21 +35,21 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(!ispath(ruleset, /datum/ruleset))
stack_trace("Non-ruleset in GLOB.dynamic_forced_rulesets: \"[ruleset]\" ([ruleset?.type])")
continue
- gamemode_budget -= pick_ruleset(new ruleset, gamemode_budget, ignore_budget = TRUE)
+ ruleset_budget -= pick_ruleset(new ruleset, ruleset_budget, ignore_budget = TRUE)
- while(gamemode_budget >= 0)
+ while(ruleset_budget >= 0)
var/datum/ruleset/ruleset = pickweight(possible_rulesets)
if(!ruleset)
return
- gamemode_budget -= pick_ruleset(ruleset, gamemode_budget)
+ ruleset_budget -= pick_ruleset(ruleset, ruleset_budget)
possible_rulesets -= ruleset
-/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, gamemode_budget, ignore_budget)
+/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, ruleset_budget, ignore_budget)
if(!ruleset)
return
if(!ruleset.ruleset_possible())
return
- if(!ignore_budget && (gamemode_budget < ruleset.ruleset_cost))
+ if(!ignore_budget && (ruleset_budget < ruleset.ruleset_cost))
return
rulesets[ruleset] = ruleset.weight
@@ -87,7 +87,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
budget -= ruleset.cost
/datum/game_mode/dynamic/pre_setup()
- allocate_gamemode_budget()
+ allocate_ruleset_budget()
allocate_antagonist_budget()
var/budget_overflow = 0
for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets
diff --git a/config/example/config.toml b/config/example/config.toml
index dbbd2398b41e..3d84628395db 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -263,17 +263,18 @@ gamemode_probabilities = [
{ gamemode = "changeling", probability = 0 },
{ gamemode = "cult", probability = 3 },
{ gamemode = "extend-a-traitormongous", probability = 2 }, # Autotraitor
- { gamemode = "extended", probability = 3 },
+ { gamemode = "extended", probability = 0 },
{ gamemode = "nuclear", probability = 2 },
{ gamemode = "raginmages", probability = 0 },
{ gamemode = "revolution", probability = 0 },
- { gamemode = "traitor", probability = 2 },
- { gamemode = "traitorchan", probability = 3 },
- { gamemode = "traitorvamp", probability = 3 },
- { gamemode = "vampchan", probability = 3},
- { gamemode = "vampire", probability = 3 },
+ { gamemode = "traitor", probability = 0 },
+ { gamemode = "traitorchan", probability = 0 },
+ { gamemode = "traitorvamp", probability = 0 },
+ { gamemode = "vampchan", probability = 0 },
+ { gamemode = "vampire", probability = 0 },
{ gamemode = "wizard", probability = 2 },
- { gamemode = "trifecta", probability = 3 },
+ { gamemode = "trifecta", probability = 0 },
+ { gamemode = "dynamic", probability = 20 },
]
# Do we want the amount of traitors to scale with population?
traitor_scaling = true
From 5ab99b96e4e83a2b0b680d337b5cbb0243d58441 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Mon, 9 Dec 2024 19:01:37 -0500
Subject: [PATCH 05/17] fix
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index aa042261e144..791fdeded1a4 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -12,11 +12,11 @@
var/datum/antagonist/antagonist
var/mind_role
- // these roles 100% cannot be this antagonist
+ /// These roles 100% cannot be this antagonist
var/list/banned_jobs = list("Cyborg")
- // These roles can't be antagonists because mindshielding (this can be disabled via config)
+ /// These roles can't be antagonists because mindshielding (this can be disabled via config)
var/list/protected_jobs = MINDSHIELDED_JOBS
- // Applies the mind roll to assigned_role, preventing them from rolling a normal job. Good for wizards and nuclear operatives.
+ /// Applies the mind roll to assigned_role, preventing them from rolling a normal job. Good for wizards and nuclear operatives.
var/assign_job_role = FALSE
var/list/banned_species = list()
@@ -143,5 +143,4 @@
implier.antag_amount -= 1
antag_amount += 1
-// /datum/ruleset/team
-// var/datum/team/team_type
+#undef
From 0ec6f7c4d827bfb4ba1aa70670f4d0deb60947e2 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Sun, 22 Dec 2024 12:54:00 -0700
Subject: [PATCH 06/17] im going to have a fucking stroke i hate mindflayers
---
code/__DEFINES/gamemode.dm | 7 ++
code/__HELPERS/_logging.dm | 5 ++
code/game/gamemodes/dynamic/antag_rulesets.dm | 72 +++++++++++-----
code/game/gamemodes/dynamic/dynamic.dm | 83 ++++++++++++-------
code/modules/admin/misc_admin_procs.dm | 2 +
code/modules/admin/topic.dm | 44 +++++++---
.../antagonists/mind_flayer/flayer_datum.dm | 3 +-
.../antagonists/traitor/datum_mindslave.dm | 2 +-
.../modules/antagonists/vampire/vamp_datum.dm | 3 +-
9 files changed, 153 insertions(+), 68 deletions(-)
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index 2c9ef8563d63..350b3cf89ba5 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -71,3 +71,10 @@
#define NUKE_SITE_OFF_STATION_ZLEVEL 2
/// The bomb's location cannot be found.
#define NUKE_SITE_INVALID 3
+
+/**
+ * Dynamic Gamemode Defines
+ */
+#define DYNAMIC_RULESET_NORMAL "Normal"
+#define DYNAMIC_RULESET_FORCED "Forced"
+#define DYNAMIC_RULESET_BANNED "Banned"
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 7fc2bee5074c..e8754e90e699 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -43,6 +43,11 @@ GLOBAL_PROTECT(log_end)
if(check_rights(R_DEBUG | R_VIEWRUNTIMES, FALSE, C.mob) && (C.prefs.toggles & PREFTOGGLE_CHAT_DEBUGLOGS))
to_chat(C, "DEBUG: [text]", MESSAGE_TYPE_DEBUG, confidential = TRUE)
+/proc/log_dynamic(text) // CTODO, REMOVE
+ for(var/client/C in GLOB.admins)
+ if(check_rights(R_DEBUG, FALSE, C.mob) && (C.prefs.toggles & PREFTOGGLE_CHAT_DEBUGLOGS))
+ to_chat(C, "DYNAMIC: [text]", MESSAGE_TYPE_DEBUG, confidential = TRUE)
+
/proc/log_game(text)
if(GLOB.configuration.logging.game_logging)
rustg_log_write(GLOB.world_game_log, "GAME: [text][GLOB.log_end]")
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 791fdeded1a4..724e95fea899 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -1,16 +1,20 @@
#define MINDSHIELDED_JOBS list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General")
/datum/ruleset
+ /// What this ruleset is called
var/name = "BASE RULESET"
+ /// The cost to roll this ruleset
var/ruleset_cost = 1
+ /// The weight to roll this ruleset
var/ruleset_weight = 1
+ /// The cost to roll an antagonist of this ruleset
var/cost = 1
+ /// The weight to roll an antagonist of this ruleset
var/weight = 1
- // var/list/banned_mutual_rulesets = list() // could be used to prevent nukies rolling while theres cultists, or wizards, etc
- var/datum/ruleset/implied/implied_ruleset
-
+ /// Antagonist datum to apply to users
var/datum/antagonist/antagonist
- var/mind_role
+ /// A ruleset to be added when this ruleset is selected by the gamemode
+ var/datum/ruleset/implied/implied_ruleset
/// These roles 100% cannot be this antagonist
var/list/banned_jobs = list("Cyborg")
@@ -18,23 +22,35 @@
var/list/protected_jobs = MINDSHIELDED_JOBS
/// Applies the mind roll to assigned_role, preventing them from rolling a normal job. Good for wizards and nuclear operatives.
var/assign_job_role = FALSE
-
+ /// A blacklist of species names that cannot play this antagonist
var/list/banned_species = list()
+ /// 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
+
/* This stuff changes, all stuff above is static */
+ /// How many antagonists to spawn
var/antag_amount = 0
+ /// All of the minds that we will make into our antagonist type
var/list/datum/mind/pre_antags = list()
-
-/datum/ruleset/proc/ruleset_possible()
- return length(SSticker.mode.get_players_for_role(mind_role))
+/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets)
+ if(ruleset_budget < ruleset_cost)
+ return "Not enough budget"
+ if(!length(SSticker.mode.get_players_for_role(antagonist::job_rank))) // this specifically needs to be job_rank not special_rank
+ return "No drafted players"
/datum/ruleset/proc/antagonist_possible(budget)
return budget >= cost
/datum/ruleset/proc/pre_setup()
- var/list/datum/mind/possible_antags = SSticker.mode.get_players_for_role(mind_role)
+ if(antag_amount == 0)
+ return
+ if(antag_amount < 0)
+ stack_trace("/datum/ruleset/proc/pre_setup() for [type] somehow had a negative antagonist amount")
+ return
+ var/list/datum/mind/possible_antags = SSticker.mode.get_players_for_role(antagonist::job_rank) // this specifically needs to be job_rank not special_rank
if(!length(possible_antags))
refund("No possible players for [src] ruleset.") // we allocate antag budget before we allocate players, and previous rulesets can steal our players
return cost*antag_amount // shitty refund for now
@@ -49,8 +65,8 @@
continue
pre_antags += antag
if(assign_job_role)
- antag.assigned_role = mind_role
- antag.special_role = mind_role
+ antag.assigned_role = antagonist::special_role
+ antag.special_role = antagonist::special_role
antag.restricted_roles = banned_jobs
antag_amount -= 1
@@ -73,8 +89,8 @@
/datum/ruleset/proc/refund(info)
// not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
// OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
- stack_trace("[info] Refunding [cost*antag_amount] budget.")
- stack_trace("REFUNDING NOT IMPLEMENTED!!")
+ log_dynamic("[info] Refunding [cost*antag_amount] budget.")
+ log_dynamic("REFUNDING NOT IMPLEMENTED!!")
// ctodo real refunding?
/datum/ruleset/traitor
@@ -83,7 +99,6 @@
cost = 5
weight = 2
antagonist = /datum/antagonist/traitor
- mind_role = SPECIAL_ROLE_TRAITOR
/datum/ruleset/traitor/post_setup()
var/random_time = rand(5 MINUTES, 15 MINUTES)
@@ -99,7 +114,6 @@
ruleset_weight = 12
cost = 10
antagonist = /datum/antagonist/vampire
- mind_role = ROLE_VAMPIRE
banned_jobs = list("Cyborg", "AI", "Chaplain")
banned_species = list("Machine")
@@ -110,29 +124,42 @@
ruleset_weight = 9
cost = 10
antagonist = /datum/antagonist/changeling
- mind_role = ROLE_CHANGELING
banned_jobs = list("Cyborg", "AI")
banned_species = list("Machine")
implied_ruleset = /datum/ruleset/implied/mindflayer
-// This is the fucking worst, but its required to not change functionality with mindflayers
+/datum/ruleset/changeling/ruleset_possible(ruleset_budget, rulesets)
+ // Theres already a ruleset, we're good to go
+ if(length(rulesets))
+ return ..()
+ // We're the first ruleset, but we can afford another ruleset
+ if((ruleset_budget >= /datum/ruleset/traitor::ruleset_cost) || (ruleset_budget >= /datum/ruleset/vampire::ruleset_cost))
+ return ..()
+ return "Needs a secondary ruleset in rotation"
+
+// 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
- // cannot be rolled normally, this is applied by other methods.
+ // These 3 variables should never change
ruleset_cost = 0
ruleset_weight = 0
- cost = 0
weight = 0
+ // cost is allowed to be edited to help with refunding antagonists
+ cost = 0
// This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
var/target_signal
+/datum/ruleset/implied/proc/handle_signal(datum/antagonist/implier)
+ stack_trace("[type]/on_implied() wrugv9nwefguinwegujibner!")
+ on_implied()
+
/datum/ruleset/implied/proc/on_implied(datum/antagonist/implier)
- return
+ stack_trace("[type]/on_implied() not implemented!")
/datum/ruleset/implied/mindflayer
name = "Mindflayer"
antagonist = /datum/antagonist/mindflayer
- mind_role = ROLE_MIND_FLAYER
+ cost = 10
target_signal = COMSIG_RULESET_FAILED_SPECIES
banned_jobs = list("Cyborg", "AI")
@@ -140,7 +167,8 @@
banned_species_only = TRUE
/datum/ruleset/implied/mindflayer/on_implied(datum/ruleset/implier)
+ log_dynamic("Rolled implied [name]: +1 [name], -1 [implier.name].")
implier.antag_amount -= 1
antag_amount += 1
-#undef
+#undef MINDSHIELDED_JOBS
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index fd08b2284840..4c9b3d33e70b 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -15,43 +15,59 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
if(ruleset.ruleset_weight <= 0)
continue
- possible_rulesets |= ruleset
+ possible_rulesets |= ruleset.name
if(ruleset.implied_ruleset)
- possible_rulesets += ruleset.implied_ruleset
+ possible_rulesets |= ruleset.implied_ruleset.name
to_chat(world, "Possible Rulesets: [english_list(possible_rulesets)]")
/datum/game_mode/dynamic/proc/allocate_ruleset_budget()
- var/ruleset_budget = text2num(pickweight(list("0" = 3, "1" = 5, "2" = 12, "3" = 3))) // more likely to or 2
- if(ruleset_budget <= 0)
- return
+ var/ruleset_budget = text2num(GLOB.dynamic_forced_rulesets["budget"] || pickweight(list("0" = 3, "1" = 5, "2" = 12, "3" = 3))) // more likely to or 2
+ log_dynamic("Allocated gamemode budget: [ruleset_budget]")
var/list/possible_rulesets = list()
for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
if(ruleset.ruleset_weight <= 0)
continue
+ if(GLOB.dynamic_forced_rulesets[ruleset] == DYNAMIC_RULESET_BANNED)
+ continue
var/datum/ruleset/new_ruleset = new ruleset()
possible_rulesets[new_ruleset] = new_ruleset.ruleset_weight
+ log_dynamic("Available rulesets: [english_list(possible_rulesets)]")
+
for(var/datum/ruleset/ruleset as anything in GLOB.dynamic_forced_rulesets)
+ if(ruleset == "budget")
+ continue
+ if(GLOB.dynamic_forced_rulesets[ruleset] != DYNAMIC_RULESET_FORCED)
+ continue
if(!ispath(ruleset, /datum/ruleset))
stack_trace("Non-ruleset in GLOB.dynamic_forced_rulesets: \"[ruleset]\" ([ruleset?.type])")
continue
- ruleset_budget -= pick_ruleset(new ruleset, ruleset_budget, ignore_budget = TRUE)
+ log_dynamic("Forcing ruleset: [ruleset.name]")
+ ruleset_budget -= pick_ruleset(new ruleset, ruleset_budget, force = TRUE)
+ for(var/datum/ruleset/old_ruleset in possible_rulesets)
+ if(old_ruleset.type == ruleset)
+ possible_rulesets -= old_ruleset
+ qdel(old_ruleset)
while(ruleset_budget >= 0)
var/datum/ruleset/ruleset = pickweight(possible_rulesets)
if(!ruleset)
+ log_dynamic("No more available rulesets")
return
ruleset_budget -= pick_ruleset(ruleset, ruleset_budget)
possible_rulesets -= ruleset
+ qdel(ruleset)
+ log_dynamic("No more ruleset budget")
-/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, ruleset_budget, ignore_budget)
+/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, ruleset_budget, force)
if(!ruleset)
return
- if(!ruleset.ruleset_possible())
- return
- if(!ignore_budget && (ruleset_budget < ruleset.ruleset_cost))
- return
-
+ if(!force)
+ var/failure_reason = ruleset.ruleset_possible(ruleset_budget, rulesets)
+ if(failure_reason)
+ log_dynamic("Failed [ruleset.name] ruleset: [failure_reason]")
+ return
+ log_dynamic("Rolled ruleset: [ruleset.name]")
rulesets[ruleset] = ruleset.weight
. = ruleset.ruleset_cost // return the ruleset cost to be subtracted from the gamemode budget
if(!ruleset.implied_ruleset)
@@ -59,18 +75,25 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/datum/ruleset/implied/implied = locate(ruleset.implied_ruleset) in implied_rulesets
if(!implied)
+ log_dynamic("Adding implied ruleset: [ruleset.implied_ruleset.name]")
implied = new ruleset.implied_ruleset
implied_rulesets += implied
- implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, on_implied))
- return
+ implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, handle_signal))
+ // ruleset.RegisterSignal(implied, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, handle_signal)) // ctodo remove this line?
/datum/game_mode/dynamic/proc/allocate_antagonist_budget()
if(!length(rulesets))
+ log_dynamic("No rulesets in play.")
return
var/budget = num_players()
+ log_dynamic("Allocated antagonist budget: [budget].")
+
for(var/datum/ruleset/ruleset in rulesets)
ruleset.antag_amount = 1
budget -= ruleset.cost
+ log_dynamic("Automatic deduction: +1 [ruleset.name]. Remaining budget: [budget].")
+
+ log_dynamic("Rulesets in play: [english_list((rulesets + implied_rulesets))]")
apply_antag_budget(budget)
@@ -79,42 +102,40 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
while(budget >= 0)
var/datum/ruleset/ruleset = pickweight(temp_rulesets)
if(!ruleset)
+ log_dynamic("No rulesets remaining. Remaining budget: [budget].")
return
if(!ruleset.antagonist_possible(budget))
+ log_dynamic("Rolled [ruleset.name]: failed, removing [ruleset.name] ruleset.")
temp_rulesets -= ruleset
continue
+ log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].")
ruleset.antag_amount++
budget -= ruleset.cost
+ log_dynamic("No more antagonist budget remaining.")
/datum/game_mode/dynamic/pre_setup()
+ var/watch = start_watch()
+ log_dynamic("Starting dynamic setup.")
allocate_ruleset_budget()
+ log_dynamic("-=-=-=-=-=-=-=-=-=-=-=-=-")
allocate_antagonist_budget()
+ log_dynamic("=-=-=-=-=-=-=-=-=-=-=-=-=")
var/budget_overflow = 0
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.pre_setup()
- apply_antag_budget(budget_overflow) // CTODO CURRENTLY, THIS INCREASES THE BUDGET AND DOES NOTHING!!!
+ log_dynamic("Budget overflow: [budget_overflow].")
+ // for the future, maybe try readding antagonists with apply_antag_budget(budget_overflow)
+ log_dynamic("Finished dynamic setup in [stop_watch(watch)]s")
return TRUE
/datum/game_mode/dynamic/post_setup()
for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets))
+ if(length(ruleset.pre_antags))
+ log_dynamic("Making antag datums for [ruleset.name] ruleset.")
ruleset.post_setup()
..()
// /datum/game_mode/dynamic/traitors_to_add()
-// . = 0
-// for(var/datum/mind/traitor_mind as anything in traitors)
-// if(!QDELETED(traitor_mind) && traitor_mind.current) // Explicitly no client check in case you happen to fall SSD when this gets ran
-// continue
-// .++
-// traitors -= traitor_mind
-
-// var/extra_points = num_players_started() - cost_at_roundstart
-// if(extra_points - TOT_COST < 0)
-// return 0 // Not enough new players to add extra tots
-
-// while(extra_points)
-// .++
-// if(extra_points < TOT_COST) // The leftover change is enough for us to buy another traitor with, what a deal!
-// return
-// extra_points -= TOT_COST
+// should probably implement this in some form
diff --git a/code/modules/admin/misc_admin_procs.dm b/code/modules/admin/misc_admin_procs.dm
index afec64f26193..c0f75b8ab491 100644
--- a/code/modules/admin/misc_admin_procs.dm
+++ b/code/modules/admin/misc_admin_procs.dm
@@ -327,6 +327,8 @@ GLOBAL_VAR_INIT(disable_explosions, FALSE)
dat += "Change Game Mode
"
if(GLOB.master_mode == "secret")
dat += "(Force Secret Mode)
"
+ if(GLOB.master_mode == "dynamic")
+ dat += "(Force Dynamic Rulesets)
"
dat += "
"
dat += "Create Object
"
dat += "Quick Create Object
"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 24e893c9ce5a..82d51ac4504e 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1052,9 +1052,10 @@
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)
+ 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]: [(ruleset in GLOB.dynamic_forced_rulesets) ? "Forced" : "Normal"]
"}
- usr << browse(dat, "window=f_secret")
+ dat += {"[ruleset.name]: [GLOB.dynamic_forced_rulesets[ruleset] || DYNAMIC_RULESET_NORMAL]
"}
+ usr << browse(dat, "window=f_dynamic")
else if(href_list["c_mode2"])
if(!check_rights(R_ADMIN|R_SERVER)) return
@@ -1089,16 +1090,35 @@
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic!", null, null, null, null)
- var/datum/ruleset/ruleset = href_list["f_dynamic2"]
- if(ruleset in GLOB.dynamic_forced_rulesets)
- log_admin("[key_name(usr)] un-forced the [ruleset.type] ruleset.")
- message_admins("[key_name_admin(usr)] un-forced the [ruleset.name] (ruleset.type) ruleset].")
- GLOB.dynamic_forced_rulesets -= ruleset
- else
- log_admin("[key_name(usr)] forced the [ruleset.type] ruleset.")
- message_admins("[key_name_admin(usr)] forced the [ruleset.name] (ruleset.type) ruleset].")
- GLOB.dynamic_forced_rulesets += ruleset
- Game() // updates the main game menu
+ if(href_list["f_dynamic2"] == "budget")
+ var/budget = input(usr, "Pick a budget for the dynamic gamemode (-1 to randomize)", "Gamemode Budget") as num|null
+ if(isnull(budget))
+ return
+ if(budget < 0)
+ GLOB.dynamic_forced_rulesets -= "budget"
+ log_admin("[key_name(usr)] set the ruleset budget to random.")
+ message_admins("[key_name_admin(usr)] set the ruleset budget to random.")
+ else
+ GLOB.dynamic_forced_rulesets["budget"] = budget
+ log_admin("[key_name(usr)] set the ruleset budget to [budget]")
+ message_admins("[key_name_admin(usr)] set the ruleset budget to [budget].")
+ .(href, list("f_dynamic"=1))
+ return
+
+ var/datum/ruleset/ruleset = text2path(href_list["f_dynamic2"])
+ switch(GLOB.dynamic_forced_rulesets[ruleset])
+ if(DYNAMIC_RULESET_FORCED)
+ log_admin("[key_name(usr)] banned the [ruleset] ruleset.")
+ message_admins("[key_name_admin(usr)] banned the [ruleset.name] ([ruleset.type]) ruleset.")
+ GLOB.dynamic_forced_rulesets[ruleset] = DYNAMIC_RULESET_BANNED
+ if(DYNAMIC_RULESET_BANNED)
+ log_admin("[key_name(usr)] set the [ruleset] ruleset to normal.")
+ message_admins("[key_name_admin(usr)] set the [ruleset.name] ([ruleset.type]) ruleset to normal.")
+ GLOB.dynamic_forced_rulesets[ruleset] = DYNAMIC_RULESET_NORMAL
+ else // not set, and already at normal
+ log_admin("[key_name(usr)] forced the [ruleset] ruleset.")
+ message_admins("[key_name_admin(usr)] forced the [ruleset.name] ([ruleset.type]) ruleset.")
+ GLOB.dynamic_forced_rulesets[ruleset] = DYNAMIC_RULESET_FORCED
.(href, list("f_dynamic"=1))
else if(href_list["monkeyone"])
diff --git a/code/modules/antagonists/mind_flayer/flayer_datum.dm b/code/modules/antagonists/mind_flayer/flayer_datum.dm
index 637bcd1d7884..d8ec8a391496 100644
--- a/code/modules/antagonists/mind_flayer/flayer_datum.dm
+++ b/code/modules/antagonists/mind_flayer/flayer_datum.dm
@@ -1,8 +1,9 @@
/datum/antagonist/mindflayer
name = "Mindflayer"
+ job_rank = ROLE_MIND_FLAYER
+ special_role = SPECIAL_ROLE_MIND_FLAYER
antag_hud_type = ANTAG_HUD_MIND_FLAYER
antag_hud_name = "hudflayer"
- special_role = SPECIAL_ROLE_MIND_FLAYER
wiki_page_name = "Mindflayer"
/// The current amount of swarms the mind flayer has access to purchase with
var/usable_swarms = 0
diff --git a/code/modules/antagonists/traitor/datum_mindslave.dm b/code/modules/antagonists/traitor/datum_mindslave.dm
index 09beb78be3ca..0e9f58ed6c3b 100644
--- a/code/modules/antagonists/traitor/datum_mindslave.dm
+++ b/code/modules/antagonists/traitor/datum_mindslave.dm
@@ -4,7 +4,7 @@ RESTRICT_TYPE(/datum/antagonist/mindslave)
/datum/antagonist/mindslave
name = "Mindslave"
roundend_category = "mindslaves"
- job_rank = SPECIAL_ROLE_TRAITOR
+ job_rank = ROLE_TRAITOR
special_role = SPECIAL_ROLE_TRAITOR
antag_hud_type = ANTAG_HUD_TRAITOR
antag_hud_name = "mindslave" // This isn't named "hudmindslave" because `add_serve_hud()` adds "hud" to the beginning.
diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm
index 3fe4dcc76609..24f6ed464fd9 100644
--- a/code/modules/antagonists/vampire/vamp_datum.dm
+++ b/code/modules/antagonists/vampire/vamp_datum.dm
@@ -2,9 +2,10 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
/datum/antagonist/vampire
name = "Vampire"
+ job_rank = ROLE_VAMPIRE
+ special_role = SPECIAL_ROLE_VAMPIRE
antag_hud_type = ANTAG_HUD_VAMPIRE
antag_hud_name = "hudvampire"
- special_role = SPECIAL_ROLE_VAMPIRE
wiki_page_name = "Vampire"
var/bloodtotal = 0
var/bloodusable = 0
From 37deed70a919246cdc2acbd03d55aba0471611bf Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Sun, 22 Dec 2024 15:14:38 -0700
Subject: [PATCH 07/17] dynamic is ready
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 3 ---
code/game/gamemodes/dynamic/dynamic.dm | 4 +---
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 724e95fea899..8d9b87cc1e1e 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -149,9 +149,6 @@
// This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
var/target_signal
-/datum/ruleset/implied/proc/handle_signal(datum/antagonist/implier)
- stack_trace("[type]/on_implied() wrugv9nwefguinwegujibner!")
- on_implied()
/datum/ruleset/implied/proc/on_implied(datum/antagonist/implier)
stack_trace("[type]/on_implied() not implemented!")
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 4c9b3d33e70b..f6847c090a87 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -56,7 +56,6 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
return
ruleset_budget -= pick_ruleset(ruleset, ruleset_budget)
possible_rulesets -= ruleset
- qdel(ruleset)
log_dynamic("No more ruleset budget")
/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, ruleset_budget, force)
@@ -78,8 +77,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
log_dynamic("Adding implied ruleset: [ruleset.implied_ruleset.name]")
implied = new ruleset.implied_ruleset
implied_rulesets += implied
- implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, handle_signal))
- // ruleset.RegisterSignal(implied, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, handle_signal)) // ctodo remove this line?
+ implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, on_implied))
/datum/game_mode/dynamic/proc/allocate_antagonist_budget()
if(!length(rulesets))
From 38e28983926f0095c0ee1878743f1f756023f110 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Sun, 22 Dec 2024 16:29:12 -0700
Subject: [PATCH 08/17] fucking hell
---
code/modules/admin/topic.dm | 2 --
1 file changed, 2 deletions(-)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 82d51ac4504e..cc163ace3046 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1034,8 +1034,6 @@
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "secret")
return alert(usr, "The game mode has to be secret!", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be secret!", null, null, null, null)
var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
"}
for(var/mode in GLOB.configuration.gamemode.gamemodes)
dat += {"[GLOB.configuration.gamemode.gamemode_names[mode]]
"}
From 81aa6278a48f1b7ef4e052303f70eb4c978f550b Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Sun, 22 Dec 2024 20:39:02 -0700
Subject: [PATCH 09/17] yes
---
code/game/gamemodes/dynamic/dynamic.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index f6847c090a87..04404fc7f5ec 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -106,9 +106,9 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
log_dynamic("Rolled [ruleset.name]: failed, removing [ruleset.name] ruleset.")
temp_rulesets -= ruleset
continue
- log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].")
ruleset.antag_amount++
budget -= ruleset.cost
+ log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].")
log_dynamic("No more antagonist budget remaining.")
/datum/game_mode/dynamic/pre_setup()
From e24f44e965c3e1d92bc32bc7f6393e09b4c08cc9 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Wed, 1 Jan 2025 09:56:58 -0700
Subject: [PATCH 10/17] warrior review
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 79 +++++++++++--------
code/game/gamemodes/dynamic/dynamic.dm | 18 ++---
2 files changed, 54 insertions(+), 43 deletions(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 8d9b87cc1e1e..b7092760aa64 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -1,5 +1,6 @@
-#define MINDSHIELDED_JOBS list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Trans-Solar Federation General")
-
+/**
+ * These are gamemode rulesets for the dynamic gamemode type. They determine what antagonists spawn during a round.
+ */
/datum/ruleset
/// What this ruleset is called
var/name = "BASE RULESET"
@@ -8,18 +9,30 @@
/// The weight to roll this ruleset
var/ruleset_weight = 1
/// The cost to roll an antagonist of this ruleset
- var/cost = 1
+ var/antag_cost = 1
/// The weight to roll an antagonist of this ruleset
- var/weight = 1
+ var/antag_weight = 1
/// Antagonist datum to apply to users
- var/datum/antagonist/antagonist
+ var/datum/antagonist/antagonist_type
/// A ruleset to be added when this ruleset is selected by the gamemode
- var/datum/ruleset/implied/implied_ruleset
+ var/datum/ruleset/implied/implied_ruleset_type
/// These roles 100% cannot be this antagonist
var/list/banned_jobs = list("Cyborg")
/// These roles can't be antagonists because mindshielding (this can be disabled via config)
- var/list/protected_jobs = MINDSHIELDED_JOBS
+ var/list/protected_jobs = list(
+ "Security Officer",
+ "Warden",
+ "Detective",
+ "Head of Security",
+ "Captain", "Blueshield",
+ "Nanotrasen Representative",
+ "Magistrate",
+ "Internal Affairs Agent",
+ "Nanotrasen Navy Officer",
+ "Special Operations Officer",
+ "Trans-Solar Federation General"
+ )
/// Applies the mind roll to assigned_role, preventing them from rolling a normal job. Good for wizards and nuclear operatives.
var/assign_job_role = FALSE
/// A blacklist of species names that cannot play this antagonist
@@ -38,11 +51,11 @@
/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets)
if(ruleset_budget < ruleset_cost)
return "Not enough budget"
- if(!length(SSticker.mode.get_players_for_role(antagonist::job_rank))) // this specifically needs to be job_rank not special_rank
+ if(!length(SSticker.mode.get_players_for_role(antagonist_type::job_rank))) // this specifically needs to be job_rank not special_rank
return "No drafted players"
/datum/ruleset/proc/antagonist_possible(budget)
- return budget >= cost
+ return budget >= antag_cost
/datum/ruleset/proc/pre_setup()
if(antag_amount == 0)
@@ -50,29 +63,29 @@
if(antag_amount < 0)
stack_trace("/datum/ruleset/proc/pre_setup() for [type] somehow had a negative antagonist amount")
return
- var/list/datum/mind/possible_antags = SSticker.mode.get_players_for_role(antagonist::job_rank) // this specifically needs to be job_rank not special_rank
+ var/list/datum/mind/possible_antags = SSticker.mode.get_players_for_role(antagonist_type::job_rank) // this specifically needs to be job_rank not special_rank
if(!length(possible_antags))
refund("No possible players for [src] ruleset.") // we allocate antag budget before we allocate players, and previous rulesets can steal our players
- return cost*antag_amount // shitty refund for now
+ return antag_cost * antag_amount // shitty refund for now
if(GLOB.configuration.gamemode.prevent_mindshield_antags)
banned_jobs += protected_jobs
- for(var/datum/mind/antag as anything in shuffle(possible_antags))
+ for(var/datum/mind/antag as anything in shuffle_inplace(possible_antags))
if(antag_amount <= 0)
break
if(!can_apply(antag))
continue
pre_antags += antag
if(assign_job_role)
- antag.assigned_role = antagonist::special_role
- antag.special_role = antagonist::special_role
+ antag.assigned_role = antagonist_type::special_role
+ antag.special_role = antagonist_type::special_role
antag.restricted_roles = banned_jobs
antag_amount -= 1
if(antag_amount > 0)
refund("Missing [antag_amount] antagonists for [src] ruleset.")
- return cost*antag_amount // shitty refund for now
+ return antag_cost * antag_amount // shitty refund for now
/datum/ruleset/proc/can_apply(datum/mind/antag)
if(EXCLUSIVE_OR(antag.current.client.prefs.active_character.species in banned_species, banned_species_only))
@@ -84,26 +97,26 @@
/datum/ruleset/proc/post_setup()
for(var/datum/mind/antag as anything in pre_antags)
- antag.add_antag_datum(antagonist)
+ antag.add_antag_datum(antagonist_type)
/datum/ruleset/proc/refund(info)
// not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
// OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
- log_dynamic("[info] Refunding [cost*antag_amount] budget.")
+ log_dynamic("[info] Refunding [antag_cost * antag_amount] budget.")
log_dynamic("REFUNDING NOT IMPLEMENTED!!")
// ctodo real refunding?
/datum/ruleset/traitor
name = "Traitor"
ruleset_weight = 11
- cost = 5
- weight = 2
- antagonist = /datum/antagonist/traitor
+ antag_cost = 5
+ antag_weight = 2
+ antagonist_type = /datum/antagonist/traitor
/datum/ruleset/traitor/post_setup()
var/random_time = rand(5 MINUTES, 15 MINUTES)
for(var/datum/mind/antag as anything in pre_antags)
- var/datum/antagonist/traitor/traitor_datum = new antagonist()
+ var/datum/antagonist/traitor/traitor_datum = new antagonist_type()
if(ishuman(antag.current))
traitor_datum.delayed_objectives = TRUE
traitor_datum.addtimer(CALLBACK(traitor_datum, TYPE_PROC_REF(/datum/antagonist/traitor, reveal_delayed_objectives)), random_time, TIMER_DELETE_ME)
@@ -112,22 +125,22 @@
/datum/ruleset/vampire
name = "Vampire"
ruleset_weight = 12
- cost = 10
- antagonist = /datum/antagonist/vampire
+ antag_cost = 10
+ antagonist_type = /datum/antagonist/vampire
banned_jobs = list("Cyborg", "AI", "Chaplain")
banned_species = list("Machine")
- implied_ruleset = /datum/ruleset/implied/mindflayer
+ implied_ruleset_type = /datum/ruleset/implied/mindflayer
/datum/ruleset/changeling
name = "Changeling"
ruleset_weight = 9
- cost = 10
- antagonist = /datum/antagonist/changeling
+ antag_cost = 10
+ antagonist_type = /datum/antagonist/changeling
banned_jobs = list("Cyborg", "AI")
banned_species = list("Machine")
- implied_ruleset = /datum/ruleset/implied/mindflayer
+ implied_ruleset_type = /datum/ruleset/implied/mindflayer
/datum/ruleset/changeling/ruleset_possible(ruleset_budget, rulesets)
// Theres already a ruleset, we're good to go
@@ -143,9 +156,9 @@
// These 3 variables should never change
ruleset_cost = 0
ruleset_weight = 0
- weight = 0
- // cost is allowed to be edited to help with refunding antagonists
- cost = 0
+ antag_weight = 0
+ // antag_cost is allowed to be edited to help with refunding antagonists
+ antag_cost = 0
// This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
var/target_signal
@@ -155,8 +168,8 @@
/datum/ruleset/implied/mindflayer
name = "Mindflayer"
- antagonist = /datum/antagonist/mindflayer
- cost = 10
+ antagonist_type = /datum/antagonist/mindflayer
+ antag_cost = 10
target_signal = COMSIG_RULESET_FAILED_SPECIES
banned_jobs = list("Cyborg", "AI")
@@ -167,5 +180,3 @@
log_dynamic("Rolled implied [name]: +1 [name], -1 [implier.name].")
implier.antag_amount -= 1
antag_amount += 1
-
-#undef MINDSHIELDED_JOBS
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 04404fc7f5ec..ae54b4a7e2d4 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -16,8 +16,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(ruleset.ruleset_weight <= 0)
continue
possible_rulesets |= ruleset.name
- if(ruleset.implied_ruleset)
- possible_rulesets |= ruleset.implied_ruleset.name
+ if(ruleset.implied_ruleset_type)
+ possible_rulesets |= ruleset.implied_ruleset_type.name
to_chat(world, "Possible Rulesets: [english_list(possible_rulesets)]")
/datum/game_mode/dynamic/proc/allocate_ruleset_budget()
@@ -67,15 +67,15 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
log_dynamic("Failed [ruleset.name] ruleset: [failure_reason]")
return
log_dynamic("Rolled ruleset: [ruleset.name]")
- rulesets[ruleset] = ruleset.weight
+ rulesets[ruleset] = ruleset.antag_weight
. = ruleset.ruleset_cost // return the ruleset cost to be subtracted from the gamemode budget
- if(!ruleset.implied_ruleset)
+ if(!ruleset.implied_ruleset_type)
return
- var/datum/ruleset/implied/implied = locate(ruleset.implied_ruleset) in implied_rulesets
+ var/datum/ruleset/implied/implied = locate(ruleset.implied_ruleset_type) in implied_rulesets
if(!implied)
- log_dynamic("Adding implied ruleset: [ruleset.implied_ruleset.name]")
- implied = new ruleset.implied_ruleset
+ log_dynamic("Adding implied ruleset: [ruleset.implied_ruleset_type.name]")
+ implied = new ruleset.implied_ruleset_type
implied_rulesets += implied
implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, on_implied))
@@ -88,7 +88,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
for(var/datum/ruleset/ruleset in rulesets)
ruleset.antag_amount = 1
- budget -= ruleset.cost
+ budget -= ruleset.antag_cost
log_dynamic("Automatic deduction: +1 [ruleset.name]. Remaining budget: [budget].")
log_dynamic("Rulesets in play: [english_list((rulesets + implied_rulesets))]")
@@ -107,7 +107,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
temp_rulesets -= ruleset
continue
ruleset.antag_amount++
- budget -= ruleset.cost
+ budget -= ruleset.antag_cost
log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].")
log_dynamic("No more antagonist budget remaining.")
From 25c9e8a583358dad8a5228e04f991bf977644134 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Thu, 2 Jan 2025 13:59:19 -0700
Subject: [PATCH 11/17] blah blah
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index b7092760aa64..20e23ec7b8d9 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -25,7 +25,8 @@
"Warden",
"Detective",
"Head of Security",
- "Captain", "Blueshield",
+ "Captain",
+ "Blueshield",
"Nanotrasen Representative",
"Magistrate",
"Internal Affairs Agent",
From 6ae0526cb240900e987b07d4b3c8b27f7b99fc62 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Thu, 2 Jan 2025 16:58:08 -0700
Subject: [PATCH 12/17] updates based on #27811
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 1 +
1 file changed, 1 insertion(+)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 20e23ec7b8d9..5b8d2a261c60 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -30,6 +30,7 @@
"Nanotrasen Representative",
"Magistrate",
"Internal Affairs Agent",
+ "Nanotrasen Career Trainer",
"Nanotrasen Navy Officer",
"Special Operations Officer",
"Trans-Solar Federation General"
From 2523fc8d69c6052cfa3c4aad55a0a30129332a0c Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Fri, 3 Jan 2025 14:32:45 -0700
Subject: [PATCH 13/17] ok fix
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 5b8d2a261c60..cdb426916768 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -50,6 +50,10 @@
/// All of the minds that we will make into our antagonist type
var/list/datum/mind/pre_antags = list()
+/datum/ruleset/Destroy(force, ...)
+ stack_trace("[src] ([type]) was destroyed.")
+ return ..()
+
/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets)
if(ruleset_budget < ruleset_cost)
return "Not enough budget"
@@ -73,7 +77,8 @@
if(GLOB.configuration.gamemode.prevent_mindshield_antags)
banned_jobs += protected_jobs
- for(var/datum/mind/antag as anything in shuffle_inplace(possible_antags))
+ shuffle_inplace(possible_antags)
+ for(var/datum/mind/antag as anything in possible_antags)
if(antag_amount <= 0)
break
if(!can_apply(antag))
From fba7ed4e4f07f324bdc7aced1bce89df1d34fbbd Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Fri, 3 Jan 2025 15:00:52 -0700
Subject: [PATCH 14/17] makes the menu usable during secret
---
code/modules/admin/misc_admin_procs.dm | 2 +-
code/modules/admin/topic.dm | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/code/modules/admin/misc_admin_procs.dm b/code/modules/admin/misc_admin_procs.dm
index c0f75b8ab491..9e9fba26e10e 100644
--- a/code/modules/admin/misc_admin_procs.dm
+++ b/code/modules/admin/misc_admin_procs.dm
@@ -327,7 +327,7 @@ GLOBAL_VAR_INIT(disable_explosions, FALSE)
dat += "Change Game Mode
"
if(GLOB.master_mode == "secret")
dat += "(Force Secret Mode)
"
- if(GLOB.master_mode == "dynamic")
+ if(GLOB.master_mode == "dynamic" || (GLOB.master_mode == "secret" && GLOB.secret_force_mode == "dynamic"))
dat += "(Force Dynamic Rulesets)
"
dat += "
"
dat += "Create Object
"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index cc163ace3046..f25bbe8445a0 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1046,7 +1046,7 @@
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ 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)
@@ -1086,7 +1086,7 @@
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ 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)
if(href_list["f_dynamic2"] == "budget")
var/budget = input(usr, "Pick a budget for the dynamic gamemode (-1 to randomize)", "Gamemode Budget") as num|null
From ce3f6ce57bd1a3f1fdb73692dacfd32a69ce23d6 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Fri, 3 Jan 2025 17:20:02 -0700
Subject: [PATCH 15/17] ok RANDOM BULLSHIT GO!
---
code/game/gamemodes/dynamic/antag_rulesets.dm | 6 ++++--
code/game/gamemodes/dynamic/dynamic.dm | 7 +++++++
code/game/gamemodes/game_mode.dm | 5 ++++-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index cdb426916768..4fe9a721c1c6 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -166,9 +166,10 @@
antag_weight = 0
// antag_cost is allowed to be edited to help with refunding antagonists
antag_cost = 0
- // This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
+ /// This signal is registered on whatever (multiple) rulesets implied us. This will call on_implied.
var/target_signal
-
+ /// Set this to true if this implied ruleset was activated
+ var/was_triggered = FALSE
/datum/ruleset/implied/proc/on_implied(datum/antagonist/implier)
stack_trace("[type]/on_implied() not implemented!")
@@ -187,3 +188,4 @@
log_dynamic("Rolled implied [name]: +1 [name], -1 [implier.name].")
implier.antag_amount -= 1
antag_amount += 1
+ was_triggered = TRUE
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index ae54b4a7e2d4..cb97610d366f 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -137,3 +137,10 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
// /datum/game_mode/dynamic/traitors_to_add()
// should probably implement this in some form
+
+/datum/game_mode/dynamic/get_webhook_name()
+ var/list/implied_and_used = list()
+ for(var/datum/ruleset/implied/implied as anything in implied_rulesets)
+ if(implied.was_triggered)
+ implied_and_used += implied
+ return "[name] ([english_list(rulesets + implied_and_used, nothing_text = "Extended")])"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index ffa6c0a158f1..b917c4a44b99 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -247,7 +247,7 @@
for(var/tech_id in SSeconomy.tech_levels)
SSblackbox.record_feedback("tally", "cargo max tech level sold", SSeconomy.tech_levels[tech_id], tech_id)
- GLOB.discord_manager.send2discord_simple(DISCORD_WEBHOOK_PRIMARY, "A round of [name] has ended - [surviving_total] survivors, [ghosts] ghosts.")
+ GLOB.discord_manager.send2discord_simple(DISCORD_WEBHOOK_PRIMARY, "A round of [get_webhook_name()] has ended - [surviving_total] survivors, [ghosts] ghosts.")
if(SSredis.connected)
// Send our presence to required channels
var/list/presence_data = list()
@@ -680,3 +680,6 @@
traitor.special_role = SPECIAL_ROLE_TRAITOR
traitor.restricted_roles = restricted_jobs
traitor.add_antag_datum(/datum/antagonist/traitor) // They immediately get a new objective
+
+/datum/game_mode/proc/get_webhook_name()
+ return name
From 7a10203e0c2e3f8e394c2c54162d41fd10d566cc Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Mon, 13 Jan 2025 13:34:37 -0500
Subject: [PATCH 16/17] Dynamic final changes
---
code/__DEFINES/gamemode.dm | 4 +
code/__HELPERS/_logging.dm | 5 --
.../game/gamemodes/autotraitor/autotraitor.dm | 3 +
code/game/gamemodes/dynamic/antag_rulesets.dm | 19 ++--
code/game/gamemodes/dynamic/dynamic.dm | 88 ++++++++++++-------
code/game/gamemodes/game_mode.dm | 5 +-
code/game/machinery/cryopod.dm | 8 +-
7 files changed, 81 insertions(+), 51 deletions(-)
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index 350b3cf89ba5..76332f7fb738 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -78,3 +78,7 @@
#define DYNAMIC_RULESET_NORMAL "Normal"
#define DYNAMIC_RULESET_FORCED "Forced"
#define DYNAMIC_RULESET_BANNED "Banned"
+
+#define RULESET_FAILURE_BUDGET "Not enough budget"
+#define RULESET_FAILURE_NO_PLAYERS "No drafted players"
+#define RULESET_FAILURE_CHANGELING_SECONDARY_RULESET "Needs a secondary ruleset in rotation"
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index e8754e90e699..7fc2bee5074c 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -43,11 +43,6 @@ GLOBAL_PROTECT(log_end)
if(check_rights(R_DEBUG | R_VIEWRUNTIMES, FALSE, C.mob) && (C.prefs.toggles & PREFTOGGLE_CHAT_DEBUGLOGS))
to_chat(C, "DEBUG: [text]", MESSAGE_TYPE_DEBUG, confidential = TRUE)
-/proc/log_dynamic(text) // CTODO, REMOVE
- for(var/client/C in GLOB.admins)
- if(check_rights(R_DEBUG, FALSE, C.mob) && (C.prefs.toggles & PREFTOGGLE_CHAT_DEBUGLOGS))
- to_chat(C, "DYNAMIC: [text]", MESSAGE_TYPE_DEBUG, confidential = TRUE)
-
/proc/log_game(text)
if(GLOB.configuration.logging.game_logging)
rustg_log_write(GLOB.world_game_log, "GAME: [text][GLOB.log_end]")
diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm
index 25203e503e12..c30cbaea4323 100644
--- a/code/game/gamemodes/autotraitor/autotraitor.dm
+++ b/code/game/gamemodes/autotraitor/autotraitor.dm
@@ -139,3 +139,6 @@
if(prob(traitor_prob))
message_admins("New traitor roll passed. Making a new Traitor.")
character.mind.make_Traitor()
+
+/datum/game_mode/traitor/autotraitor/on_mob_cryo(mob/sleepy_mob, obj/machinery/cryopod/cryopod)
+ possible_traitors.Remove(sleepy_mob)
diff --git a/code/game/gamemodes/dynamic/antag_rulesets.dm b/code/game/gamemodes/dynamic/antag_rulesets.dm
index 4fe9a721c1c6..3e56784bdbb2 100644
--- a/code/game/gamemodes/dynamic/antag_rulesets.dm
+++ b/code/game/gamemodes/dynamic/antag_rulesets.dm
@@ -56,9 +56,9 @@
/datum/ruleset/proc/ruleset_possible(ruleset_budget, rulesets)
if(ruleset_budget < ruleset_cost)
- return "Not enough budget"
+ return RULESET_FAILURE_BUDGET
if(!length(SSticker.mode.get_players_for_role(antagonist_type::job_rank))) // this specifically needs to be job_rank not special_rank
- return "No drafted players"
+ return RULESET_FAILURE_NO_PLAYERS
/datum/ruleset/proc/antagonist_possible(budget)
return budget >= antag_cost
@@ -102,16 +102,16 @@
return FALSE
return TRUE
-/datum/ruleset/proc/post_setup()
+/datum/ruleset/proc/post_setup(datum/game_mode/dynamic)
for(var/datum/mind/antag as anything in pre_antags)
antag.add_antag_datum(antagonist_type)
/datum/ruleset/proc/refund(info)
// not enough antagonists signed up!!! idk what to do. The only real solution is to procedurally allocate budget, which will result in 1000x more get_players_for_role() calls. Which is not cheap.
// OR we cache get_players_for_role() and then just check if they have a special_role. May be unreliable.
- log_dynamic("[info] Refunding [antag_cost * antag_amount] budget.")
- log_dynamic("REFUNDING NOT IMPLEMENTED!!")
- // ctodo real refunding?
+ // log_dynamic("[info] Refunding [antag_cost * antag_amount] budget.")
+ // Currently unimplemented. Will be useful for a possible future PR where latejoin antagonists are factored in.
+ return
/datum/ruleset/traitor
name = "Traitor"
@@ -120,7 +120,7 @@
antag_weight = 2
antagonist_type = /datum/antagonist/traitor
-/datum/ruleset/traitor/post_setup()
+/datum/ruleset/traitor/post_setup(datum/game_mode/dynamic)
var/random_time = rand(5 MINUTES, 15 MINUTES)
for(var/datum/mind/antag as anything in pre_antags)
var/datum/antagonist/traitor/traitor_datum = new antagonist_type()
@@ -128,6 +128,7 @@
traitor_datum.delayed_objectives = TRUE
traitor_datum.addtimer(CALLBACK(traitor_datum, TYPE_PROC_REF(/datum/antagonist/traitor, reveal_delayed_objectives)), random_time, TIMER_DELETE_ME)
antag.add_antag_datum(traitor_datum)
+ addtimer(CALLBACK(dynamic, TYPE_PROC_REF(/datum/game_mode, fill_antag_slots)), random_time)
/datum/ruleset/vampire
name = "Vampire"
@@ -156,7 +157,7 @@
// We're the first ruleset, but we can afford another ruleset
if((ruleset_budget >= /datum/ruleset/traitor::ruleset_cost) || (ruleset_budget >= /datum/ruleset/vampire::ruleset_cost))
return ..()
- return "Needs a secondary ruleset in rotation"
+ return RULESET_FAILURE_CHANGELING_SECONDARY_RULESET
// 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
@@ -185,7 +186,7 @@
banned_species_only = TRUE
/datum/ruleset/implied/mindflayer/on_implied(datum/ruleset/implier)
- log_dynamic("Rolled implied [name]: +1 [name], -1 [implier.name].")
+ // log_dynamic("Rolled implied [name]: +1 [name], -1 [implier.name].")
implier.antag_amount -= 1
antag_amount += 1
was_triggered = TRUE
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index cb97610d366f..b5e892af26df 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -5,10 +5,14 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
config_tag = "dynamic"
secondary_restricted_jobs = list("AI")
required_players = 10
- var/list/forced_rulesets = list()
+ /// Non-implied rulesets in play
var/list/datum/ruleset/rulesets = list()
+ /// Implied rulesets that are in play
var/list/datum/ruleset/implied_rulesets = list()
+ /// How much budget is left after roundstart antagonists roll
+ var/budget_overflow = 0
+
/datum/game_mode/dynamic/announce()
to_chat(world, "The current game mode is - Dynamic")
var/list/possible_rulesets = list()
@@ -22,7 +26,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_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
- log_dynamic("Allocated gamemode budget: [ruleset_budget]")
+ // log_dynamic("Allocated gamemode budget: [ruleset_budget]")
var/list/possible_rulesets = list()
for(var/datum/ruleset/ruleset as anything in subtypesof(/datum/ruleset))
if(ruleset.ruleset_weight <= 0)
@@ -32,7 +36,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/datum/ruleset/new_ruleset = new ruleset()
possible_rulesets[new_ruleset] = new_ruleset.ruleset_weight
- log_dynamic("Available rulesets: [english_list(possible_rulesets)]")
+ // log_dynamic("Available rulesets: [english_list(possible_rulesets)]")
for(var/datum/ruleset/ruleset as anything in GLOB.dynamic_forced_rulesets)
if(ruleset == "budget")
@@ -42,7 +46,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(!ispath(ruleset, /datum/ruleset))
stack_trace("Non-ruleset in GLOB.dynamic_forced_rulesets: \"[ruleset]\" ([ruleset?.type])")
continue
- log_dynamic("Forcing ruleset: [ruleset.name]")
+ // log_dynamic("Forcing ruleset: [ruleset.name]")
ruleset_budget -= pick_ruleset(new ruleset, ruleset_budget, force = TRUE)
for(var/datum/ruleset/old_ruleset in possible_rulesets)
if(old_ruleset.type == ruleset)
@@ -52,11 +56,11 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
while(ruleset_budget >= 0)
var/datum/ruleset/ruleset = pickweight(possible_rulesets)
if(!ruleset)
- log_dynamic("No more available rulesets")
+ // log_dynamic("No more available rulesets")
return
ruleset_budget -= pick_ruleset(ruleset, ruleset_budget)
possible_rulesets -= ruleset
- log_dynamic("No more ruleset budget")
+ // log_dynamic("No more ruleset budget")
/datum/game_mode/dynamic/proc/pick_ruleset(datum/ruleset/ruleset, ruleset_budget, force)
if(!ruleset)
@@ -64,9 +68,9 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(!force)
var/failure_reason = ruleset.ruleset_possible(ruleset_budget, rulesets)
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]")
+ // log_dynamic("Rolled ruleset: [ruleset.name]")
rulesets[ruleset] = ruleset.antag_weight
. = ruleset.ruleset_cost // return the ruleset cost to be subtracted from the gamemode budget
if(!ruleset.implied_ruleset_type)
@@ -74,69 +78,86 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/datum/ruleset/implied/implied = locate(ruleset.implied_ruleset_type) in implied_rulesets
if(!implied)
- log_dynamic("Adding implied ruleset: [ruleset.implied_ruleset_type.name]")
+ // log_dynamic("Adding implied ruleset: [ruleset.implied_ruleset_type.name]")
implied = new ruleset.implied_ruleset_type
implied_rulesets += implied
implied.RegisterSignal(ruleset, implied.target_signal, TYPE_PROC_REF(/datum/ruleset/implied, on_implied))
/datum/game_mode/dynamic/proc/allocate_antagonist_budget()
if(!length(rulesets))
- log_dynamic("No rulesets in play.")
+ // log_dynamic("No rulesets in play.")
return
var/budget = num_players()
- log_dynamic("Allocated antagonist budget: [budget].")
+ // log_dynamic("Allocated antagonist budget: [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].")
+ // log_dynamic("Automatic deduction: +1 [ruleset.name]. Remaining budget: [budget].")
- log_dynamic("Rulesets in play: [english_list((rulesets + implied_rulesets))]")
+ // log_dynamic("Rulesets in play: [english_list((rulesets + implied_rulesets))]")
apply_antag_budget(budget)
-/datum/game_mode/dynamic/proc/apply_antag_budget(budget) // ctodo, 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(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)
var/datum/ruleset/ruleset = pickweight(temp_rulesets)
if(!ruleset)
- log_dynamic("No rulesets remaining. Remaining budget: [budget].")
+ // log_dynamic("No rulesets remaining. Remaining budget: [budget].")
return
if(!ruleset.antagonist_possible(budget))
- log_dynamic("Rolled [ruleset.name]: failed, removing [ruleset.name] ruleset.")
+ // 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].")
- log_dynamic("No more antagonist budget remaining.")
+ // log_dynamic("Rolled [ruleset.name]: success, +1 [ruleset.name]. Remaining budget: [budget].")
+ // log_dynamic("No more antagonist budget remaining.")
/datum/game_mode/dynamic/pre_setup()
- var/watch = start_watch()
- log_dynamic("Starting dynamic setup.")
+ // var/watch = start_watch()
+ // log_dynamic("Starting dynamic setup.")
allocate_ruleset_budget()
- log_dynamic("-=-=-=-=-=-=-=-=-=-=-=-=-")
+ // log_dynamic("-=-=-=-=-=-=-=-=-=-=-=-=-")
allocate_antagonist_budget()
- log_dynamic("=-=-=-=-=-=-=-=-=-=-=-=-=")
- var/budget_overflow = 0
+ // log_dynamic("=-=-=-=-=-=-=-=-=-=-=-=-=")
+
for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets)) // rulesets first, then implied rulesets
- log_dynamic("Applying [ruleset.antag_amount] [ruleset.name]\s.")
+ // log_dynamic("Applying [ruleset.antag_amount] [ruleset.name]\s.")
budget_overflow += ruleset.pre_setup()
- log_dynamic("Budget overflow: [budget_overflow].")
+ // log_dynamic("Budget overflow: [budget_overflow].")
// for the future, maybe try readding antagonists with apply_antag_budget(budget_overflow)
- log_dynamic("Finished dynamic setup in [stop_watch(watch)]s")
+ // log_dynamic("Finished dynamic setup in [stop_watch(watch)]s")
return TRUE
/datum/game_mode/dynamic/post_setup()
for(var/datum/ruleset/ruleset in (rulesets + implied_rulesets))
- if(length(ruleset.pre_antags))
- log_dynamic("Making antag datums for [ruleset.name] ruleset.")
- ruleset.post_setup()
+ // if(length(ruleset.pre_antags))
+ // log_dynamic("Making antag datums for [ruleset.name] ruleset.")
+ ruleset.post_setup(src)
..()
-// /datum/game_mode/dynamic/traitors_to_add()
-// should probably implement this in some form
+/datum/game_mode/dynamic/traitors_to_add()
+ . = floor(budget_overflow / /datum/ruleset/traitor::antag_cost)
+ budget_overflow -= (. * /datum/ruleset/traitor::antag_cost)
+
+/datum/game_mode/dynamic/latespawn(mob)
+ . = ..()
+ budget_overflow++
+
+/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--
+ 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
/datum/game_mode/dynamic/get_webhook_name()
var/list/implied_and_used = list()
@@ -144,3 +165,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
if(implied.was_triggered)
implied_and_used += implied
return "[name] ([english_list(rulesets + implied_and_used, nothing_text = "Extended")])"
+
+// /proc/log_dynamic(text)
+// for(var/client/C in GLOB.admins)
+// if(check_rights(R_DEBUG, FALSE, C.mob) && (C.prefs.toggles & PREFTOGGLE_CHAT_DEBUGLOGS))
+// to_chat(C, "DYNAMIC: [text]", MESSAGE_TYPE_DEBUG, confidential = TRUE)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index b917c4a44b99..9b9e6d2df5bc 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -662,7 +662,7 @@
if(length(traitors) < traitors_to_add())
traitors_to_add += (traitors_to_add() - length(traitors))
- if(!traitors_to_add)
+ if(traitors_to_add <= 0)
return
var/list/potential_recruits = get_alive_players_for_role(ROLE_TRAITOR)
@@ -683,3 +683,6 @@
/datum/game_mode/proc/get_webhook_name()
return name
+
+/datum/game_mode/proc/on_mob_cryo(mob/sleepy_mob, obj/machinery/cryopod/cryopod)
+ return
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index e5822924c397..caff61796c24 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -340,7 +340,7 @@
//Delete all items not on the preservation list.
var/list/items = contents
- items -= occupant // Don't delete the occupant
+ items -= occupant // Don't delete the occupant // this fucking nullspaces the occupant btw, i fuckin hate old code
items -= announce // or the autosay radio.
ADD_TRAIT(occupant, TRAIT_CRYO_DESPAWNING, TRAIT_GENERIC)
@@ -368,6 +368,8 @@
if(IS_SACRIFICE_TARGET(occupant.mind))
SSticker.mode.cult_team.find_new_sacrifice_target()
+ SSticker.mode.on_mob_cryo(occupant, src)
+
//Update any existing objectives involving this mob.
if(occupant.mind)
if(occupant.mind.initial_account)
@@ -386,10 +388,6 @@
if(occupant.mind.objective_holder.clear())
occupant.mind.special_role = null
- else
- if(SSticker.mode.name == "AutoTraitor")
- var/datum/game_mode/traitor/autotraitor/current_mode = SSticker.mode
- current_mode.possible_traitors.Remove(occupant)
// Delete them from datacore.
From defa5a5fa0ee4ca50baf6a4d10f4ec5c2b5b02cb Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Mon, 13 Jan 2025 16:14:45 -0500
Subject: [PATCH 17/17] fixes some missing budget
---
code/game/gamemodes/dynamic/dynamic.dm | 1 +
1 file changed, 1 insertion(+)
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index b5e892af26df..1e0e1ec4ad2e 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -105,6 +105,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_rulesets)
var/datum/ruleset/ruleset = pickweight(temp_rulesets)
if(!ruleset)
// log_dynamic("No rulesets remaining. Remaining budget: [budget].")
+ budget_overflow = budget
return
if(!ruleset.antagonist_possible(budget))
// log_dynamic("Rolled [ruleset.name]: failed, removing [ruleset.name] ruleset.")