[Role Datums] Dynamic Mode (#19548)

* Dynamic Mode

* no rules

* latejoin

* midround

* Traitor

* secs

* process

* better

* aight
This commit is contained in:
DeityLink
2018-09-09 02:00:13 +02:00
committed by MadmanMartian
parent deddaed3ec
commit 98071b8de4
13 changed files with 769 additions and 2 deletions

View File

@@ -1,3 +1,9 @@
// Dynamic Mode
#define CURRENT_LIVING_PLAYERS "living"
#define CURRENT_LIVING_ANTAGS "antags"
#define CURRENT_DEAD_PLAYERS "dead"
#define CURRENT_OBSERVERS "observers"
// Faction IDs
#define BLOODCULT "cult of Nar-Sie"
#define REVOLUTION "revolution"
@@ -43,9 +49,12 @@
#define GREET_DEFAULT "default"
#define GREET_ROUNDSTART "roundstart"
#define GREET_LATEJOIN "latejoin"
#define GREET_ADMINTOGGLE "admintoggle"
#define GREET_CUSTOM "custom"
#define GREET_AUTOTATOR "autotator"
#define GREET_CONVERTED "converted"
#define GREET_PAMPHLET "pamphlet"

View File

@@ -0,0 +1,279 @@
var/list/forced_roundstart_ruleset = list()
/datum/gamemode/dynamic
name = "Dynamic Mode"
var/threat_level = 0//rolled at the beginning of the round.
var/threat = 0//set at the beginning of the round. Spent by the mode to "purchase" rules.
var/list/roundstart_rules = list()
var/list/latejoin_rules = list()
var/list/midround_rules = list()
var/list/second_rule_req = list(0,0,0,80,60,40,20,0,0,0)//requirements for extra round start rules
//var/list/second_rule_req = list(100,100,100,80,60,40,20,0,0,0)//requirements for extra round start rules
var/list/third_rule_req = list(100,100,100,100,100,70,50,30,10,0)
var/roundstart_pop_ready = 0
var/list/candidates = list()
var/list/current_rules = list()
var/list/executed_rules = list()
var/list/living_players = list()
var/list/living_antags = list()
var/list/dead_players = list()
var/list/list_observers = list()
var/latejoin_injection_cooldown = 0
var/midround_injection_cooldown = 0
var/datum/dynamic_ruleset/latejoin/forced_latejoin_rule = null
/datum/gamemode/dynamic/can_start()
threat_level = rand(1,100)*0.6 + rand(1,100)*0.4//https://docs.google.com/spreadsheets/d/1QLN_OBHqeL4cm9zTLEtxlnaJHHUu0IUPzPbsI-DFFmc/edit#gid=499381388
threat = threat_level
message_admins("Dynamic Mode initialized with a Threat Level of... <font size='8'>[threat_level]</font>!")
return 1
/datum/gamemode/dynamic/Setup()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
roundstart_rules += new rule()
for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin))
latejoin_rules += new rule()
for (var/rule in subtypesof(/datum/dynamic_ruleset/midround))
midround_rules += new rule()
for(var/mob/new_player/player in player_list)
if(player.ready && player.mind)
roundstart_pop_ready++
candidates.Add(player)
message_admins("Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.")
if (candidates.len <= 0)
message_admins("Not a single player readied-up. The round will begin without any roles assigned.")
return 1
if (roundstart_rules.len <= 0)
message_admins("There are no roundstart rules within the code, what the fuck? The round will begin without any roles assigned.")
return 1
if (forced_roundstart_ruleset.len > 0)
rigged_roundstart()
else
roundstart()
return 1
/datum/gamemode/dynamic/proc/rigged_roundstart()
message_admins("[forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
for (var/datum/dynamic_ruleset/roundstart/rule in forced_roundstart_ruleset)
rule.mode = src
rule.candidates = candidates.Copy()
rule.trim_candidates()
if (rule.ready())
picking_roundstart_rule(list(rule))
/datum/gamemode/dynamic/proc/roundstart()
var/list/drafted_rules = list()
var/i = 0
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
if (rule.acceptable(roundstart_pop_ready,threat_level) && threat >= rule.cost) //if we got the population and threat required
i++ //we check whether we've got elligible players
rule.candidates = candidates.Copy()
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = rule.weight
var/indice_pop = min(10,round(roundstart_pop_ready/5)+1)
message_admins("[i] rulesets qualify for the current pop and threat level, including [drafted_rules.len] with elligible candidates.")
if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules))
if (threat >= second_rule_req[indice_pop])//we've got enough population and threat for a second rulestart rule
message_admins("The current pop and threat level allow for a second round start ruleset, there remains [candidates.len] elligible candidates and [drafted_rules.len] elligible rulesets")
if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules))
if (threat >= third_rule_req[indice_pop])//we've got enough population and threat for a third rulestart rule
message_admins("The current pop and threat level allow for a third round start ruleset, there remains [candidates.len] elligible candidates and [drafted_rules.len] elligible rulesets")
if (!drafted_rules.len > 0 || !picking_roundstart_rule(drafted_rules))
message_admins("The mode failed to pick a third ruleset.")
else
message_admins("The mode failed to pick a second ruleset.")
else
message_admins("The mode failed to pick a first ruleset. The round will begin without any roles assigned.")
return 0
return 1
/datum/gamemode/dynamic/proc/picking_roundstart_rule(var/list/drafted_rules = list())
var/datum/dynamic_ruleset/roundstart/starting_rule = pickweight(drafted_rules)
if (starting_rule)
message_admins("Picking a ruleset...<font size='3'>[starting_rule.name]</font>!")
log_admin("Picking a ruleset...[starting_rule.name]!")
roundstart_rules -= starting_rule
drafted_rules -= starting_rule
threat = max(0,threat-starting_rule.cost)
if (starting_rule.execute())//this should never fail since ready() returned 1
executed_rules += starting_rule
if (starting_rule.persistent)
current_rules += starting_rule
for(var/mob/M in starting_rule.assigned)
candidates -= M
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
rule.candidates -= M//removing the assigned players from the candidates for the other rules
if (!rule.ready())
drafted_rules -= rule//and removing rules from those that are no longer elligible
return 1
else
message_admins("....except not because whoever coded that ruleset forgot some cases in ready() apparently! execute() returned 0.")
return 0
/datum/gamemode/dynamic/proc/picking_latejoin_rule(var/list/drafted_rules = list())
var/datum/dynamic_ruleset/latejoin/latejoin_rule = pickweight(drafted_rules)
if (latejoin_rule)
if (!latejoin_rule.repeatable)
latejoin_rules -= latejoin_rule
threat = max(0,threat-latejoin_rule.cost)
if (latejoin_rule.execute())//this should never fail since ready() returned 1
var/mob/M = pick(latejoin_rule.assigned)
message_admins("[key_name(M)] joined the station, and was selected by the <font size='3'>[latejoin_rule.name]</font> ruleset.")
log_admin("[key_name(M)] joined the station, and was selected by the [latejoin_rule.name] ruleset.")
executed_rules += latejoin_rule
if (latejoin_rule.persistent)
current_rules += latejoin_rule
return 1
return 0
/datum/gamemode/dynamic/proc/picking_midround_rule(var/list/drafted_rules = list())
var/datum/dynamic_ruleset/midround/midround_rule = pickweight(drafted_rules)
if (midround_rule)
if (!midround_rule.repeatable)
midround_rules -= midround_rule
threat = max(0,threat-midround_rule.cost)
if (midround_rule.execute())//this should never fail since ready() returned 1
message_admins("Injecting some threats...<font size='3'>[midround_rule.name]</font>!")
log_admin("Injecting some threats...[midround_rule.name]!")
executed_rules += midround_rule
if (midround_rule.persistent)
current_rules += midround_rule
return 1
return 0
/datum/gamemode/dynamic/proc/picking_specific_rule(var/ruletype,var/forced=0)//an experimental proc to allow admins to call rules on the fly or have rules call other rules
var/datum/dynamic_ruleset/midround/new_rule = new ruletype()//you should only use it to call midround rules though.
update_playercounts()
var/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
current_players[CURRENT_LIVING_PLAYERS] = living_players.Copy()
current_players[CURRENT_LIVING_ANTAGS] = living_antags.Copy()
current_players[CURRENT_DEAD_PLAYERS] = dead_players.Copy()
current_players[CURRENT_OBSERVERS] = list_observers.Copy()
if (new_rule && (forced || (new_rule.acceptable(living_players.len,threat_level) && new_rule.cost <= threat)))
new_rule.candidates = current_players.Copy()
new_rule.trim_candidates()
if (new_rule.ready())
threat -= new_rule.cost
if (new_rule.execute())//this should never fail since ready() returned 1
message_admins("Making a call to a specific ruleset...<font size='3'>[new_rule.name]</font>!")
log_admin("Making a call to a specific ruleset...[new_rule.name]!")
executed_rules += new_rule
if (new_rule.persistent)
current_rules += new_rule
return 1
return 0
/datum/gamemode/dynamic/process()
if (latejoin_injection_cooldown)
latejoin_injection_cooldown--
for (var/datum/dynamic_ruleset/rule in current_rules)
rule.process()
if (midround_injection_cooldown)
midround_injection_cooldown--
else
//time to inject some threat into the round
if(emergency_shuttle.departed)//unless the shuttle is gone
return
update_playercounts()
if (injection_attempt())
midround_injection_cooldown = rand(12000,21000)//20 to 35 minutes inbetween midround threat injections attempts
var/list/drafted_rules = list()
var/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
current_players[CURRENT_LIVING_PLAYERS] = living_players.Copy()
current_players[CURRENT_LIVING_ANTAGS] = living_antags.Copy()
current_players[CURRENT_DEAD_PLAYERS] = dead_players.Copy()
current_players[CURRENT_OBSERVERS] = list_observers.Copy()
for (var/datum/dynamic_ruleset/latejoin/rule in midround_rules)
if (rule.acceptable(living_players.len,threat_level) && threat >= rule.cost)
rule.candidates = current_players.Copy()
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = rule.weight
if (drafted_rules.len > 0)
picking_latejoin_rule(drafted_rules)
/datum/gamemode/dynamic/proc/update_playercounts()
living_players = list()
living_antags = list()
dead_players = list()
list_observers = list()
for (var/mob/M in player_list)
if (!M.client)
continue
if (istype(M,/mob/new_player))
continue
if (M.stat != DEAD)
living_players.Add(M)
if (M.mind && (M.mind.antag_roles.len > 0))
living_antags.Add(M)
else
if (istype(M,/mob/dead/observer))
var/mob/dead/observer/O = M
if (O.started_as_observer)//Observers
list_observers.Add(M)
continue
if (O.mind && O.mind.current && O.mind.current.ajourn)//Cultists
living_players.Add(M)//yes we're adding a ghost to "living_players", so make sure to properly check for type when testing midround rules
continue
dead_players.Add(M)//Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
/datum/gamemode/dynamic/proc/injection_attempt()//will need to gather stats to refine those values later
if (latejoin_injection_cooldown)
return
var/chance = 0
var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(living_players.len/5))//https://docs.google.com/spreadsheets/d/1QLN_OBHqeL4cm9zTLEtxlnaJHHUu0IUPzPbsI-DFFmc/edit#gid=2053826290
if (!living_antags.len)
chance += 50//no antags at all? let's boost those odds!
else
var/current_pop_per_antag = living_players.len / living_antags.len
if (current_pop_per_antag > max_pop_per_antag)
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
else
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
if (dead_players.len > living_players.len)
chance -= 30//more than half the crew died? ew, let's calm down on antags
if (threat > 70)
chance += 20
if (threat < 30)
chance -= 20
chance = round(max(0,chance))
return (prob(chance))
/datum/gamemode/dynamic/latespawn(var/mob/living/newPlayer)
if(emergency_shuttle.departed)//no more rules after the shuttle has left
return
update_playercounts()
if (forced_latejoin_rule)
forced_latejoin_rule.candidates = list(newPlayer)
forced_latejoin_rule.trim_candidates()
if (forced_latejoin_rule.ready())
picking_latejoin_rule(list(forced_latejoin_rule))
forced_latejoin_rule = null
else if (injection_attempt())
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
if (rule.acceptable(living_players.len,threat_level) && threat >= rule.cost)
rule.candidates = list(newPlayer)
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = rule.weight
if (drafted_rules.len > 0 && picking_latejoin_rule(drafted_rules))
latejoin_injection_cooldown = rand(6600,10200)//11 to 17 minutes inbetween antag latejoiner rolls

View File

@@ -0,0 +1,147 @@
/datum/dynamic_ruleset
var/name = ""//For admin logging, and round end scoreboard
var/persistent = 0//if set to 1, the rule won't be discarded after being executed, and /gamemode/dynamic will call process() every MC tick
var/repeatable = 0//if set to 1, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules)
var/list/candidates = list()//list of players that are being drafted for this rule
var/list/assigned = list()//list of players that were selected for this rule
var/role_category = ROLE_TRAITOR//rule will only accept candidates with "Yes" or "Always" in the preferences for this role
var/list/restricted_from_jobs = list()//if set, rule will deny candidates from those jobs
var/list/exclusive_to_jobs = list()//if set, rule will only accept candidates from those jobs
var/list/jobs_must_exist = list()//if set, there needs to be a certain amount of players doing those jobs (among the players who won't be drafted) for the rule to be drafted
var/required_candidates = 0//the rule needs this many candidates (post-trimming) to be executed (example: Cult need 4 players at round start)
var/weight = 5//1 -> 9, probability for this rule to be picked against other rules
var/cost = 0//threat cost for this rule.
var/list/requirements = list(40,30,20,10,10,10,10,10,10,10)
//requirements are the threat level requirements per pop range. The ranges are as follow:
//0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+
//so with the above default values, The rule will never get drafted below 10 threat level (aka: "peaceful extended"), and it requires a higher threat level at lower pops.
//for reminder: the threat level is rolled at roundstart and tends to hover around 50 https://docs.google.com/spreadsheets/d/1QLN_OBHqeL4cm9zTLEtxlnaJHHUu0IUPzPbsI-DFFmc/edit#gid=499381388
var/datum/gamemode/dynamic/mode = null
/datum/dynamic_ruleset/New()
..()
if (istype(ticker.mode, /datum/gamemode/dynamic))
mode = ticker.mode
else
message_admins("A dynamic ruleset was created but server isn't on Dynamic Mode!")
qdel(src)
/datum/dynamic_ruleset/roundstart//One or more of those drafted at roundstart
/datum/dynamic_ruleset/latejoin//Can be drafted when a player joins the server
/datum/dynamic_ruleset/midround//Can be drafted once in a while during a round
var/list/living_players = list()
var/list/living_antags = list()
var/list/dead_players = list()
var/list/list_observers = list()
/datum/dynamic_ruleset/proc/acceptable(var/population=0,var/threat=0)
//by default, a rule is acceptable if it satisfies the threat level/population requirements.
//If your rule has extra checks, such as counting security officers, do that in ready() instead
var/indice_pop = min(10,round(population/5)+1)
return (threat >= requirements[indice_pop])
/datum/dynamic_ruleset/proc/process()
//write here your rule execution code, everything about faction/role spawning/populating.
return
/datum/dynamic_ruleset/proc/execute()
//write here your rule execution code, everything about faction/role spawning/populating.
return 1
/datum/dynamic_ruleset/proc/ready() //Here you can perform any additional checks you want. (such as checking the map, the amount of certain jobs, etc)
if (required_candidates > candidates.len) //IMPORTANT: If ready() returns 1, that means execute() should never fail!
return 0
return 1
/datum/dynamic_ruleset/proc/trim_candidates()
return
//////////////////////////////////////////////
// //
// ROUNDSTART RULESETS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/roundstart/trim_candidates()
for(var/mob/new_player/P in candidates)
if (!P.client || !P.mind || !P.mind.assigned_role)//are they connected?
candidates.Remove(P)
continue
if (!P.client.desires_role(role_category) || jobban_isbanned(P, role_category))//are they willing and not antag-banned?
candidates.Remove(P)
continue
if (P.mind.assigned_role in restricted_from_jobs)//does their job allow for it?
candidates.Remove(P)
continue
if ((exclusive_to_jobs.len > 0) && !(P.mind.assigned_role in exclusive_to_jobs))//is the rule exclusive to their job?
candidates.Remove(P)
continue
//////////////////////////////////////////////
// //
// LATEJOIN RULESETS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/latejoin/trim_candidates()
for(var/mob/new_player/P in candidates)
if (!P.client || !P.mind || !P.mind.assigned_role)//are they connected?
candidates.Remove(P)
continue
if (!P.client.desires_role(role_category) || jobban_isbanned(P, role_category))//are they willing and not antag-banned?
candidates.Remove(P)
continue
if (P.mind.assigned_role in restricted_from_jobs)//does their job allow for it?
candidates.Remove(P)
continue
if ((exclusive_to_jobs.len > 0) && !(P.mind.assigned_role in exclusive_to_jobs))//is the rule exclusive to their job?
candidates.Remove(P)
continue
//////////////////////////////////////////////
// //
// MIDROUND RULESETS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/trim_candidates()
//unlike the previous two types, these rulesets are not meant for /mob/new_player
//and since I want those rulesets to be as flexible as possible, I'm not gonna put much here,
//but be sure to check dynamic_rulesets_debug.dm for an example.
//
//all you need to know is that here, the candidates list contains 4 lists itself, indexed with the following defines:
//candidates = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
//so for example you can get the list of all current dead players with var/list/dead_players = candidates[CURRENT_DEAD_PLAYERS]
//make sure to properly typecheck the mobs in those lists, as the dead_players list could contain ghosts, or dead players still in their bodies.
//we're still gonna trim the obvious (mobs without clients, jobbanned players, etc)
living_players = trim_list(candidates[CURRENT_LIVING_PLAYERS])
living_antags = trim_list(candidates[CURRENT_LIVING_ANTAGS])
dead_players = trim_list(candidates[CURRENT_DEAD_PLAYERS])
observers = trim_list(candidates[CURRENT_OBSERVERS])
/datum/dynamic_ruleset/midround/proc/trim_list(var/list/L = list())
var/list/trimmed_list = L.Copy()
for(var/mob/M in trimmed_list)
if (!M.client)//are they connected?
trimmed_list.Remove(M)
continue
if (!M.client.desires_role(role_category) || jobban_isbanned(M, role_category))//are they willing and not antag-banned?
trimmed_list.Remove(M)
continue
if (M.mind.assigned_role in restricted_from_jobs)//does their job allow for it?
trimmed_list.Remove(M)
continue
if ((exclusive_to_jobs.len > 0) && !(M.mind.assigned_role in exclusive_to_jobs))//is the rule exclusive to their job?
trimmed_list.Remove(M)
continue
return trimmed_list
//You can then for example prompt dead players in execute() to join as strike teams or whatever
//Or autotator someone

View File

@@ -0,0 +1,88 @@
/datum/dynamic_ruleset/roundstart/test_traitor
name = "Test Lone Traitor"
persistent = 0//if set to 1, the rule won't be discarded after being executed, and the game mode will call update() once in a while
repeatable = 0//if set to 1, dynamic mode will be able to draft this ruleset again later on
role_category = ROLE_TRAITOR//rule will only accept candidates with "Yes" or "Always" in the preferences for this role
required_candidates = 1//the rule needs this many candidates (post-trimming) to be executed (example: Cult need 4 players at round start)
weight = 5//1 -> 9, probability for this rule to be picked against other rules
cost = 0//threat cost for this rule.
requirements = list(0,0,0,0,0,0,0,0,0,0)
/datum/dynamic_ruleset/roundstart/test_traitor/execute()
var/mob/M = pick(candidates)
assigned += M
var/datum/role/traitor/newTraitor = new
newTraitor.AssignToRole(M.mind,1)
newTraitor.OnPostSetup(FALSE)
newTraitor.Greet(GREET_ROUNDSTART)
return 1
/datum/dynamic_ruleset/roundstart/test_cultist
name = "Test Lone Cultist"
persistent = 0//if set to 1, the rule won't be discarded after being executed, and the game mode will call update() once in a while
repeatable = 0//if set to 1, dynamic mode will be able to draft this ruleset again later on
role_category = ROLE_CULTIST//rule will only accept candidates with "Yes" or "Always" in the preferences for this role
required_candidates = 1//the rule needs this many candidates (post-trimming) to be executed (example: Cult need 4 players at round start)
weight = 8//1 -> 9, probability for this rule to be picked against other rules
cost = 0//threat cost for this rule.
requirements = list(0,0,0,0,0,0,0,0,0,0)
/datum/dynamic_ruleset/roundstart/test_cultist/execute()
var/mob/M = pick(candidates)
assigned += M
var/datum/role/cultist/newCultist = new
newCultist.AssignToRole(M.mind,1)
var/datum/faction/bloodcult/cult = find_active_faction_by_type(/datum/faction/bloodcult)
if (!cult)
cult = ticker.mode.CreateFaction(/datum/faction/bloodcult, null, 1)
cult.HandleRecruitedRole(newCultist)
newCultist.OnPostSetup(FALSE)
newCultist.Greet(GREET_ROUNDSTART)
return 1
/datum/dynamic_ruleset/roundstart/test_vampire
name = "Test Lone Vampire"
persistent = 0//if set to 1, the rule won't be discarded after being executed, and the game mode will call update() once in a while
repeatable = 0//if set to 1, dynamic mode will be able to draft this ruleset again later on
role_category = ROLE_VAMPIRE//rule will only accept candidates with "Yes" or "Always" in the preferences for this role
required_candidates = 1//the rule needs this many candidates (post-trimming) to be executed (example: Cult need 4 players at round start)
weight = 2//1 -> 9, probability for this rule to be picked against other rules
cost = 0//threat cost for this rule.
requirements = list(0,0,0,0,0,0,0,0,0,0)
/datum/dynamic_ruleset/roundstart/test_vampire/execute()
var/mob/M = pick(candidates)
assigned += M
var/datum/role/traitor/newVampire = new
newVampire.AssignToRole(M.mind,1)
newVampire.OnPostSetup(FALSE)
newVampire.Greet(GREET_ROUNDSTART)
return 1
////////////////////////////
/datum/dynamic_ruleset/latejoin/test_traitor
name = "Test Latejoin Traitor"
persistent = 0//if set to 1, the rule won't be discarded after being executed, and the game mode will call update() once in a while
repeatable = 0//if set to 1, dynamic mode will be able to draft this ruleset again later on
role_category = ROLE_TRAITOR//rule will only accept candidates with "Yes" or "Always" in the preferences for this role
required_candidates = 1//the rule needs this many candidates (post-trimming) to be executed (example: Cult need 4 players at round start)
weight = 5//1 -> 9, probability for this rule to be picked against other rules
cost = 0//threat cost for this rule.
requirements = list(0,0,0,0,0,0,0,0,0,0)
/datum/dynamic_ruleset/latejoin/test_traitor/execute()
var/mob/M = pick(candidates)
assigned += M
var/datum/role/traitor/newTraitor = new
newTraitor.AssignToRole(M.mind,1)
newTraitor.OnPostSetup(FALSE)
newTraitor.Greet(GREET_ROUNDSTART)
return 1

View File

@@ -0,0 +1,34 @@
//////////////////////////////////////////////
// //
// SYNDICATE TRAITORS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/latejoin/infiltrator
name = "Syndicate Infiltrator"
role_category = ROLE_TRAITOR
restricted_from_jobs = list("Cyborg","Mobile MMI","Security Officer", "Warden", "Detective", "Head of Security", "Captain")
required_candidates = 1
weight = 5
cost = 5
requirements = list(40,30,20,10,10,10,10,10,10,10)
/datum/dynamic_ruleset/latejoin/infiltrator/acceptable(var/population=0,var/threat=0)
var/player_count = mode.living_players.len
var/antag_count = mode.living_antags.len
var/max_traitors = round(player_count / 10) + 1
if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low
return ..()
else
return 0
/datum/dynamic_ruleset/latejoin/infiltrator/execute()
var/mob/M = pick(candidates)
assigned += M
candidates -= M
var/datum/role/traitor/newTraitor = new
newTraitor.AssignToRole(M.mind,1)
newTraitor.OnPostSetup(FALSE)
newTraitor.Greet(GREET_LATEJOIN)
return 1

View File

@@ -0,0 +1,43 @@
//////////////////////////////////////////////
// //
// SYNDICATE TRAITORS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/autotraitor
name = "Syndicate Sleeper Agent"
role_category = ROLE_TRAITOR
restricted_from_jobs = list("Cyborg","Mobile MMI","Security Officer", "Warden", "Detective", "Head of Security", "Captain")
required_candidates = 1
weight = 5
cost = 5
requirements = list(40,30,20,10,10,10,10,10,10,10)
/datum/dynamic_ruleset/midround/autotraitor/acceptable(var/population=0,var/threat=0)
var/player_count = mode.living_players.len
var/antag_count = mode.living_antags.len
var/max_traitors = round(player_count / 10) + 1
if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low
return ..()
else
return 0
/datum/dynamic_ruleset/midround/autotraitor/trim_candidates()
..()
for(var/mob/living/player in living_players)
if(player.z == map.zCentcomm)
living_players -= player//we don't autotator people on Z=2
continue
if(player.mind && (player.mind.antag_roles.len > 0))
living_players -= player//we don't autotator people with roles already
/datum/dynamic_ruleset/midround/autotraitor/execute()
var/mob/M = pick(living_players)
assigned += M
candidates -= M
var/datum/role/traitor/newTraitor = new
newTraitor.AssignToRole(M.mind,1)
newTraitor.OnPostSetup(FALSE)
newTraitor.Greet(GREET_AUTOTATOR)
return 1

View File

@@ -0,0 +1,37 @@
//////////////////////////////////////////////
// //
// SYNDICATE TRAITORS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/roundstart/traitor
name = "Syndicate Traitors"
persistent = 1
role_category = ROLE_TRAITOR
restricted_from_jobs = list("Cyborg","Mobile MMI","Security Officer", "Warden", "Detective", "Head of Security", "Captain")
required_candidates = 1
weight = 5
cost = 10
requirements = list(40,30,20,10,10,10,10,10,10,10)
var/autotraitor_cooldown = 900//15 minutes
/datum/dynamic_ruleset/roundstart/traitor/execute()
var/num_traitors = min(round(mode.candidates.len / 10) + 1, candidates.len)
for (var/i = 1 to num_traitors)
var/mob/M = pick(candidates)
assigned += M
candidates -= M
var/datum/role/traitor/newTraitor = new
newTraitor.AssignToRole(M.mind,1)
newTraitor.OnPostSetup(FALSE)
newTraitor.Greet(GREET_ROUNDSTART)
return 1
/datum/dynamic_ruleset/roundstart/traitor/process()
if (autotraitor_cooldown)
autotraitor_cooldown--
else
autotraitor_cooldown = 900//15 minutes
message_admins("Dynamic Mode: Checking if we can turn someone into a traitor...")
mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autotraitor)

View File

@@ -34,7 +34,8 @@ var/veil_thickness = CULT_PROLOGUE
M.special_role = "Cultist"
/datum/faction/bloodcult/OnPostSetup()
..()
for(var/datum/role/R in members)
R.OnPostSetup(FALSE)
initialize_cultwords()
//to recode later on

View File

@@ -68,6 +68,23 @@
M.take_uplink()
to_chat(M.current, "<span class='warning'>You have been stripped of your uplink.</span>")
/datum/role/traitor/Greet(var/greeting,var/custom)
if(!greeting)
return
var/icon/logo = icon('icons/logos.dmi', logo_state)
switch(greeting)
if (GREET_ROUNDSTART)
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> <span class='danger'>You are a Syndicate agent, a Traitor.</span>")
if (GREET_AUTOTATOR)
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> <span class='danger'>Your memory clears up as you remember your identity as a sleeper agent of the Syndicate. It's time to pay your debt to them. You are now a Traitor.</span>")
if (GREET_LATEJOIN)
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> <span class='danger'>As a Syndicate agent, you are to infiltrate the crew and accomplish your objectives at all cost. You are a Traitor.</span>")
else
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> <span class='danger'>You are a Traitor.</span>")
to_chat(antag.current, "<span class='info'><a HREF='?src=\ref[antag.current];getwiki=[wikiroute]'>(Wiki Guide)</a></span>")
//________________________________________________

View File

@@ -679,6 +679,21 @@ var/global/floorIsLava = 0
"}
if(master_mode == "secret")
dat += "<A href='?src=\ref[src];f_secret=1'>(Force Secret Mode)</A><br>"
if(master_mode == "Dynamic Mode")
if(ticker.current_state == GAME_STATE_PREGAME)
dat += "<A href='?src=\ref[src];f_dynamic_roundstart=1'>(Force Roundstart Rulesets)</A><br>"
if (forced_roundstart_ruleset.len > 0)
for(var/datum/dynamic_ruleset/roundstart/rule in forced_roundstart_ruleset)
dat += {"<A href='?src=\ref[src];f_dynamic_roundstart_remove=\ref[rule]'>-> [rule.name] <-</A><br>"}
dat += "<A href='?src=\ref[src];f_dynamic_roundstart_clear=1'>(Clear Rulesets)</A><br>"
else
dat += "<A href='?src=\ref[src];f_dynamic_latejoin=1'>(Force Next Latejoin Ruleset)</A><br>"
if (ticker && ticker.mode && istype(ticker.mode,/datum/gamemode/dynamic))
var/datum/gamemode/dynamic/mode = ticker.mode
if (mode.forced_latejoin_rule)
dat += {"<A href='?src=\ref[src];f_dynamic_latejoin_clear=1'>-> [mode.forced_latejoin_rule.name] <-</A><br>"}
dat += "<A href='?src=\ref[src];f_dynamic_midround=1'>(Execute Midround Ruleset!)</A><br>"
dat += {"
<hr />

View File

@@ -1447,6 +1447,98 @@
dat += {"Now: [secret_force_mode]"}
usr << browse(dat, "window=f_secret")
else if(href_list["f_dynamic_roundstart"])
if(!check_rights(R_ADMIN))
return
if(ticker && ticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(master_mode != "Dynamic Mode")
return alert(usr, "The game mode has to be Dynamic Mode!", null, null, null, null)
var/roundstart_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
var/datum/dynamic_ruleset/roundstart/newrule = new rule()
roundstart_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules
if (added_rule)
forced_roundstart_ruleset += roundstart_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.")
message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1)
Game()
else if(href_list["f_dynamic_roundstart_clear"])
if(!check_rights(R_ADMIN))
return
forced_roundstart_ruleset = list()
Game()
log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.")
message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1)
else if(href_list["f_dynamic_roundstart_remove"])
if(!check_rights(R_ADMIN))
return
var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"])
forced_roundstart_ruleset -= rule
Game()
log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.")
message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1)
else if(href_list["f_dynamic_latejoin"])
if(!check_rights(R_ADMIN))
return
if(!ticker || !ticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
if(master_mode != "Dynamic Mode")
return alert(usr, "The game mode has to be Dynamic Mode!", null, null, null, null)
var/latejoin_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin))
var/datum/dynamic_ruleset/latejoin/newrule = new rule()
latejoin_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules
if (added_rule)
var/datum/gamemode/dynamic/mode = ticker.mode
mode.forced_latejoin_rule = latejoin_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.")
message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1)
Game()
else if(href_list["f_dynamic_latejoin_clear"])
if(!check_rights(R_ADMIN))
return
if (ticker && ticker.mode && istype(ticker.mode,/datum/gamemode/dynamic))
var/datum/gamemode/dynamic/mode = ticker.mode
mode.forced_latejoin_rule = null
Game()
log_admin("[key_name(usr)] cleared the forced latejoin ruleset.")
message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1)
else if(href_list["f_dynamic_midround"])
if(!check_rights(R_ADMIN))
return
if(!ticker || !ticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
if(master_mode != "Dynamic Mode")
return alert(usr, "The game mode has to be Dynamic Mode!", null, null, null, null)
var/midround_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/midround))
var/datum/dynamic_ruleset/midround/newrule = new rule()
midround_rules[newrule.name] = rule
var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules
if (added_rule)
var/datum/gamemode/dynamic/mode = ticker.mode
log_admin("[key_name(usr)] executed the [added_rule] ruleset.")
message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1)
mode.picking_specific_rule(midround_rules[added_rule],1)
else if(href_list["c_mode2"])
if(!check_rights(R_ADMIN|R_SERVER))
return

View File

@@ -1 +1 @@
sandbox
Dynamic Mode

View File

@@ -282,6 +282,11 @@
#include "code\datums\gamemode\traitor.dm"
#include "code\datums\gamemode\vampire_gamemode.dm"
#include "code\datums\gamemode\wizard.dm"
#include "code\datums\gamemode\dynamic\dynamic.dm"
#include "code\datums\gamemode\dynamic\dynamic_rulesets.dm"
#include "code\datums\gamemode\dynamic\dynamic_rulesets_latejoin.dm"
#include "code\datums\gamemode\dynamic\dynamic_rulesets_midround.dm"
#include "code\datums\gamemode\dynamic\dynamic_rulesets_roundstart.dm"
#include "code\datums\gamemode\factions\faction.dm"
#include "code\datums\gamemode\factions\malf.dm"
#include "code\datums\gamemode\factions\vampire_faction.dm"