mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com> Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
1002 lines
37 KiB
Plaintext
1002 lines
37 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)
|
|
SEND_GLOBAL_SIGNAL(COMSIG_RULESET_BODY_GENERATED_FROM_GHOSTS, 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.can_roll_midround()))
|
|
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)
|
|
// AIs are technically considered "Ghost roles" as far as candidate selection are concerned
|
|
// So we need to allow it here. We filter of actual ghost role AIs (charlie) via trim_candidates ourselves
|
|
restrict_ghost_roles = FALSE
|
|
|
|
/datum/dynamic_ruleset/midround/malf/trim_candidates()
|
|
..()
|
|
candidates = list()
|
|
for(var/mob/living/silicon/ai/player in living_players)
|
|
if(!is_station_level(player.z))
|
|
continue
|
|
if(isnull(player.mind))
|
|
continue
|
|
if(player.mind.special_role || player.mind.can_roll_midround())
|
|
continue
|
|
candidates += player
|
|
|
|
/datum/dynamic_ruleset/midround/malf/ready(forced)
|
|
if(!check_candidates())
|
|
log_dynamic("FAIL: No valid AI found for the Malfunctioning AI ruleset.")
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/malf/execute()
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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)
|
|
signup_item_path = /obj/item/clothing/head/wizard
|
|
|
|
/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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /obj/machinery/nuclearbomb
|
|
|
|
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.get_job_type(/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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /obj/structure/blob/normal
|
|
|
|
/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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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 || player.mind.can_roll_midround()))
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /mob/living/basic/alien
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /obj/item/light_eater
|
|
|
|
/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.get_job_type(/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/effects/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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /mob/living/basic/space_dragon
|
|
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/effects/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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /obj/item/energy_katana
|
|
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /mob/living/basic/revenant
|
|
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 Space Pirates Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/pirates
|
|
name = "Space Pirates"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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
|
|
signup_item_path = /obj/effect/meteor/meaty/changeling
|
|
|
|
/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
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
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/items/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
|
|
|
|
/// Midround Voidwalker Ruleset (From Ghosts)
|
|
/datum/dynamic_ruleset/midround/from_ghosts/voidwalker
|
|
name = "Voidwalker"
|
|
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
|
|
antag_datum = /datum/antagonist/voidwalker
|
|
antag_flag = ROLE_VOIDWALKER
|
|
antag_flag_override = ROLE_VOIDWALKER
|
|
ruleset_category = parent_type::ruleset_category | RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS
|
|
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
|
|
required_candidates = 1
|
|
weight = 2
|
|
cost = 5
|
|
minimum_players = 40
|
|
repeatable = TRUE
|
|
signup_item_path = /obj/item/cosmic_skull
|
|
ruleset_lazy_templates = list(LAZY_TEMPLATE_KEY_VOIDWALKER_VOID)
|
|
/// The space turf we find in acceptable(), cached for ease
|
|
var/space_turf
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/voidwalker/acceptable(population = 0, threat_level = 0)
|
|
space_turf = find_space_spawn()
|
|
// Space only antag and will die on planetary gravity.
|
|
if(SSmapping.is_planetary() || !space_turf)
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/dynamic_ruleset/midround/from_ghosts/voidwalker/generate_ruleset_body(mob/applicant)
|
|
var/datum/mind/player_mind = new /datum/mind(applicant.key)
|
|
player_mind.active = TRUE
|
|
|
|
var/mob/living/carbon/human/voidwalker = new (space_turf)
|
|
player_mind.transfer_to(voidwalker)
|
|
player_mind.set_assigned_role(SSjob.get_job_type(/datum/job/voidwalker))
|
|
player_mind.special_role = antag_flag
|
|
player_mind.add_antag_datum(antag_datum)
|
|
|
|
playsound(voidwalker, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
|
|
message_admins("[ADMIN_LOOKUPFLW(voidwalker)] has been made into a Voidwalker by the midround ruleset.")
|
|
log_dynamic("[key_name(voidwalker)] was spawned as a Voidwalker by the midround ruleset.")
|
|
return voidwalker
|