Files
vgstation13/code/datums/gamemode/dynamic/dynamic_rulesets.dm
DeityLink 98071b8de4 [Role Datums] Dynamic Mode (#19548)
* Dynamic Mode

* no rules

* latejoin

* midround

* Traitor

* secs

* process

* better

* aight
2018-09-09 01:00:13 +01:00

148 lines
7.5 KiB
Plaintext

/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