mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-25 17:12:12 +00:00
## About The Pull Request Adds a new station trait: Station-Wide Background Checks! It does two things: 1. Blocks most crew-side antagonists. No traitors, changelings, spies, heretics, etc. You won't be able to fully trust your crew though, as Space changelings, Paradox Clones, Obsesseds and Blob Infected are excempted crew-antags since a background check doesn't really help here. Other antagonists still spawn: pirates, revenants, blobs, aliens, nukies, wizards etc. Expect a LOT more of these, as Dynamic is gonna put threat somewhere... 2. Reduces dynamics threat slightly, configurable per server, but defaults to 15. It is essentially the first "dynamic gamemode". ## Why It's Good For The Game Blocking crew antagonists changes the shifts dynamic, similair to old warops. Security can "trust" crew to not be antagonists, and instead can focus more on petty crimes and hunting down external threats. Due to the increased chance of external threats and reduced chance of internal threats, the crew can focus its defenses outwards. Don't worry about your coworker killing you (intentionally/probably), but do worry a lot more about the pirates trying to break through your hull, or alien nests growing in virology. I've also reduced total threat count slightly because the idea of 90 threat being dumped into ghost spawns kinda terrifies me and I do want people to be able to let their guard down a slight bit. It can be reduced/disabled for servers that already tend to lower threats. I think it's a lot of fun to change the paranoia dynamic, and a fun deviation from a normal round of spaceman13. ## Changelog 🆑 add: Station-Wide Background Checks (station trait, rare): Disables crew antagonists, but get a lot more non-crew antagonists /🆑 I want to do more like these (this was just an example I threw into discord to annoy @Mothblocks but I realized I kinda liked), and this is a good opportunity to gather community feedback and see how it plays! --------- Co-authored-by: carlarctg <53100513+carlarctg@users.noreply.github.com>
742 lines
24 KiB
Plaintext
742 lines
24 KiB
Plaintext
GLOBAL_VAR_INIT(revolutionary_win, FALSE)
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// SYNDICATE TRAITORS //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/traitor
|
|
name = "Traitors"
|
|
antag_flag = ROLE_TRAITOR
|
|
antag_datum = /datum/antagonist/traitor
|
|
minimum_required_age = 0
|
|
protected_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
)
|
|
required_candidates = 1
|
|
weight = 5
|
|
cost = 8 // Avoid raising traitor threat above this, as it is the default low cost ruleset.
|
|
scaling_cost = 9
|
|
requirements = list(8,8,8,8,8,8,8,8,8,8)
|
|
antag_cap = list("denominator" = 38)
|
|
var/autotraitor_cooldown = (15 MINUTES)
|
|
|
|
/datum/dynamic_ruleset/roundstart/traitor/pre_execute(population)
|
|
. = ..()
|
|
for (var/i in 1 to get_antag_cap_scaling_included(population))
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/M = pick_n_take(candidates)
|
|
assigned += M.mind
|
|
M.mind.special_role = ROLE_TRAITOR
|
|
M.mind.restricted_roles = restricted_roles
|
|
GLOB.pre_setup_antags += M.mind
|
|
return TRUE
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// MALFUNCTIONING AI //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/malf_ai
|
|
name = "Malfunctioning AI"
|
|
antag_flag = ROLE_MALF
|
|
antag_datum = /datum/antagonist/malf_ai
|
|
minimum_required_age = 14
|
|
exclusive_roles = list(JOB_AI)
|
|
required_candidates = 1
|
|
weight = 3
|
|
cost = 18
|
|
requirements = list(101,101,101,80,60,50,30,20,10,10)
|
|
antag_cap = 1
|
|
flags = HIGH_IMPACT_RULESET
|
|
|
|
/datum/dynamic_ruleset/roundstart/malf_ai/ready(forced)
|
|
var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
|
|
|
|
// If we're not forced, we're going to make sure we can actually have an AI in this shift,
|
|
if(!forced && min(ai_job.total_positions - ai_job.current_positions, ai_job.spawn_positions) <= 0)
|
|
log_dynamic("FAIL: [src] could not run, because there is nobody who wants to be an AI")
|
|
return FALSE
|
|
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/roundstart/malf_ai/pre_execute(population)
|
|
. = ..()
|
|
|
|
var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
|
|
// Maybe a bit too pedantic, but there should never be more malf AIs than there are available positions, spawn positions or antag cap allocations.
|
|
var/num_malf = min(get_antag_cap(population), min(ai_job.total_positions - ai_job.current_positions, ai_job.spawn_positions))
|
|
for (var/i in 1 to num_malf)
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/new_malf = pick_n_take(candidates)
|
|
assigned += new_malf.mind
|
|
new_malf.mind.special_role = ROLE_MALF
|
|
GLOB.pre_setup_antags += new_malf.mind
|
|
// We need an AI for the malf roundstart ruleset to execute. This means that players who get selected as malf AI get priority, because antag selection comes before role selection.
|
|
LAZYADDASSOC(SSjob.dynamic_forced_occupations, new_malf, "AI")
|
|
return TRUE
|
|
|
|
//////////////////////////////////////////
|
|
// //
|
|
// BLOOD BROTHERS //
|
|
// //
|
|
//////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/traitorbro
|
|
name = "Blood Brothers"
|
|
antag_flag = ROLE_BROTHER
|
|
antag_datum = /datum/antagonist/brother
|
|
protected_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
)
|
|
weight = 5
|
|
cost = 8
|
|
scaling_cost = 15
|
|
requirements = list(40,30,30,20,20,15,15,15,10,10)
|
|
antag_cap = 1
|
|
|
|
/datum/dynamic_ruleset/roundstart/traitorbro/pre_execute(population)
|
|
. = ..()
|
|
|
|
for (var/i in 1 to get_antag_cap_scaling_included(population))
|
|
var/mob/candidate = pick_n_take(candidates)
|
|
if (isnull(candidate))
|
|
break
|
|
|
|
assigned += candidate.mind
|
|
candidate.mind.restricted_roles = restricted_roles
|
|
candidate.mind.special_role = ROLE_BROTHER
|
|
GLOB.pre_setup_antags += candidate.mind
|
|
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/traitorbro/execute()
|
|
for (var/datum/mind/mind in assigned)
|
|
new /datum/team/brother_team(mind)
|
|
GLOB.pre_setup_antags -= mind
|
|
|
|
return TRUE
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// CHANGELINGS //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/changeling
|
|
name = "Changelings"
|
|
antag_flag = ROLE_CHANGELING
|
|
antag_datum = /datum/antagonist/changeling
|
|
protected_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
)
|
|
required_candidates = 1
|
|
weight = 3
|
|
cost = 16
|
|
scaling_cost = 10
|
|
requirements = list(70,70,60,50,40,20,20,10,10,10)
|
|
antag_cap = list("denominator" = 29)
|
|
|
|
/datum/dynamic_ruleset/roundstart/changeling/pre_execute(population)
|
|
. = ..()
|
|
for (var/i in 1 to get_antag_cap_scaling_included(population))
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/M = pick_n_take(candidates)
|
|
assigned += M.mind
|
|
M.mind.restricted_roles = restricted_roles
|
|
M.mind.special_role = ROLE_CHANGELING
|
|
GLOB.pre_setup_antags += M.mind
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/changeling/execute()
|
|
for(var/datum/mind/changeling in assigned)
|
|
var/datum/antagonist/changeling/new_antag = new antag_datum()
|
|
changeling.add_antag_datum(new_antag)
|
|
GLOB.pre_setup_antags -= changeling
|
|
return TRUE
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// HERETICS //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/heretics
|
|
name = "Heretics"
|
|
antag_flag = ROLE_HERETIC
|
|
antag_datum = /datum/antagonist/heretic
|
|
protected_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
)
|
|
required_candidates = 1
|
|
weight = 3
|
|
cost = 10
|
|
scaling_cost = 9
|
|
requirements = list(101,101,60,30,30,25,20,15,10,10)
|
|
antag_cap = list("denominator" = 24)
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_HERETIC_SACRIFICE)
|
|
|
|
|
|
/datum/dynamic_ruleset/roundstart/heretics/pre_execute(population)
|
|
. = ..()
|
|
var/num_ecult = get_antag_cap(population) * (scaled_times + 1)
|
|
|
|
for (var/i = 1 to num_ecult)
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/picked_candidate = pick_n_take(candidates)
|
|
assigned += picked_candidate.mind
|
|
picked_candidate.mind.restricted_roles = restricted_roles
|
|
picked_candidate.mind.special_role = ROLE_HERETIC
|
|
GLOB.pre_setup_antags += picked_candidate.mind
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/heretics/execute()
|
|
|
|
for(var/c in assigned)
|
|
var/datum/mind/cultie = c
|
|
var/datum/antagonist/heretic/new_antag = new antag_datum()
|
|
cultie.add_antag_datum(new_antag)
|
|
GLOB.pre_setup_antags -= cultie
|
|
|
|
return TRUE
|
|
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// WIZARDS //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
// Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round.
|
|
/datum/dynamic_ruleset/roundstart/wizard
|
|
name = "Wizard"
|
|
antag_flag = ROLE_WIZARD
|
|
antag_datum = /datum/antagonist/wizard
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
flags = HIGH_IMPACT_RULESET
|
|
minimum_required_age = 14
|
|
restricted_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_HEAD_OF_SECURITY,
|
|
) // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted
|
|
required_candidates = 1
|
|
weight = 2
|
|
cost = 20
|
|
requirements = list(90,90,90,80,60,40,30,20,10,10)
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_WIZARDDEN)
|
|
|
|
/datum/dynamic_ruleset/roundstart/wizard/ready(forced = FALSE)
|
|
if(!check_candidates())
|
|
return FALSE
|
|
if(!length(GLOB.wizardstart))
|
|
log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.")
|
|
message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.")
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/roundstart/wizard/round_result()
|
|
for(var/datum/antagonist/wizard/wiz in GLOB.antagonists)
|
|
var/mob/living/real_wiz = wiz.owner?.current
|
|
if(isnull(real_wiz))
|
|
continue
|
|
|
|
var/turf/wiz_location = get_turf(real_wiz)
|
|
// If this wiz is alive AND not in an away level, then we know not all wizards are dead and can leave entirely
|
|
if(considered_alive(wiz.owner) && wiz_location && !is_away_level(wiz_location.z))
|
|
return
|
|
|
|
SSticker.news_report = WIZARD_KILLED
|
|
|
|
/datum/dynamic_ruleset/roundstart/wizard/pre_execute()
|
|
. = ..()
|
|
if(GLOB.wizardstart.len == 0)
|
|
return FALSE
|
|
var/mob/M = pick_n_take(candidates)
|
|
if (M)
|
|
assigned += M.mind
|
|
M.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
|
|
M.mind.special_role = ROLE_WIZARD
|
|
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/wizard/execute()
|
|
for(var/datum/mind/M in assigned)
|
|
M.current.forceMove(pick(GLOB.wizardstart))
|
|
M.add_antag_datum(new antag_datum())
|
|
return TRUE
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// BLOOD CULT //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/bloodcult
|
|
name = "Blood Cult"
|
|
antag_flag = ROLE_CULTIST
|
|
antag_datum = /datum/antagonist/cult
|
|
minimum_required_age = 14
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CAPTAIN,
|
|
JOB_CHAPLAIN,
|
|
JOB_CYBORG,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_PERSONNEL,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
required_candidates = 2
|
|
weight = 3
|
|
cost = 20
|
|
requirements = list(100,90,80,60,40,30,10,10,10,10)
|
|
flags = HIGH_IMPACT_RULESET
|
|
antag_cap = list("denominator" = 20, "offset" = 1)
|
|
var/datum/team/cult/main_cult
|
|
|
|
/datum/dynamic_ruleset/roundstart/bloodcult/ready(population, forced = FALSE)
|
|
required_candidates = get_antag_cap(population)
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/roundstart/bloodcult/pre_execute(population)
|
|
. = ..()
|
|
var/cultists = get_antag_cap(population)
|
|
for(var/cultists_number = 1 to cultists)
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/M = pick_n_take(candidates)
|
|
assigned += M.mind
|
|
M.mind.special_role = ROLE_CULTIST
|
|
M.mind.restricted_roles = restricted_roles
|
|
GLOB.pre_setup_antags += M.mind
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/bloodcult/execute()
|
|
main_cult = new
|
|
for(var/datum/mind/M in assigned)
|
|
var/datum/antagonist/cult/new_cultist = new antag_datum()
|
|
new_cultist.cult_team = main_cult
|
|
new_cultist.give_equipment = TRUE
|
|
M.add_antag_datum(new_cultist)
|
|
GLOB.pre_setup_antags -= M
|
|
main_cult.setup_objectives()
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/bloodcult/round_result()
|
|
if(main_cult.check_cult_victory())
|
|
SSticker.mode_result = "win - cult win"
|
|
SSticker.news_report = CULT_SUMMON
|
|
return
|
|
|
|
SSticker.mode_result = "loss - staff stopped the cult"
|
|
|
|
if(main_cult.size_at_maximum == 0)
|
|
CRASH("Cult team existed with a size_at_maximum of 0 at round end!")
|
|
|
|
// If more than a certain ratio of our cultists have escaped, give the "cult escape" resport.
|
|
// Otherwise, give the "cult failure" report.
|
|
var/ratio_to_be_considered_escaped = 0.5
|
|
var/escaped_cultists = 0
|
|
for(var/datum/mind/escapee as anything in main_cult.members)
|
|
if(considered_escaped(escapee))
|
|
escaped_cultists++
|
|
|
|
SSticker.news_report = (escaped_cultists / main_cult.size_at_maximum) >= ratio_to_be_considered_escaped ? CULT_ESCAPE : CULT_FAILURE
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// NUCLEAR OPERATIVES //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear
|
|
name = "Nuclear Emergency"
|
|
antag_flag = ROLE_OPERATIVE
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
antag_datum = /datum/antagonist/nukeop
|
|
var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader
|
|
minimum_required_age = 14
|
|
restricted_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_HEAD_OF_SECURITY,
|
|
) // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted
|
|
required_candidates = 5
|
|
weight = 3
|
|
cost = 20
|
|
requirements = list(90,90,90,80,60,40,30,20,10,10)
|
|
flags = HIGH_IMPACT_RULESET
|
|
antag_cap = list("denominator" = 18, "offset" = 1)
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_NUKIEBASE)
|
|
var/required_role = ROLE_NUCLEAR_OPERATIVE
|
|
var/datum/team/nuclear/nuke_team
|
|
///The job type to dress up our nuclear operative as.
|
|
var/datum/job/job_type = /datum/job/nuclear_operative
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear/ready(population, forced = FALSE)
|
|
required_candidates = get_antag_cap(population)
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear/pre_execute(population)
|
|
. = ..()
|
|
// If ready() did its job, candidates should have 5 or more members in it
|
|
var/operatives = get_antag_cap(population)
|
|
for(var/operatives_number = 1 to operatives)
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/M = pick_n_take(candidates)
|
|
assigned += M.mind
|
|
M.mind.set_assigned_role(SSjob.GetJobType(job_type))
|
|
M.mind.special_role = required_role
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear/execute()
|
|
var/datum/mind/most_experienced = get_most_experienced(assigned, required_role)
|
|
if(!most_experienced)
|
|
most_experienced = assigned[1]
|
|
var/datum/antagonist/nukeop/leader/leader = most_experienced.add_antag_datum(antag_leader_datum)
|
|
nuke_team = leader.nuke_team
|
|
for(var/datum/mind/assigned_player in assigned)
|
|
if(assigned_player == most_experienced)
|
|
continue
|
|
var/datum/antagonist/nukeop/new_op = new antag_datum()
|
|
assigned_player.add_antag_datum(new_op)
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear/round_result()
|
|
var/result = nuke_team.get_result()
|
|
switch(result)
|
|
if(NUKE_RESULT_FLUKE)
|
|
SSticker.mode_result = "loss - syndicate nuked - disk secured"
|
|
SSticker.news_report = NUKE_SYNDICATE_BASE
|
|
if(NUKE_RESULT_NUKE_WIN)
|
|
SSticker.mode_result = "win - syndicate nuke"
|
|
SSticker.news_report = STATION_DESTROYED_NUKE
|
|
if(NUKE_RESULT_NOSURVIVORS)
|
|
SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time"
|
|
SSticker.news_report = STATION_DESTROYED_NUKE
|
|
if(NUKE_RESULT_WRONG_STATION)
|
|
SSticker.mode_result = "halfwin - blew wrong station"
|
|
SSticker.news_report = NUKE_MISS
|
|
if(NUKE_RESULT_WRONG_STATION_DEAD)
|
|
SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time"
|
|
SSticker.news_report = NUKE_MISS
|
|
if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
|
|
SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead"
|
|
SSticker.news_report = OPERATIVES_KILLED
|
|
if(NUKE_RESULT_CREW_WIN)
|
|
SSticker.mode_result = "loss - evacuation - disk secured"
|
|
SSticker.news_report = OPERATIVES_KILLED
|
|
if(NUKE_RESULT_DISK_LOST)
|
|
SSticker.mode_result = "halfwin - evacuation - disk not secured"
|
|
SSticker.news_report = OPERATIVE_SKIRMISH
|
|
if(NUKE_RESULT_DISK_STOLEN)
|
|
SSticker.mode_result = "halfwin - detonation averted"
|
|
SSticker.news_report = OPERATIVE_SKIRMISH
|
|
else
|
|
SSticker.mode_result = "halfwin - interrupted"
|
|
SSticker.news_report = OPERATIVE_SKIRMISH
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// REVS //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/revs
|
|
name = "Revolution"
|
|
persistent = TRUE
|
|
antag_flag = ROLE_REV_HEAD
|
|
antag_flag_override = ROLE_REV_HEAD
|
|
antag_datum = /datum/antagonist/rev/head
|
|
minimum_required_age = 14
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CAPTAIN,
|
|
JOB_CHIEF_ENGINEER,
|
|
JOB_CHIEF_MEDICAL_OFFICER,
|
|
JOB_CYBORG,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_PERSONNEL,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_QUARTERMASTER,
|
|
JOB_RESEARCH_DIRECTOR,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
required_candidates = 3
|
|
weight = 3
|
|
delay = 7 MINUTES
|
|
cost = 20
|
|
requirements = list(101,101,70,40,30,20,10,10,10,10)
|
|
antag_cap = 3
|
|
flags = HIGH_IMPACT_RULESET
|
|
blocking_rules = list(/datum/dynamic_ruleset/latejoin/provocateur)
|
|
// I give up, just there should be enough heads with 35 players...
|
|
minimum_players = 35
|
|
var/datum/team/revolution/revolution
|
|
var/finished = FALSE
|
|
|
|
/datum/dynamic_ruleset/roundstart/revs/pre_execute(population)
|
|
. = ..()
|
|
var/max_candidates = get_antag_cap(population)
|
|
for(var/i = 1 to max_candidates)
|
|
if(candidates.len <= 0)
|
|
break
|
|
var/mob/M = pick_n_take(candidates)
|
|
assigned += M.mind
|
|
M.mind.restricted_roles = restricted_roles
|
|
M.mind.special_role = antag_flag
|
|
GLOB.pre_setup_antags += M.mind
|
|
return TRUE
|
|
|
|
/datum/dynamic_ruleset/roundstart/revs/execute()
|
|
revolution = new()
|
|
for(var/datum/mind/M in assigned)
|
|
GLOB.pre_setup_antags -= M
|
|
if(check_eligible(M))
|
|
var/datum/antagonist/rev/head/new_head = new antag_datum()
|
|
new_head.give_flash = TRUE
|
|
new_head.give_hud = TRUE
|
|
new_head.remove_clumsy = TRUE
|
|
M.add_antag_datum(new_head,revolution)
|
|
else
|
|
assigned -= M
|
|
log_dynamic("[ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
|
|
if(revolution.members.len)
|
|
revolution.update_objectives()
|
|
revolution.update_rev_heads()
|
|
SSshuttle.registerHostileEnvironment(revolution)
|
|
return TRUE
|
|
log_dynamic("[ruletype] [name] failed to get any eligible headrevs. Refunding [cost] threat.")
|
|
return FALSE
|
|
|
|
/datum/dynamic_ruleset/roundstart/revs/clean_up()
|
|
qdel(revolution)
|
|
..()
|
|
|
|
/datum/dynamic_ruleset/roundstart/revs/rule_process()
|
|
var/winner = revolution.process_victory()
|
|
if (isnull(winner))
|
|
return
|
|
|
|
finished = winner
|
|
|
|
if(winner == REVOLUTION_VICTORY)
|
|
GLOB.revolutionary_win = TRUE
|
|
|
|
return RULESET_STOP_PROCESSING
|
|
|
|
/// Checks for revhead loss conditions and other antag datums.
|
|
/datum/dynamic_ruleset/roundstart/revs/proc/check_eligible(datum/mind/M)
|
|
var/turf/T = get_turf(M.current)
|
|
if(!considered_afk(M) && considered_alive(M) && is_station_level(T.z) && !M.antag_datums?.len && !HAS_TRAIT(M, TRAIT_MINDSHIELD))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/dynamic_ruleset/roundstart/revs/round_result()
|
|
revolution.round_result(finished)
|
|
|
|
// Admin only rulesets. The threat requirement is 101 so it is not possible to roll them.
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// EXTENDED //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/extended
|
|
name = "Extended"
|
|
antag_flag = null
|
|
antag_datum = null
|
|
restricted_roles = list()
|
|
required_candidates = 0
|
|
weight = 3
|
|
cost = 0
|
|
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
|
flags = LONE_RULESET
|
|
|
|
/datum/dynamic_ruleset/roundstart/extended/pre_execute()
|
|
. = ..()
|
|
message_admins("Starting a round of extended.")
|
|
log_game("Starting a round of extended.")
|
|
SSdynamic.spend_roundstart_budget(SSdynamic.round_start_budget)
|
|
SSdynamic.spend_midround_budget(SSdynamic.mid_round_budget)
|
|
SSdynamic.threat_log += "[worldtime2text()]: Extended ruleset set threat to 0."
|
|
return TRUE
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// CLOWN OPS //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear/clown_ops
|
|
name = "Clown Operatives"
|
|
antag_datum = /datum/antagonist/nukeop/clownop
|
|
antag_flag = ROLE_CLOWN_OPERATIVE
|
|
antag_flag_override = ROLE_OPERATIVE
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
|
|
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
|
required_role = ROLE_CLOWN_OPERATIVE
|
|
job_type = /datum/job/clown_operative
|
|
|
|
/datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute()
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
|
|
var/list/nukes = SSmachines.get_machines_by_type(/obj/machinery/nuclearbomb/syndicate)
|
|
for(var/obj/machinery/nuclearbomb/syndicate/nuke as anything in nukes)
|
|
new /obj/machinery/nuclearbomb/syndicate/bananium(nuke.loc)
|
|
qdel(nuke)
|
|
|
|
//////////////////////////////////////////////
|
|
// //
|
|
// METEOR //
|
|
// //
|
|
//////////////////////////////////////////////
|
|
|
|
/datum/dynamic_ruleset/roundstart/meteor
|
|
name = "Meteor"
|
|
persistent = TRUE
|
|
required_candidates = 0
|
|
weight = 3
|
|
cost = 0
|
|
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
|
flags = LONE_RULESET
|
|
var/meteordelay = 2000
|
|
var/nometeors = FALSE
|
|
var/rampupdelta = 5
|
|
|
|
/datum/dynamic_ruleset/roundstart/meteor/rule_process()
|
|
if(nometeors || meteordelay > world.time - SSticker.round_start_time)
|
|
return
|
|
|
|
var/list/wavetype = GLOB.meteors_normal
|
|
var/meteorminutes = (world.time - SSticker.round_start_time - meteordelay) / 10 / 60
|
|
|
|
if (prob(meteorminutes))
|
|
wavetype = GLOB.meteors_threatening
|
|
|
|
if (prob(meteorminutes/2))
|
|
wavetype = GLOB.meteors_catastrophic
|
|
|
|
var/ramp_up_final = clamp(round(meteorminutes/rampupdelta), 1, 10)
|
|
|
|
spawn_meteors(ramp_up_final, wavetype)
|
|
|
|
/// Ruleset for Nations
|
|
/datum/dynamic_ruleset/roundstart/nations
|
|
name = "Nations"
|
|
required_candidates = 0
|
|
weight = 0 //admin only (and for good reason)
|
|
cost = 0
|
|
flags = LONE_RULESET | ONLY_RULESET
|
|
|
|
/datum/dynamic_ruleset/roundstart/nations/execute()
|
|
. = ..()
|
|
//notably assistant is not in this list to prevent the round turning into BARBARISM instantly, and silicon is in this list for UN
|
|
var/list/department_types = list(
|
|
/datum/job_department/silicon, //united nations
|
|
/datum/job_department/cargo,
|
|
/datum/job_department/engineering,
|
|
/datum/job_department/medical,
|
|
/datum/job_department/science,
|
|
/datum/job_department/security,
|
|
/datum/job_department/service,
|
|
)
|
|
|
|
for(var/department_type in department_types)
|
|
create_separatist_nation(department_type, announcement = FALSE, dangerous = FALSE, message_admins = FALSE)
|
|
|
|
GLOB.round_default_lawset = /datum/ai_laws/united_nations
|
|
|
|
/datum/dynamic_ruleset/roundstart/spies
|
|
name = "Spies"
|
|
antag_flag = ROLE_SPY
|
|
antag_datum = /datum/antagonist/spy
|
|
minimum_required_age = 0
|
|
protected_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_PERSONNEL, // AA = bad
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
)
|
|
required_candidates = 3 // lives or dies by there being a few spies
|
|
weight = 5
|
|
cost = 8
|
|
scaling_cost = 4
|
|
minimum_players = 10
|
|
antag_cap = list("denominator" = 20, "offset" = 1)
|
|
requirements = list(8, 8, 8, 8, 8, 8, 8, 8, 8, 8)
|
|
/// What fraction is added to the antag cap for each additional scale
|
|
var/fraction_per_scale = 0.2
|
|
|
|
/datum/dynamic_ruleset/roundstart/spies/pre_execute(population)
|
|
for(var/i in 1 to get_antag_cap_scaling_included(population))
|
|
if(length(candidates) <= 0)
|
|
break
|
|
var/mob/picked_player = pick_n_take(candidates)
|
|
assigned += picked_player.mind
|
|
picked_player.mind.special_role = ROLE_SPY
|
|
picked_player.mind.restricted_roles = restricted_roles
|
|
GLOB.pre_setup_antags += picked_player.mind
|
|
return TRUE
|
|
|
|
// Scaling adds a fraction of the amount of additional spies rather than the full amount.
|
|
/datum/dynamic_ruleset/roundstart/spies/get_scaling_antag_cap(population)
|
|
return ceil(..() * fraction_per_scale)
|