mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-29 18:40:42 +00:00
## About The Pull Request Three changes to ghost orbit popups. Let's check them one-by-one. First off -- The obsession awakening and blob host midrounds now have a ghost popup when summoned via Dynamic. They already have them when summoned by a random event roll, so this is mostly just bringing the two different spawn methods in line with each other. None of the other midround dynamic picks (autotraitor, revs, etc) have had this added, because they're less focused on spectacle and require more mystery to function properly. Second -- The electrified railings on the tram no longer notify ghosts every time someone gets shocked. Electrocute_act is called on anything that passes by it, and the popup would be thrown regardless of whether or not they've "fallen in the path of an oncoming tram" or not. Making it check if the electrocute_act actually successfully stunned a player, and that they're about to be hit, would probably require an unwieldy rewrite to the behavior that I'd rather not resort to. I brought this helper call into this world, and now I'm taking it out. Lastly -- The supply pod random event can now be properly jumped to. Originally, it would track the landing indicator, which would delete almost immediately and leave an un-orbitable popup on your screen. Now you jump to the landing zone, regardless of when you click the toast popup. This is really long PR body for such tiny changes please don't think this is anything more than it actually is. ## Why It's Good For The Game Minor improvements to some ghost orbit popups, because Observers are players too! ## Changelog 🆑 qol: Dynamic midrounds "Obsession Awakening" and "Blob Host" now have ghost orbit popups, so you can see them happening in real time. qol: There is no longer a ghost orbit popup for players being shocked by an electrified tram rail. qol: The Stray Cargo orbit popup no longer has a half-second window to orbit before it becomes useless. /🆑
952 lines
34 KiB
Plaintext
952 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_RULESET
|
|
var/midround_ruleset_style
|
|
/// 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()
|
|
|
|
/// The minimum round time before this ruleset will show up
|
|
var/minimum_round_time = 0
|
|
/// Abstract root value
|
|
var/abstract_type = /datum/dynamic_ruleset/midround
|
|
|
|
/datum/dynamic_ruleset/midround/forget_startup()
|
|
living_players = list()
|
|
living_antags = list()
|
|
dead_players = list()
|
|
list_observers = list()
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts
|
|
weight = 0
|
|
required_type = /mob/dead/observer
|
|
abstract_type = /datum/dynamic_ruleset/midround/from_ghosts
|
|
/// 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
|
|
///Path of an item to show up in ghost polls for applicants to sign up.
|
|
var/signup_item_path = /obj/structure/sign/poster/contraband/syndicate_recruitment
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/check_candidates()
|
|
var/dead_count = dead_players.len + list_observers.len
|
|
if (required_candidates <= dead_count)
|
|
return TRUE
|
|
|
|
log_dynamic("FAIL: [src], a from_ghosts ruleset, did not have enough dead candidates: [required_candidates] needed, [dead_count] found")
|
|
|
|
return FALSE
|
|
|
|
/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/to_trim = list())
|
|
var/list/trimmed_list = to_trim.Copy()
|
|
for(var/mob/creature in trimmed_list)
|
|
if (!istype(creature, required_type))
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if (isnull(creature.client)) // Are they connected?
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if(creature.client.get_remaining_days(minimum_required_age) > 0)
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if (!((antag_preference || antag_flag) in creature.client.prefs.be_special))
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if (is_banned_from(creature.ckey, list(antag_flag_override || antag_flag, ROLE_SYNDICATE)))
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if (isnull(creature.mind))
|
|
continue
|
|
if (restrict_ghost_roles && !(creature.mind.assigned_role.job_flags & JOB_CREW_MEMBER)) // Are they not playing a station role?
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if (creature.mind.assigned_role.title in restricted_roles) // Does their job allow it?
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if (length(exclusive_roles) && !(creature.mind.assigned_role.title in exclusive_roles)) // Is the rule exclusive to their job?
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if(HAS_TRAIT(creature, TRAIT_MIND_TEMPORARILY_GONE)) // are they out of body?
|
|
trimmed_list.Remove(creature)
|
|
continue
|
|
if(HAS_TRAIT(creature, TRAIT_TEMPORARY_BODY)) // are they an avatar?
|
|
trimmed_list.Remove(creature)
|
|
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
|
|
// subtype your midround with /from_ghosts or /from_living to get candidate checking. Or check yourself by subtyping from neither
|
|
/datum/dynamic_ruleset/midround/ready(forced = FALSE)
|
|
if (forced)
|
|
return TRUE
|
|
|
|
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(SSdynamic.threat_level/10)
|
|
var/ruleset_forced = (GLOB.dynamic_forced_rulesets[type] || RULESET_NOT_FORCED) == RULESET_FORCE_ENABLED
|
|
if (!ruleset_forced && job_check < required_enemies[threat])
|
|
log_dynamic("FAIL: [src] is not ready, because there are not enough enemies: [required_enemies[threat]] needed, [job_check] found")
|
|
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
|
|
|
|
SSdynamic.log_dynamic_and_announce("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
|
|
candidates = SSpolling.poll_ghost_candidates(
|
|
question = "Looking for volunteers to become [span_notice(antag_flag)] for [span_danger(name)]",
|
|
check_jobban = antag_flag_override,
|
|
role = antag_flag || antag_flag_override,
|
|
poll_time = 30 SECONDS,
|
|
alert_pic = signup_item_path,
|
|
role_name_text = antag_flag,
|
|
)
|
|
|
|
if(!candidates || candidates.len <= 0)
|
|
SSdynamic.log_dynamic_and_announce("The ruleset [name] received no applications.")
|
|
SSdynamic.executed_rules -= src
|
|
attempt_replacement()
|
|
return
|
|
|
|
SSdynamic.log_dynamic_and_announce("[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)
|
|
SSdynamic.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
|
|
assigned += applicant
|
|
finish_applications()
|
|
|
|
/// Here the accepted applications get generated bodies and their setup is finished.
|
|
/// Called by review_applications()
|
|
/datum/dynamic_ruleset/midround/from_ghosts/proc/finish_applications()
|
|
var/i = 0
|
|
for(var/mob/applicant as anything in assigned)
|
|
i++
|
|
var/mob/new_character = applicant
|
|
if(makeBody)
|
|
new_character = generate_ruleset_body(applicant)
|
|
finish_setup(new_character, i)
|
|
notify_ghosts(
|
|
"[applicant.name] has been picked for the ruleset [name]!",
|
|
source = new_character,
|
|
)
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/proc/generate_ruleset_body(mob/applicant)
|
|
var/mob/living/carbon/human/new_character = make_body(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/from_living/autotraitor/sleeper_agent = new
|
|
|
|
SSdynamic.configure_ruleset(sleeper_agent)
|
|
|
|
if (!SSdynamic.picking_specific_rule(sleeper_agent))
|
|
return
|
|
|
|
SSdynamic.picking_specific_rule(/datum/dynamic_ruleset/latejoin/infiltrator)
|
|
|
|
///subtype to handle checking players
|
|
/datum/dynamic_ruleset/midround/from_living
|
|
weight = 0
|
|
abstract_type = /datum/dynamic_ruleset/midround/from_living
|
|
|
|
/datum/dynamic_ruleset/midround/from_living/ready(forced)
|
|
if(!check_candidates())
|
|
return FALSE
|
|
return ..()
|
|
|
|
|
|
/// Midround Traitor Ruleset (From Living)
|
|
/datum/dynamic_ruleset/midround/from_living/autotraitor
|
|
name = "Syndicate Sleeper Agent"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/traitor/infiltrator/sleeper_agent
|
|
antag_flag = ROLE_SLEEPER_AGENT
|
|
antag_flag_override = ROLE_TRAITOR
|
|
protected_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_PERSONNEL,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_PRISONER,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
ROLE_POSITRONIC_BRAIN,
|
|
)
|
|
required_candidates = 1
|
|
weight = 35
|
|
cost = 3
|
|
requirements = list(3,3,3,3,3,3,3,3,3,3)
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/from_living/autotraitor/trim_candidates()
|
|
..()
|
|
candidates = living_players
|
|
for(var/mob/living/player in candidates)
|
|
if(issilicon(player)) // Your assigned role doesn't change when you are turned into a silicon.
|
|
candidates -= player
|
|
else if(is_centcom_level(player.z))
|
|
candidates -= player // We don't autotator people in CentCom
|
|
else if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
|
|
candidates -= player // We don't autotator people with roles already
|
|
|
|
/datum/dynamic_ruleset/midround/from_living/autotraitor/execute()
|
|
var/mob/M = pick(candidates)
|
|
assigned += M
|
|
candidates -= M
|
|
var/datum/antagonist/traitor/infiltrator/sleeper_agent/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_dynamic("[key_name(M)] was selected by the [name] ruleset and has been made into a midround traitor.")
|
|
return TRUE
|
|
|
|
/// Midround Malf AI Ruleset (From Living)
|
|
/datum/dynamic_ruleset/midround/malf
|
|
name = "Malfunctioning AI"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/malf_ai
|
|
antag_flag = ROLE_MALF_MIDROUND
|
|
antag_flag_override = ROLE_MALF
|
|
enemy_roles = list(
|
|
JOB_CHEMIST,
|
|
JOB_CHIEF_ENGINEER,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_RESEARCH_DIRECTOR,
|
|
JOB_SCIENTIST,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
exclusive_roles = list(JOB_AI)
|
|
required_enemies = list(4,4,4,4,4,4,2,2,2,0)
|
|
required_candidates = 1
|
|
minimum_players = 25
|
|
weight = 2
|
|
cost = 10
|
|
required_type = /mob/living/silicon/ai
|
|
blocking_rules = list(/datum/dynamic_ruleset/roundstart/malf_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), LAW_ION)
|
|
else
|
|
new_malf_ai.add_ion_law(generate_ion_law())
|
|
return TRUE
|
|
|
|
/// Midround Wizard Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/wizard
|
|
name = "Wizard"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/wizard
|
|
antag_flag = ROLE_WIZARD_MIDROUND
|
|
antag_flag_override = ROLE_WIZARD
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 1
|
|
cost = 10
|
|
requirements = REQUIREMENTS_VERY_HIGH_THREAT_NEEDED
|
|
flags = HIGH_IMPACT_RULESET
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_WIZARDDEN)
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/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/midround/from_ghosts/wizard/finish_setup(mob/new_character, index)
|
|
..()
|
|
new_character.forceMove(pick(GLOB.wizardstart))
|
|
|
|
/// Midround Nuclear Operatives Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/nuclear
|
|
name = "Nuclear Assault"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_flag = ROLE_OPERATIVE_MIDROUND
|
|
antag_flag_override = ROLE_OPERATIVE
|
|
antag_datum = /datum/antagonist/nukeop
|
|
enemy_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
required_enemies = list(3,3,3,3,3,2,1,1,0,0)
|
|
required_candidates = 5
|
|
weight = 5
|
|
cost = 7
|
|
minimum_round_time = 70 MINUTES
|
|
requirements = REQUIREMENTS_VERY_HIGH_THREAT_NEEDED
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_NUKIEBASE)
|
|
flags = HIGH_IMPACT_RULESET
|
|
|
|
var/list/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat_level=0)
|
|
if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in SSdynamic.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 (!check_candidates())
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_applications()
|
|
var/mob/leader = get_most_experienced(assigned, ROLE_NUCLEAR_OPERATIVE)
|
|
if(leader)
|
|
assigned.Remove(leader)
|
|
assigned.Insert(1, leader)
|
|
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)
|
|
var/datum/antagonist/nukeop/leader/leader_antag_datum = new()
|
|
new_character.mind.add_antag_datum(leader_antag_datum)
|
|
return
|
|
return ..()
|
|
|
|
/// Midround Blob Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/blob
|
|
name = "Blob"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/blob
|
|
antag_flag = ROLE_BLOB
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
minimum_round_time = 35 MINUTES
|
|
weight = 3
|
|
cost = 8
|
|
minimum_players = 25
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/blob/generate_ruleset_body(mob/applicant)
|
|
var/body = applicant.become_overmind()
|
|
return body
|
|
|
|
/// Midround Blob Infection Ruleset (From Living)
|
|
/datum/dynamic_ruleset/midround/from_living/blob_infection
|
|
name = "Blob Infection"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/blob/infection
|
|
antag_flag = ROLE_BLOB_INFECTION
|
|
antag_flag_override = ROLE_BLOB
|
|
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,
|
|
ROLE_POSITRONIC_BRAIN,
|
|
)
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
minimum_round_time = 35 MINUTES
|
|
weight = 3
|
|
cost = 10
|
|
minimum_players = 25
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/from_living/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/from_living/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
|
|
notify_ghosts(
|
|
"[blob_antag] has become a blob host!",
|
|
source = blob_antag,
|
|
header = "So Bulbous...",
|
|
)
|
|
return ..()
|
|
|
|
/// Midround Xenomorph Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph
|
|
name = "Alien Infestation"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/xeno
|
|
antag_flag = ROLE_ALIEN
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
minimum_round_time = 40 MINUTES
|
|
weight = 5
|
|
cost = 10
|
|
minimum_players = 25
|
|
repeatable = TRUE
|
|
var/list/vents = list()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/forget_startup()
|
|
vents = list()
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
|
|
// 50% chance of being incremented by one
|
|
required_candidates += prob(50)
|
|
var/list/vent_pumps = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/atmospherics/components/unary/vent_pump)
|
|
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent as anything in vent_pumps)
|
|
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_atmos_machines.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_dynamic("[key_name(new_xeno)] was spawned as an alien by the midround ruleset.")
|
|
return new_xeno
|
|
|
|
/// Midround Nightmare Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/nightmare
|
|
name = "Nightmare"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/nightmare
|
|
antag_flag = ROLE_NIGHTMARE
|
|
antag_flag_override = ROLE_ALIEN
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 3
|
|
cost = 5
|
|
minimum_players = 15
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/acceptable(population = 0, threat_level = 0)
|
|
var/turf/spawn_loc = find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = TRUE) //Checks if there's a single safe, dark tile on station.
|
|
if(!spawn_loc)
|
|
return FALSE
|
|
return ..()
|
|
|
|
/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/new_nightmare = new (find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = TRUE))
|
|
player_mind.transfer_to(new_nightmare)
|
|
player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nightmare))
|
|
player_mind.special_role = ROLE_NIGHTMARE
|
|
player_mind.add_antag_datum(/datum/antagonist/nightmare)
|
|
new_nightmare.set_species(/datum/species/shadow/nightmare)
|
|
|
|
playsound(new_nightmare, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
|
|
message_admins("[ADMIN_LOOKUPFLW(new_nightmare)] has been made into a Nightmare by the midround ruleset.")
|
|
log_dynamic("[key_name(new_nightmare)] was spawned as a Nightmare by the midround ruleset.")
|
|
return new_nightmare
|
|
|
|
/// Midround Space Dragon Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon
|
|
name = "Space Dragon"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/space_dragon
|
|
antag_flag = ROLE_SPACE_DRAGON
|
|
antag_flag_override = ROLE_SPACE_DRAGON
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 4
|
|
cost = 7
|
|
minimum_players = 25
|
|
repeatable = TRUE
|
|
var/list/spawn_locs = list()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon/forget_startup()
|
|
spawn_locs = list()
|
|
return ..()
|
|
|
|
/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/basic/space_dragon/S = new (pick(spawn_locs))
|
|
player_mind.transfer_to(S)
|
|
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_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
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/abductors
|
|
name = "Abductors"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/abductor
|
|
antag_flag = ROLE_ABDUCTOR
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 2
|
|
required_applicants = 2
|
|
weight = 4
|
|
cost = 7
|
|
minimum_players = 25
|
|
repeatable = TRUE
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_ABDUCTOR_SHIPS)
|
|
|
|
var/datum/team/abductor_team/new_team
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/abductors/forget_startup()
|
|
new_team = null
|
|
return ..()
|
|
|
|
/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)
|
|
|
|
/// Midround Space Ninja Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja
|
|
name = "Space Ninja"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/ninja
|
|
antag_flag = ROLE_NINJA
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 4
|
|
cost = 8
|
|
minimum_players = 30
|
|
repeatable = TRUE
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_NINJA_HOLDING_FACILITY) // I mean, no one uses the nets anymore but whateva
|
|
|
|
var/list/spawn_locs = list()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja/forget_startup()
|
|
spawn_locs = list()
|
|
return ..()
|
|
|
|
/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_dynamic("[key_name(ninja)] was spawned as a Space Ninja by the midround ruleset.")
|
|
return ninja
|
|
|
|
/// Midround Spiders Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/spiders
|
|
name = "Spiders"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_flag = ROLE_SPIDER
|
|
required_type = /mob/dead/observer
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 0
|
|
weight = 3
|
|
cost = 8
|
|
minimum_players = 27
|
|
repeatable = TRUE
|
|
var/spawncount = 2
|
|
|
|
/datum/dynamic_ruleset/midround/spiders/execute()
|
|
create_midwife_eggs(spawncount)
|
|
return ..()
|
|
|
|
/// Midround Revenant Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/revenant
|
|
name = "Revenant"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/revenant
|
|
antag_flag = ROLE_REVENANT
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 4
|
|
cost = 5
|
|
minimum_players = 15
|
|
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/forget_startup()
|
|
spawn_locs = list()
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population=0, threat_level=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/basic/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
|
|
|
|
/// Midround Sentient Disease Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
|
|
name = "Sentient Disease"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_datum = /datum/antagonist/disease
|
|
antag_flag = ROLE_SENTIENT_DISEASE
|
|
required_candidates = 1
|
|
minimum_players = 25
|
|
weight = 4
|
|
cost = 8
|
|
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, TYPE_PROC_REF(/mob/camera/disease, 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
|
|
|
|
/// Midround Space Pirates Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/pirates
|
|
name = "Space Pirates"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_flag = "Space Pirates"
|
|
required_type = /mob/dead/observer
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 0
|
|
weight = 3
|
|
cost = 8
|
|
minimum_players = 20
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/pirates/acceptable(population=0, threat_level=0)
|
|
if (SSmapping.is_planetary() || GLOB.light_pirate_gangs.len == 0)
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/pirates/execute()
|
|
send_pirate_threat(GLOB.light_pirate_gangs)
|
|
return ..()
|
|
|
|
/// Dangerous Space Pirates ruleset
|
|
/datum/dynamic_ruleset/midround/dangerous_pirates
|
|
name = "Dangerous Space Pirates"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
|
|
antag_flag = "Space Pirates"
|
|
required_type = /mob/dead/observer
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 0
|
|
weight = 3
|
|
cost = 8
|
|
minimum_players = 25
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/dangerous_pirates/acceptable(population=0, threat_level=0)
|
|
if (SSmapping.is_planetary() || GLOB.heavy_pirate_gangs.len == 0)
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/dangerous_pirates/execute()
|
|
send_pirate_threat(GLOB.heavy_pirate_gangs)
|
|
return ..()
|
|
|
|
/// Midround Obsessed Ruleset (From Living)
|
|
/datum/dynamic_ruleset/midround/from_living/obsessed
|
|
name = "Obsessed"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/obsessed
|
|
antag_flag = ROLE_OBSESSED
|
|
restricted_roles = list(
|
|
JOB_AI,
|
|
JOB_CYBORG,
|
|
ROLE_POSITRONIC_BRAIN,
|
|
)
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 4
|
|
cost = 3 // Doesn't have the same impact on rounds as revenants, dragons, sentient disease (10) or syndicate infiltrators (5).
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/from_living/obsessed/trim_candidates()
|
|
..()
|
|
candidates = living_players
|
|
for(var/mob/living/carbon/human/candidate in candidates)
|
|
if( \
|
|
!candidate.get_organ_by_type(/obj/item/organ/internal/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/from_living/obsessed/execute()
|
|
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.")
|
|
notify_ghosts(
|
|
"[obsessed] has developed an obsession with someone!",
|
|
source = obsessed,
|
|
header = "Love Can Bloom",
|
|
)
|
|
return TRUE
|
|
|
|
/// Midround Space Changeling Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/changeling_midround
|
|
name = "Space Changeling"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/changeling/space
|
|
antag_flag = ROLE_CHANGELING_MIDROUND
|
|
antag_flag_override = ROLE_CHANGELING
|
|
required_type = /mob/dead/observer
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 3
|
|
cost = 7
|
|
minimum_players = 15
|
|
repeatable = TRUE
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/changeling_midround/generate_ruleset_body(mob/applicant)
|
|
var/body = generate_changeling_meteor(applicant)
|
|
message_admins("[ADMIN_LOOKUPFLW(body)] has been made into a space changeling by the midround ruleset.")
|
|
log_dynamic("[key_name(body)] was spawned as a space changeling by the midround ruleset.")
|
|
return body
|
|
|
|
/// Midround Paradox Clone Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/paradox_clone
|
|
name = "Paradox Clone"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/paradox_clone
|
|
antag_flag = ROLE_PARADOX_CLONE
|
|
enemy_roles = list(
|
|
JOB_CAPTAIN,
|
|
JOB_DETECTIVE,
|
|
JOB_HEAD_OF_SECURITY,
|
|
JOB_SECURITY_OFFICER,
|
|
JOB_WARDEN,
|
|
)
|
|
required_enemies = list(2, 2, 1, 1, 1, 1, 1, 0, 0, 0)
|
|
required_candidates = 1
|
|
weight = 4
|
|
cost = 3
|
|
repeatable = TRUE
|
|
signup_item_path = /obj/effect/bluespace_stream
|
|
var/list/possible_spawns = list() ///places the antag can spawn
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/paradox_clone/forget_startup()
|
|
possible_spawns = list()
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/paradox_clone/execute()
|
|
possible_spawns += find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = FALSE)
|
|
if(!possible_spawns.len)
|
|
return MAP_ERROR
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/paradox_clone/generate_ruleset_body(mob/applicant)
|
|
var/datum/mind/player_mind = new /datum/mind(applicant.key)
|
|
player_mind.active = TRUE
|
|
|
|
var/mob/living/carbon/human/clone_victim = find_original()
|
|
var/mob/living/carbon/human/clone = clone_victim.make_full_human_copy(pick(possible_spawns))
|
|
player_mind.transfer_to(clone)
|
|
|
|
var/datum/antagonist/paradox_clone/new_datum = player_mind.add_antag_datum(/datum/antagonist/paradox_clone)
|
|
new_datum.original_ref = WEAKREF(clone_victim.mind)
|
|
new_datum.setup_clone()
|
|
|
|
playsound(clone, 'sound/weapons/zapbang.ogg', 30, TRUE)
|
|
new /obj/item/storage/toolbox/mechanical(clone.loc) //so they dont get stuck in maints
|
|
|
|
message_admins("[ADMIN_LOOKUPFLW(clone)] has been made into a Paradox Clone by the midround ruleset.")
|
|
clone.log_message("was spawned as a Paradox Clone of [key_name(clone)] by the midround ruleset.", LOG_GAME)
|
|
|
|
return clone
|
|
|
|
/**
|
|
* Trims through GLOB.player_list and finds a target
|
|
* Returns a single human victim, if none is possible then returns null.
|
|
*/
|
|
/datum/dynamic_ruleset/midround/from_ghosts/paradox_clone/proc/find_original()
|
|
var/list/possible_targets = list()
|
|
|
|
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
|
if(!player.client || !player.mind || player.stat)
|
|
continue
|
|
if(!(player.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
|
|
continue
|
|
possible_targets += player
|
|
|
|
if(possible_targets.len)
|
|
return pick(possible_targets)
|
|
return FALSE
|
|
|
|
#undef MALF_ION_PROB
|
|
#undef REPLACE_LAW_WITH_ION_PROB
|