Files
Bubberstation/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
Rohesie 6c4134d1ea Job refactor 2: less hardcoded lists (#60578)
* Job refactor 2: less hardcoded lists

* Obsessed can happen
2021-08-05 21:13:05 +02:00

894 lines
34 KiB
Plaintext

/// Probability the AI going malf will be accompanied by an ion storm announcement and some ion laws.
#define MALF_ION_PROB 33
/// The probability to replace an existing law with an ion law instead of adding a new ion law.
#define REPLACE_LAW_WITH_ION_PROB 10
//////////////////////////////////////////////
// //
// MIDROUND RULESETS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround // Can be drafted once in a while during a round
ruletype = "Midround"
/// If the ruleset should be restricted from ghost roles.
var/restrict_ghost_roles = TRUE
/// What mob type the ruleset is restricted to.
var/required_type = /mob/living/carbon/human
var/list/living_players = list()
var/list/living_antags = list()
var/list/dead_players = list()
var/list/list_observers = list()
/datum/dynamic_ruleset/midround/from_ghosts
weight = 0
required_type = /mob/dead/observer
/// Whether the ruleset should call generate_ruleset_body or not.
var/makeBody = TRUE
/// The rule needs this many applicants to be properly executed.
var/required_applicants = 1
/datum/dynamic_ruleset/midround/trim_candidates()
living_players = trim_list(GLOB.alive_player_list)
living_antags = trim_list(GLOB.current_living_antags)
dead_players = trim_list(GLOB.dead_player_list)
list_observers = trim_list(GLOB.current_observers_list)
/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list())
var/list/trimmed_list = L.Copy()
for(var/mob/M in trimmed_list)
if (!istype(M, required_type))
trimmed_list.Remove(M)
continue
if (!M.client) // Are they connected?
trimmed_list.Remove(M)
continue
if(M.client.get_remaining_days(minimum_required_age) > 0)
trimmed_list.Remove(M)
continue
if(antag_flag_override)
if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
trimmed_list.Remove(M)
continue
else
if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE)))
trimmed_list.Remove(M)
continue
if (M.mind)
if (restrict_ghost_roles && (M.mind.assigned_role.title in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
trimmed_list.Remove(M)
continue
if (M.mind.assigned_role.title in restricted_roles) // Does their job allow it?
trimmed_list.Remove(M)
continue
if ((exclusive_roles.len > 0) && !(M.mind.assigned_role.title in exclusive_roles)) // 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
// IMPORTANT, since /datum/dynamic_ruleset/midround may accept candidates from both living, dead, and even antag players, you need to manually check whether there are enough candidates
// (see /datum/dynamic_ruleset/midround/autotraitor/ready(forced = FALSE) for example)
/datum/dynamic_ruleset/midround/ready(forced = FALSE)
if (!forced)
var/job_check = 0
if (enemy_roles.len > 0)
for (var/mob/M in GLOB.alive_player_list)
if (M.stat == DEAD || !M.client)
continue // Dead/disconnected players cannot count as opponents
if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
var/threat = round(mode.threat_level/10)
if (job_check < required_enemies[threat])
return FALSE
return TRUE
/datum/dynamic_ruleset/midround/from_ghosts/execute()
var/list/possible_candidates = list()
possible_candidates.Add(dead_players)
possible_candidates.Add(list_observers)
send_applications(possible_candidates)
if(assigned.len > 0)
return TRUE
else
return FALSE
/// This sends a poll to ghosts if they want to be a ghost spawn from a ruleset.
/datum/dynamic_ruleset/midround/from_ghosts/proc/send_applications(list/possible_volunteers = list())
if (possible_volunteers.len <= 0) // This shouldn't happen, as ready() should return FALSE if there is not a single valid candidate
message_admins("Possible volunteers was 0. This shouldn't appear, because of ready(), unless you forced it!")
return
message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, antag_flag_override ? antag_flag_override : antag_flag, poll_time = 300)
if(!candidates || candidates.len <= 0)
mode.dynamic_log("The ruleset [name] received no applications.")
mode.executed_rules -= src
attempt_replacement()
return
message_admins("[candidates.len] players volunteered for the ruleset [name].")
log_game("DYNAMIC: [candidates.len] players volunteered for [name].")
review_applications()
/// Here is where you can check if your ghost applicants are valid for the ruleset.
/// Called by send_applications().
/datum/dynamic_ruleset/midround/from_ghosts/proc/review_applications()
if(candidates.len < required_applicants)
mode.executed_rules -= src
return
for (var/i = 1, i <= required_candidates, i++)
if(candidates.len <= 0)
break
var/mob/applicant = pick(candidates)
candidates -= applicant
if(!isobserver(applicant))
if(applicant.stat == DEAD) // Not an observer? If they're dead, make them one.
applicant = applicant.ghostize(FALSE)
else // Not dead? Disregard them, pick a new applicant
i--
continue
if(!applicant)
i--
continue
var/mob/new_character = applicant
if (makeBody)
new_character = generate_ruleset_body(applicant)
finish_setup(new_character, i)
assigned += applicant
notify_ghosts("[new_character] has been picked for the ruleset [name]!", source = new_character, action = NOTIFY_ORBIT, header="Something Interesting!")
/datum/dynamic_ruleset/midround/from_ghosts/proc/generate_ruleset_body(mob/applicant)
var/mob/living/carbon/human/new_character = makeBody(applicant)
new_character.dna.remove_all_mutations()
return new_character
/datum/dynamic_ruleset/midround/from_ghosts/proc/finish_setup(mob/new_character, index)
var/datum/antagonist/new_role = new antag_datum()
setup_role(new_role)
new_character.mind.add_antag_datum(new_role)
new_character.mind.special_role = antag_flag
/datum/dynamic_ruleset/midround/from_ghosts/proc/setup_role(datum/antagonist/new_role)
return
/// Fired when there are no valid candidates. Will spawn a sleeper agent or latejoin traitor.
/datum/dynamic_ruleset/midround/from_ghosts/proc/attempt_replacement()
var/datum/dynamic_ruleset/midround/autotraitor/sleeper_agent = new
// Otherwise, it has a chance to fail. We don't want that, since this is already pretty unlikely.
sleeper_agent.has_failure_chance = FALSE
mode.configure_ruleset(sleeper_agent)
if (!mode.picking_specific_rule(sleeper_agent))
return
mode.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator)
//////////////////////////////////////////////
// //
// SYNDICATE TRAITORS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/autotraitor
name = "Syndicate Sleeper Agent"
antag_datum = /datum/antagonist/traitor
antag_flag = ROLE_TRAITOR
protected_roles = list("Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain")
restricted_roles = list("Cyborg", "AI", "Positronic Brain")
required_candidates = 1
weight = 7
cost = 10
requirements = list(50,40,30,20,10,10,10,10,10,10)
repeatable = TRUE
/// Whether or not this instance of sleeper agent should be randomly acceptable.
/// If TRUE, then this has a threat level% chance to succeed.
var/has_failure_chance = TRUE
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
var/player_count = GLOB.alive_player_list.len
var/antag_count = GLOB.current_living_antags.len
var/max_traitors = round(player_count / 10) + 1
// adding traitors if the antag population is getting low
var/too_little_antags = antag_count < max_traitors
if (!too_little_antags)
log_game("DYNAMIC: Too many living antags compared to living players ([antag_count] living antags, [player_count] living players, [max_traitors] max traitors)")
return FALSE
if (has_failure_chance && !prob(mode.threat_level))
log_game("DYNAMIC: Random chance to roll autotraitor failed, it was a [mode.threat_level]% chance.")
return FALSE
return ..()
/datum/dynamic_ruleset/midround/autotraitor/trim_candidates()
..()
for(var/mob/living/player in living_players)
if(issilicon(player)) // Your assigned role doesn't change when you are turned into a silicon.
living_players -= player
else if(is_centcom_level(player.z))
living_players -= player // We don't autotator people in CentCom
else if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
living_players -= player // We don't autotator people with roles already
/datum/dynamic_ruleset/midround/autotraitor/ready(forced = FALSE)
if (required_candidates > living_players.len)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/autotraitor/execute()
var/mob/M = pick(living_players)
assigned += M
living_players -= M
var/datum/antagonist/traitor/newTraitor = new
M.mind.add_antag_datum(newTraitor)
message_admins("[ADMIN_LOOKUPFLW(M)] was selected by the [name] ruleset and has been made into a midround traitor.")
log_game("DYNAMIC: [key_name(M)] was selected by the [name] ruleset and has been made into a midround traitor.")
return TRUE
//////////////////////////////////////////////
// //
// FAMILIES //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/families
name = "Family Head Aspirants"
persistent = TRUE
antag_flag = ROLE_FAMILIES
protected_roles = list("Prisoner", "Head of Personnel")
restricted_roles = list("Cyborg", "AI", "Security Officer", "Warden", "Detective", "Head of Security", "Captain")
required_candidates = 6 // gotta have 'em ALL
weight = 1
cost = 25
requirements = list(101,101,101,101,101,80,50,30,10,10)
flags = HIGH_IMPACT_RULESET
blocking_rules = list(/datum/dynamic_ruleset/roundstart/families)
minimum_players = 36
antag_cap = 6
/// A reference to the handler that is used to run pre_execute(), execute(), etc..
var/datum/gang_handler/handler
/datum/dynamic_ruleset/midround/families/trim_candidates()
..()
candidates = living_players
for(var/mob/living/player in candidates)
if(issilicon(player))
candidates -= player
else if(is_centcom_level(player.z))
candidates -= player
else if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
candidates -= player
else if(HAS_TRAIT(player, TRAIT_MINDSHIELD))
candidates -= player
/datum/dynamic_ruleset/midround/families/acceptable(population = 0, threat_level = 0)
. = ..()
if(GLOB.deaths_during_shift > round(mode.roundstart_pop_ready / 2))
return FALSE
/datum/dynamic_ruleset/midround/families/ready(forced = FALSE)
if (required_candidates > living_players.len)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/families/pre_execute()
..()
handler = new /datum/gang_handler(candidates,restricted_roles)
handler.gangs_to_generate = (antag_cap[indice_pop] / 2)
handler.gang_balance_cap = clamp((indice_pop - 3), 2, 5) // gang_balance_cap by indice_pop: (2,2,2,2,2,3,4,5,5,5)
handler.midround_ruleset = TRUE
handler.use_dynamic_timing = TRUE
return handler.pre_setup_analogue()
/datum/dynamic_ruleset/midround/families/execute()
return handler.post_setup_analogue(TRUE)
/datum/dynamic_ruleset/midround/families/clean_up()
QDEL_NULL(handler)
..()
/datum/dynamic_ruleset/midround/families/rule_process()
return handler.process_analogue()
/datum/dynamic_ruleset/midround/families/round_result()
return handler.set_round_result_analogue()
//////////////////////////////////////////////
// //
// Malfunctioning AI //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/malf
name = "Malfunctioning AI"
antag_datum = /datum/antagonist/malf_ai
antag_flag = ROLE_MALF
enemy_roles = list("Security Officer", "Warden","Detective","Head of Security", "Captain", "Scientist", "Chemist", "Research Director", "Chief Engineer")
exclusive_roles = list("AI")
required_enemies = list(4,4,4,4,4,4,2,2,2,0)
required_candidates = 1
weight = 3
cost = 35
requirements = list(101,101,80,70,60,60,50,50,40,40)
required_type = /mob/living/silicon/ai
/datum/dynamic_ruleset/midround/malf/trim_candidates()
..()
candidates = living_players
for(var/mob/living/player in candidates)
if(!isAI(player))
candidates -= player
continue
if(is_centcom_level(player.z))
candidates -= player
continue
if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
candidates -= player
/datum/dynamic_ruleset/midround/malf/execute()
if(!candidates || !candidates.len)
return FALSE
var/mob/living/silicon/ai/new_malf_ai = pick_n_take(candidates)
assigned += new_malf_ai.mind
var/datum/antagonist/malf_ai/malf_antag_datum = new
new_malf_ai.mind.special_role = antag_flag
new_malf_ai.mind.add_antag_datum(malf_antag_datum)
if(prob(MALF_ION_PROB))
priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", ANNOUNCER_IONSTORM)
if(prob(REPLACE_LAW_WITH_ION_PROB))
new_malf_ai.replace_random_law(generate_ion_law(), list(LAW_INHERENT, LAW_SUPPLIED, LAW_ION))
else
new_malf_ai.add_ion_law(generate_ion_law())
return TRUE
//////////////////////////////////////////////
// //
// WIZARD (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/wizard
name = "Wizard"
antag_datum = /datum/antagonist/wizard
antag_flag = ROLE_WIZARD
enemy_roles = list("Security Officer","Detective","Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 1
cost = 20
requirements = list(90,90,90,80,60,40,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
return FALSE
if(GLOB.wizardstart.len == 0)
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/midround/from_ghosts/wizard/finish_setup(mob/new_character, index)
..()
new_character.forceMove(pick(GLOB.wizardstart))
//////////////////////////////////////////////
// //
// NUCLEAR OPERATIVES (MIDROUND) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/nuclear
name = "Nuclear Assault"
antag_flag = ROLE_OPERATIVE
antag_datum = /datum/antagonist/nukeop
enemy_roles = list("AI", "Cyborg", "Security Officer", "Warden","Detective","Head of Security", "Captain")
required_enemies = list(3,3,3,3,3,2,1,1,0,0)
required_candidates = 5
weight = 5
cost = 35
requirements = list(90,90,90,80,60,40,30,20,10,10)
var/list/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
var/datum/team/nuclear/nuke_team
flags = HIGH_IMPACT_RULESET
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat=0)
if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules)
return FALSE // Unavailable if nuke ops were already sent at roundstart
indice_pop = min(operative_cap.len, round(living_players.len/5)+1)
required_candidates = operative_cap[indice_pop]
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index)
new_character.mind.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative))
new_character.mind.special_role = ROLE_NUCLEAR_OPERATIVE
if (index == 1) // Our first guy is the leader
var/datum/antagonist/nukeop/leader/new_role = new
nuke_team = new_role.nuke_team
new_character.mind.add_antag_datum(new_role)
else
return ..()
//////////////////////////////////////////////
// //
// BLOB (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/blob
name = "Blob"
antag_datum = /datum/antagonist/blob
antag_flag = ROLE_BLOB
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 2
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/from_ghosts/blob/generate_ruleset_body(mob/applicant)
var/body = applicant.become_overmind()
return body
/// Infects a random player, making them explode into a blob.
/datum/dynamic_ruleset/midround/blob_infection
name = "Blob Infection"
antag_datum = /datum/antagonist/blob
antag_flag = ROLE_BLOB
protected_roles = list("Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain")
restricted_roles = list("Cyborg", "AI", "Positronic Brain")
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 2
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/blob_infection/trim_candidates()
..()
candidates = living_players
for(var/mob/living/player as anything in candidates)
var/turf/player_turf = get_turf(player)
if(!player_turf || !is_station_level(player_turf.z))
candidates -= player
continue
if(player.mind && (player.mind.special_role || length(player.mind.antag_datums) > 0))
candidates -= player
/datum/dynamic_ruleset/midround/blob_infection/execute()
if(!candidates || !candidates.len)
return FALSE
var/mob/living/carbon/human/blob_antag = pick_n_take(candidates)
assigned += blob_antag.mind
blob_antag.mind.special_role = antag_flag
return ..()
//////////////////////////////////////////////
// //
// XENOMORPH (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph
name = "Alien Infestation"
antag_datum = /datum/antagonist/xeno
antag_flag = ROLE_ALIEN
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 3
cost = 10
requirements = list(101,101,101,70,50,40,20,15,10,10)
repeatable = TRUE
var/list/vents = list()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
// 50% chance of being incremented by one
required_candidates += prob(50)
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in GLOB.machines)
if(QDELETED(temp_vent))
continue
if(is_station_level(temp_vent.loc.z) && !temp_vent.welded)
var/datum/pipeline/temp_vent_parent = temp_vent.parents[1]
if(!temp_vent_parent)
continue // No parent vent
// Stops Aliens getting stuck in small networks.
// See: Security, Virology
if(temp_vent_parent.other_atmosmch.len > 20)
vents += temp_vent
if(!vents.len)
return FALSE
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant)
var/obj/vent = pick_n_take(vents)
var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
new_xeno.key = applicant.key
new_xeno.move_into_vent(vent)
message_admins("[ADMIN_LOOKUPFLW(new_xeno)] has been made into an alien by the midround ruleset.")
log_game("DYNAMIC: [key_name(new_xeno)] was spawned as an alien by the midround ruleset.")
return new_xeno
//////////////////////////////////////////////
// //
// NIGHTMARE (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/nightmare
name = "Nightmare"
antag_datum = /datum/antagonist/nightmare
antag_flag = ROLE_NIGHTMARE
antag_flag_override = ROLE_ALIEN
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 3
cost = 10
requirements = list(101,101,101,70,50,40,20,15,10,10)
repeatable = TRUE
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
for(var/X in GLOB.xeno_spawn)
var/turf/T = X
var/light_amount = T.get_lumcount()
if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD)
spawn_locs += T
if(!spawn_locs.len)
return FALSE
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/generate_ruleset_body(mob/applicant)
var/datum/mind/player_mind = new /datum/mind(applicant.key)
player_mind.active = TRUE
var/mob/living/carbon/human/S = new (pick(spawn_locs))
player_mind.transfer_to(S)
player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nightmare))
player_mind.special_role = ROLE_NIGHTMARE
player_mind.add_antag_datum(/datum/antagonist/nightmare)
S.set_species(/datum/species/shadow/nightmare)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Nightmare by the midround ruleset.")
log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.")
return S
//////////////////////////////////////////////
// //
// SPACE DRAGON (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon
name = "Space Dragon"
antag_datum = /datum/antagonist/space_dragon
antag_flag = ROLE_SPACE_DRAGON
antag_flag_override = ROLE_SPACE_DRAGON
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon/execute()
for(var/obj/effect/landmark/carpspawn/C in GLOB.landmarks_list)
spawn_locs += (C.loc)
if(!spawn_locs.len)
message_admins("No valid spawn locations found, aborting...")
return MAP_ERROR
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon/generate_ruleset_body(mob/applicant)
var/datum/mind/player_mind = new /datum/mind(applicant.key)
player_mind.active = TRUE
var/mob/living/simple_animal/hostile/space_dragon/S = new (pick(spawn_locs))
player_mind.transfer_to(S)
player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
player_mind.special_role = ROLE_SPACE_DRAGON
player_mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Space Dragon by the midround ruleset.")
log_game("DYNAMIC: [key_name(S)] was spawned as a Space Dragon by the midround ruleset.")
priority_announce("A large organic energy flux has been recorded near of [station_name()], please stand-by.", "Lifesign Alert")
return S
//////////////////////////////////////////////
// //
// ABDUCTORS (GHOST) //
// //
//////////////////////////////////////////////
#define ABDUCTOR_MAX_TEAMS 4
/datum/dynamic_ruleset/midround/from_ghosts/abductors
name = "Abductors"
antag_flag = "Abductor"
antag_flag_override = ROLE_ABDUCTOR
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 2
required_applicants = 2
weight = 4
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/datum/team/abductor_team/new_team
/datum/dynamic_ruleset/midround/from_ghosts/abductors/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/abductors/finish_setup(mob/new_character, index)
if (index == 1) // Our first guy is the scientist. We also initialize the team here as well since this should only happen once per pair of abductors.
new_team = new
if(new_team.team_number > ABDUCTOR_MAX_TEAMS)
return MAP_ERROR
var/datum/antagonist/abductor/scientist/new_role = new
new_character.mind.add_antag_datum(new_role, new_team)
else // Our second guy is the agent, team is already created, don't need to make another one.
var/datum/antagonist/abductor/agent/new_role = new
new_character.mind.add_antag_datum(new_role, new_team)
#undef ABDUCTOR_MAX_TEAMS
//////////////////////////////////////////////
// //
// SWARMERS (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/swarmers
name = "Swarmers"
antag_datum = /datum/antagonist/swarmer
antag_flag = "Swarmer"
antag_flag_override = ROLE_ALIEN
required_type = /mob/dead/observer
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 0
weight = 3
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/swarmers/execute()
var/list/spawn_locs = list()
for(var/x in GLOB.xeno_spawn)
var/turf/spawn_turf = x
var/light_amount = spawn_turf.get_lumcount()
if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD)
spawn_locs += spawn_turf
if(!spawn_locs.len)
message_admins("No valid spawn locations found in GLOB.xeno_spawn, aborting swarmer spawning...")
return MAP_ERROR
var/obj/structure/swarmer_beacon/new_beacon = new(pick(spawn_locs))
log_game("A Swarmer Beacon was spawned via Dynamic Mode.")
notify_ghosts("\A Swarmer Beacon has spawned!", source = new_beacon, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Swarmer Beacon Spawned")
return ..()
//////////////////////////////////////////////
// //
// SPACE NINJA (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja
name = "Space Ninja"
antag_datum = /datum/antagonist/ninja
antag_flag = "Space Ninja"
antag_flag_override = ROLE_NINJA
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja/execute()
for(var/obj/effect/landmark/carpspawn/carp_spawn in GLOB.landmarks_list)
if(!isturf(carp_spawn.loc))
stack_trace("Carp spawn found not on a turf: [carp_spawn.type] on [isnull(carp_spawn.loc) ? "null" : carp_spawn.loc.type]")
continue
spawn_locs += carp_spawn.loc
if(!spawn_locs.len)
message_admins("No valid spawn locations found, aborting...")
return MAP_ERROR
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja/generate_ruleset_body(mob/applicant)
var/mob/living/carbon/human/ninja = create_space_ninja(pick(spawn_locs))
ninja.key = applicant.key
ninja.mind.add_antag_datum(/datum/antagonist/ninja)
message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a Space Ninja by the midround ruleset.")
log_game("DYNAMIC: [key_name(ninja)] was spawned as a Space Ninja by the midround ruleset.")
return ninja
//////////////////////////////////////////////
// //
// SPIDERS (GHOST) //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/spiders
name = "Spiders"
antag_flag = "Spider"
antag_flag_override = ROLE_ALIEN
required_type = /mob/dead/observer
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 0
weight = 3
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
var/spawncount = 2
/datum/dynamic_ruleset/midround/spiders/execute()
create_midwife_eggs(spawncount)
return ..()
/// Revenant ruleset
/datum/dynamic_ruleset/midround/from_ghosts/revenant
name = "Revenant"
antag_datum = /datum/antagonist/revenant
antag_flag = "Revenant"
antag_flag_override = ROLE_REVENANT
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
requirements = list(101,101,101,70,50,40,20,15,10,10)
repeatable = TRUE
var/dead_mobs_required = 20
var/need_extra_spawns_value = 15
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population=0, threat=0)
if(GLOB.dead_mob_list.len < dead_mobs_required)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/execute()
for(var/mob/living/corpse in GLOB.dead_mob_list) //look for any dead bodies
var/turf/corpse_turf = get_turf(corpse)
if(corpse_turf && is_station_level(corpse_turf.z))
spawn_locs += corpse_turf
if(!spawn_locs.len || spawn_locs.len < need_extra_spawns_value) //look for any morgue trays, crematoriums, ect if there weren't alot of dead bodies on the station to pick from
for(var/obj/structure/bodycontainer/corpse_container in GLOB.bodycontainers)
var/turf/container_turf = get_turf(corpse_container)
if(container_turf && is_station_level(container_turf.z))
spawn_locs += container_turf
if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns
for(var/obj/effect/landmark/carpspawn/carp_spawnpoint in GLOB.landmarks_list)
if(isturf(carp_spawnpoint.loc))
spawn_locs += carp_spawnpoint.loc
if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
return FALSE
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/generate_ruleset_body(mob/applicant)
var/mob/living/simple_animal/revenant/revenant = new(pick(spawn_locs))
revenant.key = applicant.key
message_admins("[ADMIN_LOOKUPFLW(revenant)] has been made into a revenant by the midround ruleset.")
log_game("[key_name(revenant)] was spawned as a revenant by the midround ruleset.")
return revenant
/// Sentient Disease ruleset
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
name = "Sentient Disease"
antag_datum = /datum/antagonist/disease
antag_flag = "Sentient Disease"
antag_flag_override = ROLE_ALIEN
required_candidates = 1
weight = 4
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant)
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
virus.key = applicant.key
INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by the midround ruleset.")
log_game("[key_name(virus)] was spawned as a sentient disease by the midround ruleset.")
return virus
/// Space Pirates ruleset
/datum/dynamic_ruleset/midround/pirates
name = "Space Pirates"
antag_flag = "Space Pirates"
required_type = /mob/dead/observer
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 0
weight = 4
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/pirates/acceptable(population=0, threat=0)
if (!SSmapping.empty_space)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/pirates/execute()
send_pirate_threat()
return ..()
/// Obsessed ruleset
/datum/dynamic_ruleset/midround/obsessed
name = "Obsessed"
antag_datum = /datum/antagonist/obsessed
antag_flag = ROLE_OBSESSED
restricted_roles = list("Cyborg", "AI", "Positronic Brain")
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
weight = 4
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
/datum/dynamic_ruleset/midround/obsessed/trim_candidates()
..()
candidates = living_players
for(var/mob/living/carbon/human/candidate in candidates)
if( \
!candidate.getorgan(/obj/item/organ/brain) \
|| candidate.mind.has_antag_datum(/datum/antagonist/obsessed) \
|| candidate.stat == DEAD \
|| !(ROLE_OBSESSED in candidate.client?.prefs?.be_special) \
|| !candidate.mind.assigned_role \
)
candidates -= candidate
/datum/dynamic_ruleset/midround/obsessed/execute()
if(!candidates || !candidates.len)
return FALSE
var/mob/living/carbon/human/obsessed = pick_n_take(candidates)
obsessed.gain_trauma(/datum/brain_trauma/special/obsessed)
message_admins("[ADMIN_LOOKUPFLW(obsessed)] has been made Obsessed by the midround ruleset.")
log_game("[key_name(obsessed)] was made Obsessed by the midround ruleset.")
return ..()
/// Probability the AI going malf will be accompanied by an ion storm announcement and some ion laws.
#undef MALF_ION_PROB
/// The probability to replace an existing law with an ion law instead of adding a new ion law.
#undef REPLACE_LAW_WITH_ION_PROB