mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-19 02:54:41 +01:00
Exfiltration - Early Antag Escape (#29554)
* Ghost bar antag addition * Adds extraction flares and extraction procs * Ghostbarification, antag-specific extractions * Objectives reroll if target extracts * Comment format * Adds blackboxing. Prevents extraction if you're an objective target. * Fixed objective freezing, removes mindslaves on exfiltration * Vampire Portal Sprites * Better sprite for portal * Flayer telepad, assorted visual adjustments * Makes flayer and vampire portals truly one time use. Flayer ability icon * Fixed indents, vampire ability icon * Changeling flare * Removed some debug adjustments * Map fixes * Makes the exfil restriction a var on the datum * More map fixes * Centcomm my beloathed * More map fixes * Checks delay before objectives * Objective freezing list fix * Review fixes * Moves a lot of exfil item granting logic to antag datum. * Changed objective exfil block from var to proc * Centcommmm * Adds true return. Thanks Contra. Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> * Efficiency Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> * Moved jaunter subtypes * Forgot to move the admin one * Capitalization Co-authored-by: AmityBlamity <142629851+AmityBlamity@users.noreply.github.com> Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> * Reworked area selection * Black box, admin log, proper clearing of martial arts and bio chips * Exfiltration now properly checks completion of alive and free escape objective * Adds announcement to security comms when exfil item is used * Linters * Map adjustments * Fixes syndiebar library computer access * Merge conflict resolution * Updates action buttons * New Linters * Update centcomm.dmm --------- Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Co-authored-by: AmityBlamity <142629851+AmityBlamity@users.noreply.github.com>
This commit is contained in:
+3400
-2295
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ GLOBAL_LIST_EMPTY(newplayer_start)
|
||||
GLOBAL_LIST_EMPTY(latejoin)
|
||||
GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
|
||||
GLOBAL_LIST_EMPTY(syndieprisonwarp) //contractor targets go to these
|
||||
GLOBAL_LIST_EMPTY(antagextractwarp) // exfiltrating antags go here
|
||||
GLOBAL_LIST_EMPTY(backroomswarp) //backrooms hallucinators go to these
|
||||
GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these.
|
||||
GLOBAL_LIST_EMPTY(ertdirector)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/datum/outfit/admin/ghostbar_antag
|
||||
name = "Ghostly Antagonist"
|
||||
|
||||
l_ear = /obj/item/radio/headset/deadsay
|
||||
l_pocket = /obj/item/stack/spacecash/c1000
|
||||
id = /obj/item/card/id/syndicate
|
||||
backpack_contents = list(
|
||||
/obj/item/storage/box/engineer = 1,
|
||||
/obj/item/flashlight = 1
|
||||
)
|
||||
|
||||
/datum/outfit/admin/ghostbar_antag/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
|
||||
. = ..()
|
||||
if(visualsOnly)
|
||||
return
|
||||
|
||||
var/obj/item/card/id/our_id = H.wear_id
|
||||
if(!istype(our_id))
|
||||
return
|
||||
apply_to_card(our_id, H, list(ACCESS_MAINT_TUNNELS), name)
|
||||
our_id.assignment = src.name
|
||||
our_id.registered_name = H.real_name
|
||||
our_id.sex = capitalize(H.gender)
|
||||
our_id.age = H.age
|
||||
our_id.name = "[our_id.registered_name]'s ID Card ([our_id.assignment])"
|
||||
our_id.photo = get_id_photo(H)
|
||||
our_id.owner_uid = H.UID()
|
||||
our_id.owner_ckey = H.ckey
|
||||
our_id.icon_state = "syndie"
|
||||
H.job = src.name
|
||||
|
||||
H.mind.assigned_role = src.name
|
||||
H.mind.special_role = src.name
|
||||
H.mind.offstation_role = TRUE
|
||||
|
||||
H.sec_hud_set_ID()
|
||||
|
||||
/datum/outfit/admin/ghostbar_antag/syndicate
|
||||
name = "Syndicate Agent"
|
||||
|
||||
uniform = /obj/item/clothing/under/syndicate
|
||||
suit = /obj/item/clothing/suit/jacket/bomber/syndicate
|
||||
back = /obj/item/storage/backpack
|
||||
gloves = /obj/item/clothing/gloves/combat
|
||||
shoes = /obj/item/clothing/shoes/combat
|
||||
backpack_contents = list(
|
||||
/obj/item/storage/box/engineer = 1,
|
||||
/obj/item/flashlight = 1,
|
||||
/obj/item/food/syndidonkpocket = 1,
|
||||
/obj/item/gun/projectile/automatic/toy/pistol = 1,
|
||||
/obj/item/ammo_box/magazine/toy/pistol = 2
|
||||
)
|
||||
|
||||
/datum/outfit/admin/ghostbar_antag/vampire
|
||||
name = "Vampire"
|
||||
|
||||
uniform = /obj/item/clothing/under/suit/black
|
||||
suit = /obj/item/clothing/suit/draculacoat
|
||||
neck = /obj/item/clothing/neck/cloak
|
||||
back = /obj/item/storage/backpack
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
shoes = /obj/item/clothing/shoes/black
|
||||
backpack_contents = list(
|
||||
/obj/item/storage/box/engineer = 1,
|
||||
/obj/item/reagent_containers/iv_bag/blood/o_plus = 3
|
||||
)
|
||||
|
||||
/datum/outfit/admin/ghostbar_antag/changeling
|
||||
name = "Changeling"
|
||||
|
||||
uniform = /obj/item/clothing/under/chameleon
|
||||
suit = /obj/item/clothing/suit/chameleon
|
||||
neck = /obj/item/clothing/neck/chameleon
|
||||
back = /obj/item/storage/backpack/chameleon
|
||||
gloves = /obj/item/clothing/gloves/chameleon
|
||||
shoes = /obj/item/clothing/shoes/chameleon
|
||||
backpack_contents = list(
|
||||
/obj/item/storage/box/engineer = 1,
|
||||
/obj/item/toy/foamblade = 1
|
||||
)
|
||||
|
||||
/datum/outfit/admin/ghostbar_antag/mindflayer
|
||||
name = "Mindflayer"
|
||||
|
||||
uniform = /obj/item/clothing/under/psysuit
|
||||
suit = /obj/item/clothing/suit/blacktrenchcoat
|
||||
neck = /obj/item/clothing/neck/cloak
|
||||
back = /obj/item/storage/backpack
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
shoes = /obj/item/clothing/shoes/black
|
||||
backpack_contents = list(
|
||||
/obj/item/storage/box/engineer = 1,
|
||||
/obj/item/toy/plushie/ipcplushie = 1
|
||||
)
|
||||
@@ -581,6 +581,14 @@
|
||||
cost = 50
|
||||
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
|
||||
|
||||
/datum/uplink_item/device_tools/extraction_beacon
|
||||
name = "Extraction Flare"
|
||||
desc = "A special flare used to call in an extraction portal. The portal takes time to generate, and will only work in certain rooms that it is pre-calibrated for. The Syndicate withholds the right to deny a portal to agents with certain objectives."
|
||||
reference = "EXTF"
|
||||
item = /obj/item/wormhole_jaunter/extraction
|
||||
limited_stock = 1
|
||||
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
|
||||
|
||||
/datum/uplink_item/device_tools/hyper_medipen
|
||||
name = "Hyper-regenerative Medipen"
|
||||
desc = "An autoinjector filled with a variety of medical chemicals. It rapidly heals conventional injuries and genetic damage, but loses potency just as quickly. May have side effects if multiple are used in quick succession."
|
||||
|
||||
@@ -72,6 +72,9 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
/datum/objective/proc/found_target()
|
||||
return target
|
||||
|
||||
/datum/objective/proc/is_valid_exfiltration()
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* This is for objectives that need to register signals, so place them in here.
|
||||
*/
|
||||
@@ -215,6 +218,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
explanation_text = "Free Objective"
|
||||
|
||||
/datum/objective/assassinate/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
if(target?.current)
|
||||
if(target.current.stat == DEAD)
|
||||
return TRUE
|
||||
@@ -271,6 +276,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
return TARGET_INVALID_NOTHEAD
|
||||
|
||||
/datum/objective/mutiny/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
if(target?.current)
|
||||
if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client)
|
||||
return TRUE
|
||||
@@ -334,6 +341,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
/datum/objective/debrain/check_completion()
|
||||
if(!target) // If it's a free objective.
|
||||
return TRUE
|
||||
if(..())
|
||||
return TRUE
|
||||
if(!target.current || !isbrain(target.current))
|
||||
return FALSE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
@@ -358,6 +367,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
/datum/objective/protect/check_completion()
|
||||
if(!target) //If it's a free objective.
|
||||
return TRUE
|
||||
if(..())
|
||||
return TRUE
|
||||
if(target.current)
|
||||
if(target.current.stat == DEAD)
|
||||
return FALSE
|
||||
@@ -395,6 +406,9 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
return FALSE
|
||||
return SSshuttle.emergency.is_hijacked()
|
||||
|
||||
/datum/objective/hijack/is_valid_exfiltration()
|
||||
return FALSE
|
||||
|
||||
/datum/objective/hijackclone
|
||||
name = "Hijack (with clones)"
|
||||
explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape."
|
||||
@@ -453,7 +467,9 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
explanation_text = "Escape on the shuttle or an escape pod alive and free."
|
||||
needs_target = FALSE
|
||||
|
||||
/datum/objective/escape/check_completion()
|
||||
/datum/objective/escape/check_completion(exfilling = FALSE)
|
||||
if(..())
|
||||
return TRUE
|
||||
var/list/owners = get_owners()
|
||||
for(var/datum/mind/M in owners)
|
||||
// These are mandatory conditions, they should come before the freebie conditions below.
|
||||
@@ -464,6 +480,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
return TRUE
|
||||
if(SSticker.mode.station_was_nuked) // If they escaped the blast somehow, let them win.
|
||||
return TRUE
|
||||
if(exfilling)
|
||||
return TRUE
|
||||
if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME)
|
||||
return FALSE
|
||||
|
||||
@@ -536,7 +554,9 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
|
||||
// This objective should only be given to a single owner since only 1 person can have the ID card of the target.
|
||||
// We're fine to use `owner` instead of `get_owners()`.
|
||||
/datum/objective/escape/escape_with_identity/check_completion()
|
||||
/datum/objective/escape/escape_with_identity/check_completion(exfilling = FALSE)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(!target_real_name)
|
||||
return TRUE
|
||||
if(!ishuman(owner.current))
|
||||
@@ -554,6 +574,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
needs_target = FALSE
|
||||
|
||||
/datum/objective/survive/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
if(QDELETED(M.current) || M.current.stat == DEAD || is_special_dead(M.current, check_silicon = FALSE))
|
||||
return FALSE
|
||||
@@ -576,6 +598,11 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
/datum/objective/steal/found_target()
|
||||
return steal_target
|
||||
|
||||
/datum/objective/steal/is_valid_exfiltration()
|
||||
if(istype(steal_target, /datum/theft_objective/nukedisc) || istype(steal_target, /datum/theft_objective/plutonium_core))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/steal/proc/get_location()
|
||||
return steal_target.location_override || "an unknown area"
|
||||
|
||||
@@ -651,6 +678,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
/datum/objective/steal/check_completion()
|
||||
if(!steal_target)
|
||||
return TRUE // Free Objective
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
for(var/datum/mind/M in get_owners())
|
||||
if(!M.current)
|
||||
@@ -733,6 +762,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
explanation_text = "Acquire [target_amount] compatible genomes. The 'Extract DNA Sting' can be used to stealthily get genomes without killing somebody."
|
||||
|
||||
/datum/objective/absorb/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
var/datum/antagonist/changeling/cling = M?.has_antag_datum(/datum/antagonist/changeling)
|
||||
if(cling?.absorbed_dna && (cling.absorbed_count >= target_amount))
|
||||
@@ -758,6 +789,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
explanation_text = "Free Objective"
|
||||
|
||||
/datum/objective/destroy/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
if(target?.current)
|
||||
if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey)
|
||||
return TRUE
|
||||
@@ -778,6 +811,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
wanted_items = typecacheof(wanted_items)
|
||||
|
||||
/datum/objective/steal_five_of_type/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
var/stolen_count = 0
|
||||
var/list/owners = get_owners()
|
||||
var/list/all_items = list()
|
||||
@@ -805,6 +840,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
..()
|
||||
|
||||
/datum/objective/steal_five_of_type/summon_magic/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
var/stolen_count = 0
|
||||
var/list/owners = get_owners()
|
||||
var/list/all_items = list()
|
||||
@@ -843,6 +880,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
explanation_text = "Accumulate at least [target_amount] total units of blood."
|
||||
|
||||
/datum/objective/blood/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
var/datum/antagonist/vampire/V = M.has_antag_datum(/datum/antagonist/vampire)
|
||||
if(V.bloodtotal >= target_amount)
|
||||
@@ -870,6 +909,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
explanation_text = "Accumulate at least [target_amount] worth of swarms."
|
||||
|
||||
/datum/objective/swarms/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
var/datum/antagonist/mindflayer/flayer = M.has_antag_datum(/datum/antagonist/mindflayer)
|
||||
return flayer?.total_swarms_gathered >= target_amount
|
||||
@@ -930,6 +971,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
|
||||
needs_target = FALSE
|
||||
|
||||
/datum/objective/potentially_backstabbed/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
var/datum/antagonist/traitor/T = M.has_antag_datum(/datum/antagonist/traitor)
|
||||
for(var/datum/objective/our_objective in T.get_antag_objectives(FALSE))
|
||||
|
||||
@@ -198,6 +198,13 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
|
||||
spawner_list = GLOB.syndieprisonwarp
|
||||
return ..()
|
||||
|
||||
/obj/effect/landmark/spawner/antag_extract_warp
|
||||
name = "antagextractwarp"
|
||||
|
||||
/obj/effect/landmark/spawner/antag_extract_warp/Initialize(mapload)
|
||||
spawner_list = GLOB.antagextractwarp
|
||||
return ..()
|
||||
|
||||
/obj/effect/landmark/spawner/prisonwarp
|
||||
name = "prisonwarp"
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
|
||||
/// Do we have delayed objective giving?
|
||||
var/delayed_objectives = FALSE
|
||||
/// The title of the players "boss", used for exfil strings
|
||||
var/boss_title = "Operations"
|
||||
|
||||
/datum/antagonist/New()
|
||||
GLOB.antagonists += src
|
||||
@@ -498,4 +500,24 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
/datum/antagonist/proc/custom_blurb()
|
||||
return FALSE
|
||||
|
||||
/datum/antagonist/proc/exfiltrate(mob/living/carbon/human/extractor, obj/item/radio/radio)
|
||||
return
|
||||
|
||||
/datum/antagonist/proc/prepare_exfiltration(mob/user, obj/item/wormhole_jaunter/extraction/extraction_type = null)
|
||||
// No extraction for certian steals/hijack
|
||||
var/objectives = user.mind.get_all_objectives()
|
||||
for(var/datum/objective/goal in objectives)
|
||||
if(!goal.is_valid_exfiltration())
|
||||
to_chat(user, "<span class='warning'>The [boss_title] has deemed your objectives too delicate for an early extraction.</span>")
|
||||
return
|
||||
|
||||
if(world.time < 60 MINUTES) // 60 minutes of no exfil
|
||||
to_chat(user, "<span class='warning'>The [boss_title] is still preparing an exfiltration portal. Please wait another [round((36000 - world.time) / 600)] minutes before trying again.</span>")
|
||||
return
|
||||
var/mob/living/L = user
|
||||
if(!istype(L))
|
||||
return
|
||||
var/obj/item/wormhole_jaunter/extraction/extractor = new extraction_type()
|
||||
L.put_in_active_hand(extractor)
|
||||
|
||||
#undef SUCCESSFUL_DETACH
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
// Traitor flare
|
||||
/obj/item/wormhole_jaunter/extraction
|
||||
name = "extraction flare"
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
desc = "A single-use extraction flare that will let you escape the station. One way trip."
|
||||
icon_state = "flare-contractor"
|
||||
item_state = "flare"
|
||||
/// What areas can be extracted from?
|
||||
var/list/extractable_areas = list()
|
||||
/// Is there a roundstart delay?
|
||||
var/delayed_extraction = TRUE
|
||||
/// How long does the extraction take?
|
||||
var/extraction_time = 90 SECONDS
|
||||
/// Type of setup once a flare is lit
|
||||
var/setup_type = /obj/effect/temp_visual/exfiltration
|
||||
/// Type of portal spawned
|
||||
var/portal_type = /obj/effect/portal/advanced/exfiltration
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/Initialize(mapload)
|
||||
. = ..()
|
||||
var/list/possible_areas = list(
|
||||
// Rooms
|
||||
/area/station/hallway/primary/aft,
|
||||
/area/station/engineering/atmos,
|
||||
/area/station/public/arcade,
|
||||
/area/station/maintenance/assembly_line,
|
||||
/area/station/public/storage/tools/auxiliary,
|
||||
/area/station/command/office/blueshield,
|
||||
/area/station/supply/storage,
|
||||
/area/station/service/chapel,
|
||||
/area/station/service/chapel/office,
|
||||
/area/station/service/clown,
|
||||
/area/station/legal/courtroom,
|
||||
/area/station/public/toilet,
|
||||
/area/station/engineering/control,
|
||||
/area/station/engineering/controlroom,
|
||||
/area/station/hallway/secondary/exit,
|
||||
/area/holodeck/alphadeck,
|
||||
/area/station/service/hydroponics,
|
||||
/area/station/service/library,
|
||||
/area/station/service/mime,
|
||||
/area/station/supply/miningdock,
|
||||
/area/station/medical/morgue,
|
||||
/area/station/public/storage/office,
|
||||
/area/station/public/pet_store,
|
||||
/area/station/public/storage/tools,
|
||||
/area/station/public/mrchangs,
|
||||
/area/station/science/research,
|
||||
/area/station/security/checkpoint,
|
||||
/area/station/engineering/tech_storage,
|
||||
/area/station/command/teleporter,
|
||||
/area/station/science/storage,
|
||||
/area/station/science/misc_lab,
|
||||
/area/station/science/xenobiology,
|
||||
/area/station/turret_protected/aisat/interior,
|
||||
/area/station/aisat/atmos,
|
||||
/area/station/aisat/hall,
|
||||
/area/station/aisat/service,
|
||||
/area/station/service/bar,
|
||||
/area/station/supply/office,
|
||||
/area/station/medical/chemistry,
|
||||
/area/station/command/office/ce,
|
||||
/area/station/command/office/cmo,
|
||||
/area/station/medical/cloning,
|
||||
/area/station/medical/cryo,
|
||||
/area/station/public/dorms,
|
||||
/area/station/engineering/equipmentstorage,
|
||||
/area/station/engineering/break_room,
|
||||
/area/station/ai_monitored/storage/eva,
|
||||
/area/station/supply/expedition,
|
||||
/area/station/science/genetics,
|
||||
/area/station/engineering/gravitygenerator,
|
||||
/area/station/command/office/hop,
|
||||
/area/station/command/meeting_room,
|
||||
/area/station/service/kitchen,
|
||||
/area/station/science/robotics/chargebay,
|
||||
/area/station/medical/medbay,
|
||||
/area/station/medical/medbay2,
|
||||
/area/station/medical/medbay3,
|
||||
/area/station/medical/reception,
|
||||
/area/station/medical/storage,
|
||||
/area/station/medical/sleeper,
|
||||
/area/station/command/server,
|
||||
/area/station/command/office/ntrep,
|
||||
/area/station/medical/paramedic,
|
||||
/area/station/hallway/primary/port,
|
||||
/area/station/supply/qm,
|
||||
/area/station/command/office/rd,
|
||||
/area/station/science/rnd,
|
||||
/area/station/science/robotics,
|
||||
/area/station/medical/surgery/primary,
|
||||
/area/station/medical/surgery/secondary,
|
||||
/area/station/telecomms/chamber,
|
||||
/area/station/engineering/secure_storage
|
||||
)
|
||||
while(length(extractable_areas) < 3)
|
||||
var/area/selected_area = pick_n_take(possible_areas)
|
||||
for(var/area/potential in SSmapping.existing_station_areas)
|
||||
if(potential.type != selected_area)
|
||||
continue
|
||||
extractable_areas += potential
|
||||
break
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/examine(mob/user)
|
||||
. = ..()
|
||||
if(isAntag(user))
|
||||
. += "<span class='warning'>The target extraction locations are:</span>"
|
||||
for(var/area/A in extractable_areas)
|
||||
. += "<span class='warning'> - [A.name]</span>"
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/activate(mob/user)
|
||||
if(!turf_check(user))
|
||||
return
|
||||
|
||||
// Delay extractions
|
||||
if(world.time < 60 MINUTES && delayed_extraction) // 60 minutes of no exfil
|
||||
to_chat(user, "<span class='warning'>The exfiltration teleporter is calibrating. Please wait another [round((36000 - world.time) / 600)] minutes before trying again.</span>")
|
||||
return
|
||||
|
||||
// Objective checks
|
||||
var/denied = FALSE
|
||||
var/is_target = FALSE
|
||||
var/has_target_objective = FALSE
|
||||
var/objectives = user.mind.get_all_objectives()
|
||||
|
||||
// Check if you're the target of any other antags. If you are, using the beacon will find it jammed and turned to ash.
|
||||
for(var/datum/objective/O in GLOB.all_objectives)
|
||||
if(O.target != user.mind || istype(O, /datum/objective/protect))
|
||||
continue
|
||||
denied = TRUE
|
||||
is_target = TRUE
|
||||
break
|
||||
|
||||
// No extraction for NAD, Plutonium core theft, or Hijack
|
||||
for(var/datum/objective/goal in objectives)
|
||||
if(!goal.is_valid_exfiltration())
|
||||
denied = TRUE
|
||||
|
||||
if(istype(goal, /datum/objective/potentially_backstabbed))
|
||||
has_target_objective = TRUE
|
||||
|
||||
if(denied && is_target)
|
||||
to_chat(user, "<span class='warning'>Someone or something has jammed your extraction beacon, forcing it to disintegrate early!</span>")
|
||||
if(!has_target_objective)
|
||||
user.mind.add_mind_objective(/datum/objective/potentially_backstabbed, "Someone or something has jammed your extraction! Survive!")
|
||||
var/list/messages = user.mind.prepare_announce_objectives(FALSE)
|
||||
to_chat(user, chat_box_red(messages.Join("<br>")))
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(src))
|
||||
qdel(src)
|
||||
return
|
||||
else if(denied)
|
||||
to_chat(user, "<span class='warning'>Your objectives are too delicate for an early extraction.</span>")
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(src))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
var/obj/effect/temp_visual/exfiltration/F = new setup_type(get_turf(src))
|
||||
show_activation_message(user)
|
||||
user.drop_item()
|
||||
forceMove(F)
|
||||
log_and_message_admins("[user] activated an exfiltration flare.")
|
||||
var/obj/item/radio/radio = new
|
||||
radio.listening = FALSE
|
||||
radio.follow_target = src
|
||||
radio.config(list("Security" = 0))
|
||||
radio.autosay("<b>Unknown portal beacon detected at [get_area(src.loc)].</b>", "Automated Announcement", "Security")
|
||||
qdel(radio)
|
||||
addtimer(CALLBACK(src, PROC_REF(create_portal), user), extraction_time)
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/turf_check(mob/user)
|
||||
var/turf/device_turf = get_turf(user)
|
||||
var/area/our_area = get_area(device_turf)
|
||||
var/invalid_area = TRUE
|
||||
for(var/area/A in extractable_areas)
|
||||
if(istype(our_area, A))
|
||||
invalid_area = FALSE
|
||||
break
|
||||
|
||||
if(!device_turf || invalid_area)
|
||||
to_chat(user, "<span class='notice'>You're having difficulties getting the [name] to work.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/proc/create_portal(mob/user)
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(src))
|
||||
var/obj/effect/portal/advanced/exfiltration/P = new portal_type(get_turf(src), pick(GLOB.antagextractwarp), src, 30 SECONDS, user)
|
||||
P.antag_mind = user.mind
|
||||
qdel(src)
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/emag_act(mob/user)
|
||||
to_chat(user, "<span class='warning'>Emagging [src] has no effect.</span>")
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/chasm_react(mob/user)
|
||||
return // This is not an instant getaway portal like the jaunter
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/proc/show_activation_message(mob/user)
|
||||
user.visible_message("<span class='notice'>[user] pulls out a black and gold flare and lights it.</span>",
|
||||
"<span class='notice'>You light an extraction flare, initiating the extraction process.</span>")
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/vampire
|
||||
name = "blood chalice"
|
||||
icon = 'icons/obj/items.dmi'
|
||||
desc = "An unholy construct that will create a single-use portal that will let you escape the station. One way trip."
|
||||
icon_state = "blood-chalice"
|
||||
setup_type = /obj/effect/temp_visual/exfiltration/vampire
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/vampire/show_activation_message(mob/user)
|
||||
user.visible_message("<span class='notice'>[user] sets a blood-filled chalice on the ground. It begins to bubble ominously...</span>",
|
||||
"<span class='notice'>You set a blood-filled chalice on the ground. It begins to bubble ominously...</span>")
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/changeling
|
||||
name = "writhing mass"
|
||||
icon = 'icons/obj/items.dmi'
|
||||
desc = "A mass of writhing flesh that will create a single-use portal that will let you escape the station. One way trip."
|
||||
icon_state = "changeling_organ"
|
||||
setup_type = /obj/effect/temp_visual/exfiltration/changeling
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/changeling/show_activation_message(mob/user)
|
||||
user.visible_message("<span class='notice'>[user] sets a grotesque fleshy mass on the floor.</span>",
|
||||
"<span class='notice'>You set a pulsing piece of yourself on the floor.</span>")
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/mindflayer
|
||||
name = "nanite telepad"
|
||||
desc = "A swarm of mindflayer nanites in the shape of a telepad that will create a single-use portal that will let you escape the station. One way trip."
|
||||
icon_state = "flayer_telepad_base"
|
||||
setup_type = /obj/effect/temp_visual/exfiltration/mindflayer
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/changeling/show_activation_message(mob/user)
|
||||
user.visible_message("<span class='notice'>[user] sets a strange telepad on the floor. It begins to unfold.</span>",
|
||||
"<span class='notice'>You push a button on [src], and watch as it begins to unfold.</span>")
|
||||
|
||||
/obj/item/wormhole_jaunter/extraction/admin
|
||||
name = "advanced extraction flare"
|
||||
desc = "An advanced single-use extraction flare that will let you escape the station quickly. One way trip."
|
||||
delayed_extraction = FALSE
|
||||
extraction_time = 5 SECONDS
|
||||
setup_type = /obj/effect/temp_visual/exfiltration/admin
|
||||
|
||||
// MARK: Traitor Flare
|
||||
/obj/effect/temp_visual/exfiltration
|
||||
name = "extraction flare"
|
||||
layer = BELOW_MOB_LAYER
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "flare-contractor-on"
|
||||
duration = 90.1 SECONDS
|
||||
/// Sound that plays when activated
|
||||
var/activation_sound = 'sound/goonstation/misc/matchstick_light.ogg'
|
||||
/// Light emitted when activated
|
||||
var/emitted_color = "#FFD165"
|
||||
/// Does this one emit light by default?
|
||||
var/start_lit = TRUE
|
||||
|
||||
/obj/effect/temp_visual/exfiltration/Initialize(mapload)
|
||||
. = ..()
|
||||
playsound(loc, activation_sound, 50, TRUE)
|
||||
if(start_lit)
|
||||
set_light(8, l_color = emitted_color)
|
||||
|
||||
// MARK: Vampire Chalice
|
||||
/obj/effect/temp_visual/exfiltration/vampire
|
||||
name = "bloody portal"
|
||||
icon_state = "vampire-portal"
|
||||
activation_sound = 'sound/magic/strings.ogg'
|
||||
emitted_color = "#710C04"
|
||||
|
||||
// MARK: Changeling Mass
|
||||
/obj/effect/temp_visual/exfiltration/changeling
|
||||
name = "writhing mass"
|
||||
icon_state = "changeling-organ"
|
||||
activation_sound = 'sound/effects/squelch1.ogg'
|
||||
emitted_color = "#E79592"
|
||||
|
||||
// MARK: Mindflayer telepad
|
||||
/obj/effect/temp_visual/exfiltration/mindflayer
|
||||
name = "mindflayer telepad"
|
||||
desc = "A swarm of mindflayer nanites in the shape of a telepad that will create a single-use portal."
|
||||
icon_state = "flayer_telepad_deploy"
|
||||
activation_sound = 'sound/mecha/skyfall_power_up.ogg'
|
||||
start_lit = FALSE
|
||||
|
||||
/obj/effect/temp_visual/exfiltration/mindflayer/Initialize(mapload)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, PROC_REF(blink)), 10 SECONDS)
|
||||
|
||||
/obj/effect/temp_visual/exfiltration/mindflayer/proc/blink()
|
||||
icon_state = "flayer_telepad_blink"
|
||||
do_sparks(4, 0, src)
|
||||
addtimer(CALLBACK(src, PROC_REF(no_blink)), 5 SECONDS)
|
||||
|
||||
/obj/effect/temp_visual/exfiltration/mindflayer/proc/no_blink()
|
||||
icon_state = "flayer_telepad_base"
|
||||
do_sparks(4, 0, src)
|
||||
set_light(3, 0.5, LIGHT_COLOR_DARKGREEN)
|
||||
new /obj/effect/flayer_telepad_binary_effect(get_turf(src))
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "qpad-charge"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect/Initialize(mapload)
|
||||
. = ..()
|
||||
appearance_flags |= KEEP_TOGETHER
|
||||
var/icon/our_icon = icon('icons/obj/lighting.dmi', "qpad-charge")
|
||||
var/icon/alpha_mask
|
||||
alpha_mask = new('icons/effects/effects.dmi', "scanline") // Scanline effect.
|
||||
our_icon.AddAlphaMask(alpha_mask) // Finally, let's mix in a distortion effect.
|
||||
icon = our_icon
|
||||
color = list(0.2,0.45,0,0, 0,1,0,0, 0,0,0.2,0, 0,0,0,1, 0,0,0,0)
|
||||
var/mutable_appearance/theme_icon = mutable_appearance('icons/misc/pic_in_pic.dmi', "room_background", appearance_flags = appearance_flags | RESET_TRANSFORM)
|
||||
theme_icon.blend_mode = BLEND_INSET_OVERLAY
|
||||
overlays += theme_icon
|
||||
addtimer(CALLBACK(src, PROC_REF(progress_step)), 1 SECONDS)
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect/proc/progress_step()
|
||||
new /obj/effect/flayer_telepad_binary_effect_secondary(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_secondary
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "qpad-charge2"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_secondary/Initialize(mapload)
|
||||
. = ..()
|
||||
appearance_flags |= KEEP_TOGETHER
|
||||
var/icon/our_icon = icon('icons/obj/lighting.dmi', "qpad-charge2")
|
||||
var/icon/alpha_mask
|
||||
alpha_mask = new('icons/effects/effects.dmi', "scanline") // Scanline effect.
|
||||
our_icon.AddAlphaMask(alpha_mask) // Finally, let's mix in a distortion effect.
|
||||
icon = our_icon
|
||||
color = list(0.2,0.45,0,0, 0,1,0,0, 0,0,0.2,0, 0,0,0,1, 0,0,0,0)
|
||||
var/mutable_appearance/theme_icon = mutable_appearance('icons/misc/pic_in_pic.dmi', "room_background", appearance_flags = appearance_flags | RESET_TRANSFORM)
|
||||
theme_icon.blend_mode = BLEND_INSET_OVERLAY
|
||||
overlays += theme_icon
|
||||
addtimer(CALLBACK(src, PROC_REF(sparky)), 4 SECONDS)
|
||||
addtimer(CALLBACK(src, PROC_REF(progress_step)), 24 SECONDS)
|
||||
addtimer(CALLBACK(src, PROC_REF(complete_portal)), 74 SECONDS)
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_secondary/proc/sparky()
|
||||
new /obj/effect/flayer_binary_portal(get_turf(src))
|
||||
do_sparks(4, 0, src)
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_secondary/proc/progress_step()
|
||||
new /obj/effect/flayer_telepad_binary_effect_tertiary(get_turf(src))
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_secondary/proc/complete_portal()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/flayer_binary_portal
|
||||
icon_state = "bluespace"
|
||||
icon = 'icons/obj/projectiles.dmi'
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
pixel_y = 24
|
||||
|
||||
/obj/effect/flayer_binary_portal/Initialize(mapload)
|
||||
. = ..()
|
||||
// transform *= 0.25
|
||||
appearance_flags |= KEEP_TOGETHER | PIXEL_SCALE
|
||||
var/icon/our_icon = icon('icons/obj/projectiles.dmi', "bluespace")
|
||||
var/icon/alpha_mask
|
||||
alpha_mask = new('icons/effects/effects.dmi', "scanline") // Scanline effect.
|
||||
our_icon.AddAlphaMask(alpha_mask) // Finally, let's mix in a distortion effect.
|
||||
icon = our_icon
|
||||
color = list(0.2,0.45,0,0, 0,1,0,0, 0,0,0.2,0, 0,0,0,1, 0,0,0,0)
|
||||
var/mutable_appearance/theme_icon = mutable_appearance('icons/misc/pic_in_pic.dmi', "room_background", appearance_flags = appearance_flags | RESET_TRANSFORM)
|
||||
theme_icon.blend_mode = BLEND_INSET_OVERLAY
|
||||
overlays += theme_icon
|
||||
addtimer(CALLBACK(src, PROC_REF(wormhole)), 25 SECONDS)
|
||||
animate(src, transform = matrix().Scale(0.25), time = 0.1 SECONDS)
|
||||
animate(transform = matrix().Scale(1, 2), time = 24 SECONDS)
|
||||
animate(transform = matrix().Scale(0.5), time = 0.9 SECONDS)
|
||||
|
||||
/obj/effect/flayer_binary_portal/proc/wormhole()
|
||||
new /obj/effect/flayer_binary_portal_secondary(get_turf(src))
|
||||
do_sparks(4, 0, src)
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/flayer_binary_portal_secondary
|
||||
icon_state = "kinesis"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
pixel_y = 24
|
||||
|
||||
/obj/effect/flayer_binary_portal_secondary/Initialize(mapload)
|
||||
. = ..()
|
||||
set_light(5, 2, LIGHT_COLOR_DARKGREEN)
|
||||
var/obj/effect/warp_effect/bsg/warp = new /obj/effect/warp_effect/bsg(get_turf(src))
|
||||
warp.pixel_y = 24
|
||||
appearance_flags |= KEEP_TOGETHER | PIXEL_SCALE
|
||||
var/icon/our_icon = icon('icons/effects/effects.dmi', "kinesis")
|
||||
var/icon/alpha_mask
|
||||
alpha_mask = new('icons/effects/effects.dmi', "scanline") // Scanline effect.
|
||||
our_icon.AddAlphaMask(alpha_mask) // Finally, let's mix in a distortion effect.
|
||||
icon = our_icon
|
||||
color = list(0.2,0.45,0,0, 0,1,0,0, 0,0,0.2,0, 0,0,0,1, 0,0,0,0)
|
||||
var/mutable_appearance/theme_icon = mutable_appearance('icons/misc/pic_in_pic.dmi', "room_background", appearance_flags = appearance_flags | RESET_TRANSFORM)
|
||||
theme_icon.blend_mode = BLEND_INSET_OVERLAY
|
||||
overlays += theme_icon
|
||||
addtimer(CALLBACK(src, PROC_REF(complete_portal)), 45 SECONDS)
|
||||
animate(src, transform = matrix().Scale(0.3), time = 0.1 SECONDS)
|
||||
animate(transform = matrix().Scale(0.3, 0.6), time = 5 SECONDS)
|
||||
animate(transform = matrix().Scale(0.6, 1.2), time = 15 SECONDS)
|
||||
|
||||
/obj/effect/flayer_binary_portal_secondary/proc/complete_portal()
|
||||
animate(src, transform = matrix().Scale(0.1), time = 0.1 SECONDS)
|
||||
do_sparks(4, 0, src)
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_tertiary
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "qpad-beam"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_tertiary/Initialize(mapload)
|
||||
. = ..()
|
||||
appearance_flags |= KEEP_TOGETHER
|
||||
var/icon/our_icon = icon('icons/obj/lighting.dmi', "qpad-beam")
|
||||
var/icon/alpha_mask
|
||||
alpha_mask = new('icons/effects/effects.dmi', "scanline") // Scanline effect.
|
||||
our_icon.AddAlphaMask(alpha_mask) // Finally, let's mix in a distortion effect.
|
||||
icon = our_icon
|
||||
color = list(0.2,0.45,0,0, 0,1,0,0, 0,0,0.2,0, 0,0,0,1, 0,0,0,0)
|
||||
var/mutable_appearance/theme_icon = mutable_appearance('icons/misc/pic_in_pic.dmi', "room_background", appearance_flags = appearance_flags | RESET_TRANSFORM)
|
||||
theme_icon.blend_mode = BLEND_INSET_OVERLAY
|
||||
overlays += theme_icon
|
||||
addtimer(CALLBACK(src, PROC_REF(complete_portal)), 50 SECONDS)
|
||||
|
||||
/obj/effect/flayer_telepad_binary_effect_tertiary/proc/complete_portal()
|
||||
qdel(src)
|
||||
|
||||
// MARK: Debug/Admin
|
||||
/obj/effect/temp_visual/exfiltration/admin
|
||||
duration = 5.1 SECONDS
|
||||
|
||||
// MARK: Extraction Portal
|
||||
/obj/effect/portal/advanced/exfiltration
|
||||
name = "exfiltration portal"
|
||||
icon_state = "portal-syndicate"
|
||||
one_use = TRUE
|
||||
/// The mind of the exfiltrating antag.
|
||||
var/datum/mind/antag_mind = null
|
||||
/// Radio for handling extraction taunts
|
||||
var/obj/item/radio/radio
|
||||
/// Did we succeed?
|
||||
var/exfil_success = FALSE
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/Initialize(mapload)
|
||||
. = ..()
|
||||
radio = new(src)
|
||||
radio.listening = FALSE
|
||||
radio.follow_target = src
|
||||
radio.config(list("Security" = 0))
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/Destroy()
|
||||
if(exfil_success)
|
||||
return ..()
|
||||
if(antag_mind)
|
||||
log_and_message_admins("[antag_mind.original_mob_name] failed to exfiltrate.")
|
||||
SSblackbox.record_feedback("tally", "exfiltration", 1, "exfiltration_failure")
|
||||
return ..()
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/can_teleport(atom/movable/A)
|
||||
var/mob/living/M = A
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/attempt_teleport(atom/movable/victim, turf/destination, variance = 0, force_teleport = TRUE)
|
||||
if(teleports_this_cycle >= MAX_ALLOWED_TELEPORTS_PER_PROCESS)
|
||||
return
|
||||
var/use_effects = world.time >= effect_cooldown
|
||||
var/effect = null // Will result in the default effect being used
|
||||
if(!use_effects)
|
||||
effect = NONE // No effect
|
||||
|
||||
// Filter out non-antags
|
||||
var/mob/living/user = victim
|
||||
if(!istype(user))
|
||||
return FALSE
|
||||
var/datum/mind/user_mind = user.mind
|
||||
if(user_mind != antag_mind)
|
||||
to_chat(user, "<span class='warning'>You jump through the portal! Right before you see the other side, you feel an immense, sharp pain in your head!</span>")
|
||||
var/list/L = get_destinations()
|
||||
if(!length(L))
|
||||
to_chat(user, "<span class='warning'>[src] found no beacons in the sector to target.</span>")
|
||||
return
|
||||
var/random_destination = pick(L)
|
||||
if(!do_teleport(victim, random_destination, 2, force_teleport, effect, effect, bypass_area_flag = ignore_tele_proof_area_setting))
|
||||
invalid_teleport()
|
||||
return FALSE
|
||||
fail_extraction(user)
|
||||
effect_cooldown = world.time + 0.5 SECONDS
|
||||
teleports_this_cycle++
|
||||
return FALSE
|
||||
|
||||
// For the antag
|
||||
if(!do_teleport(victim, destination, variance, force_teleport, effect, effect, bypass_area_flag = ignore_tele_proof_area_setting))
|
||||
invalid_teleport()
|
||||
return FALSE
|
||||
pass_extraction(user)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/proc/get_destinations()
|
||||
var/list/destinations = list()
|
||||
|
||||
for(var/obj/item/beacon/B in GLOB.beacons)
|
||||
var/turf/T = get_turf(B)
|
||||
if(is_station_level(T.z))
|
||||
destinations += B
|
||||
|
||||
return destinations
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/proc/fail_extraction(mob/living/schmuck)
|
||||
to_chat(schmuck, "<span class='warning'>You feel immense pain!</span>")
|
||||
log_and_message_admins("[schmuck] went through an exfiltration portal they didn't control.")
|
||||
schmuck.Paralyse(30 SECONDS)
|
||||
schmuck.EyeBlind(35 SECONDS)
|
||||
schmuck.EyeBlurry(35 SECONDS)
|
||||
schmuck.AdjustConfused(35 SECONDS)
|
||||
// Same effects as if they got contracted
|
||||
var/obj/item/organ/external/injury_target
|
||||
if(prob(20))
|
||||
injury_target = schmuck.get_organ(pick(BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT))
|
||||
if(!injury_target)
|
||||
return
|
||||
schmuck.adjustBruteLoss(40)
|
||||
schmuck.adjustBrainLoss(25)
|
||||
injury_target.droplimb()
|
||||
to_chat(schmuck, "<span class='warning'>You were interrogated by your captors before being sent back! Oh god, something's missing!</span>")
|
||||
return
|
||||
// Species specific punishments first
|
||||
if(ismachineperson(schmuck))
|
||||
schmuck.emp_act(EMP_HEAVY)
|
||||
schmuck.adjustBrainLoss(30)
|
||||
to_chat(schmuck, "<span class='warning'>You were interrogated by your captors before being sent back! You feel like some of your components are loose!</span>")
|
||||
return
|
||||
schmuck.adjustBruteLoss(40)
|
||||
schmuck.adjustBrainLoss(25)
|
||||
if(isslimeperson(schmuck))
|
||||
injury_target = schmuck.get_organ(pick(BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT))
|
||||
if(!injury_target)
|
||||
return
|
||||
injury_target.cause_internal_bleeding()
|
||||
injury_target = schmuck.get_organ(BODY_ZONE_CHEST)
|
||||
injury_target.cause_internal_bleeding()
|
||||
to_chat(schmuck, "<span class='warning'>You were interrogated by your captors before being sent back! You feel like your inner membrane has been punctured!</span>")
|
||||
return
|
||||
if(prob(25))
|
||||
injury_target = schmuck.get_organ(BODY_ZONE_CHEST)
|
||||
injury_target.fracture()
|
||||
else
|
||||
injury_target = schmuck.get_organ(pick(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_R_LEG))
|
||||
if(!injury_target)
|
||||
return
|
||||
injury_target.fracture()
|
||||
injury_target.cause_internal_bleeding()
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/proc/pass_extraction(mob/living/M)
|
||||
// Freeze objectives
|
||||
for(var/datum/objective/objective in antag_mind.get_all_objectives(include_team = FALSE))
|
||||
if(istype(objective, /datum/objective/escape))
|
||||
var/datum/objective/escape/escape_alive = objective
|
||||
escape_alive.completed = escape_alive.check_completion(exfilling = TRUE)
|
||||
continue
|
||||
objective.completed = objective.check_completion()
|
||||
// Handle syndicate barification
|
||||
log_and_message_admins("[M] extracted using an exfiltration portal.")
|
||||
exfil_success = TRUE
|
||||
SSblackbox.record_feedback("tally", "exfiltration", 1, "exfiltration_success")
|
||||
prepare_ghosting(M)
|
||||
|
||||
/obj/effect/portal/advanced/exfiltration/proc/prepare_ghosting(mob/living/carbon/human/extractor)
|
||||
if(!istype(extractor))
|
||||
return
|
||||
// Remove all clothing and bio chips
|
||||
for(var/obj/item/I in extractor)
|
||||
qdel(I)
|
||||
|
||||
// Remove cybernetic implants
|
||||
for(var/obj/item/organ/internal/cyberimp/I in extractor.internal_organs)
|
||||
// Greys get to keep their implant
|
||||
if(isgrey(extractor) && istype(I, /obj/item/organ/internal/cyberimp/brain/speech_translator))
|
||||
continue
|
||||
// IPCs keep their implant
|
||||
if(ismachineperson(extractor) && istype(I, /obj/item/organ/internal/cyberimp/arm/power_cord))
|
||||
continue
|
||||
// Try removing it
|
||||
I = I.remove(extractor)
|
||||
|
||||
// Remove martial arts
|
||||
for(var/datum/martial_art/MA in extractor.mind.known_martial_arts)
|
||||
MA.remove(extractor)
|
||||
|
||||
// Equip outfits and remove spells
|
||||
var/datum/mind/extractor_mind = extractor.mind
|
||||
for(var/datum/antagonist/antag in extractor_mind.antag_datums)
|
||||
antag.exfiltrate(extractor, radio)
|
||||
|
||||
// Apply traits
|
||||
ADD_TRAIT(extractor, TRAIT_PACIFISM, GHOST_ROLE)
|
||||
ADD_TRAIT(extractor, TRAIT_RESPAWNABLE, GHOST_ROLE)
|
||||
var/obj/item/bio_chip/dust/I = new
|
||||
I.implant(extractor, null)
|
||||
|
||||
if(extractor.mind)
|
||||
if(extractor.mind.initial_account)
|
||||
GLOB.station_money_database.delete_user_account(extractor.mind.initial_account.account_number, "NAS Trurl Financial Services", FALSE)
|
||||
if(extractor.mind && extractor.mind.assigned_role)
|
||||
// Handle job slot
|
||||
var/job = extractor.mind.assigned_role
|
||||
|
||||
SSjobs.FreeRole(job)
|
||||
|
||||
@@ -50,6 +50,7 @@ RESTRICT_TYPE(/datum/antagonist/changeling)
|
||||
var/regenerating = FALSE
|
||||
blurb_text_color = COLOR_PURPLE
|
||||
blurb_text_outline_width = 1
|
||||
boss_title = "Greater Hivemind"
|
||||
|
||||
/datum/antagonist/changeling/New()
|
||||
..()
|
||||
@@ -186,6 +187,14 @@ RESTRICT_TYPE(/datum/antagonist/changeling)
|
||||
chem_charges = clamp(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown, chem_storage)
|
||||
update_chem_charges_ui(H)
|
||||
|
||||
/datum/antagonist/changeling/exfiltrate(mob/living/carbon/human/extractor, obj/item/radio/radio)
|
||||
remove_changeling_powers(FALSE)
|
||||
var/datum/action/changeling/power = new /datum/action/changeling/transform
|
||||
power.Grant(extractor)
|
||||
extractor.equipOutfit(/datum/outfit/admin/ghostbar_antag/changeling)
|
||||
radio.autosay("<b>--ZZZT!- Welcome home, [extractor.real_name]. -ZZT!-</b>", "Changeling Hive", "Security")
|
||||
SSblackbox.record_feedback("tally", "successful_extraction", 1, "Changeling")
|
||||
|
||||
/datum/antagonist/changeling/proc/update_chem_charges_ui(mob/living/carbon/human/H = owner.current)
|
||||
if(H.hud_used?.lingchemdisplay)
|
||||
H.hud_used.lingchemdisplay.invisibility = 0
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/datum/action/changeling/exfiltration
|
||||
name = "Generate Extraction Mass"
|
||||
desc = "We separate a specialized organ that will generate an extraction portal. Costs 30 chemicals."
|
||||
helptext = "Creates a fleshy mass that can be used in certain areas to create a portal to leave the station early."
|
||||
button_icon = 'icons/obj/items.dmi'
|
||||
button_icon_state = "changeling-organ"
|
||||
chemical_cost = 30
|
||||
req_human = TRUE
|
||||
power_type = CHANGELING_INNATE_POWER
|
||||
|
||||
// Create extraction mass
|
||||
/datum/action/changeling/exfiltration/sting_action(mob/living/user)
|
||||
cling.prepare_exfiltration(user, /obj/item/wormhole_jaunter/extraction/changeling)
|
||||
cling.remove_specific_power(/datum/action/changeling/exfiltration)
|
||||
return TRUE
|
||||
@@ -27,6 +27,7 @@
|
||||
var/can_pick_capstone = TRUE
|
||||
/// Have we notified that our victim does not give swarms from draining
|
||||
var/has_notified = FALSE
|
||||
boss_title = "Master Mindflayer"
|
||||
|
||||
/datum/antagonist/mindflayer/New()
|
||||
. = ..()
|
||||
@@ -60,6 +61,13 @@
|
||||
add_antag_objective(/datum/objective/swarms)
|
||||
forge_basic_objectives()
|
||||
|
||||
/datum/antagonist/mindflayer/exfiltrate(mob/living/carbon/human/extractor, obj/item/radio/radio)
|
||||
remove_all_abilities()
|
||||
remove_all_passives()
|
||||
extractor.equipOutfit(/datum/outfit/admin/ghostbar_antag/mindflayer)
|
||||
radio.autosay("<b>--ZZZT!- Excellent job, [extractor.real_name]. Proceed to -^%&!-ZZT!-</b>", "Master Flayer", "Security")
|
||||
SSblackbox.record_feedback("tally", "successful_extraction", 1, "Mindflayer")
|
||||
|
||||
/datum/antagonist/mindflayer/proc/get_swarms()
|
||||
return usable_swarms
|
||||
|
||||
|
||||
@@ -221,3 +221,19 @@
|
||||
|
||||
borg.make_mindflayer_robot(user)
|
||||
return TRUE
|
||||
|
||||
/datum/spell/flayer/self/extraction
|
||||
name = "Nanite Portal Generator"
|
||||
desc = "Allows us to use our nanites to create an extraction portal."
|
||||
action_icon = 'icons/obj/lighting.dmi'
|
||||
action_icon_state = "flayer_telepad_base"
|
||||
power_type = FLAYER_PURCHASABLE_POWER
|
||||
base_cooldown = 2 SECONDS
|
||||
var/used = FALSE
|
||||
|
||||
/datum/spell/flayer/self/extraction/cast(list/targets, mob/user)
|
||||
if(used)
|
||||
to_chat(user, "<span class='warning'>You have already attempted to create a portal generator!</span>")
|
||||
return
|
||||
flayer.prepare_exfiltration(user, /obj/item/wormhole_jaunter/extraction/mindflayer)
|
||||
used = TRUE
|
||||
|
||||
@@ -17,6 +17,7 @@ RESTRICT_TYPE(/datum/antagonist/traitor)
|
||||
var/give_uplink = TRUE
|
||||
blurb_r = 200
|
||||
blurb_a = 0.75
|
||||
boss_title = "Syndicate Operations"
|
||||
|
||||
/// Have we / are we sending a backstab message at this time. If we are, do not send another.
|
||||
var/sending_backstab = FALSE
|
||||
@@ -116,6 +117,20 @@ RESTRICT_TYPE(/datum/antagonist/traitor)
|
||||
else
|
||||
forge_human_objectives()
|
||||
|
||||
/datum/antagonist/traitor/exfiltrate(mob/living/carbon/human/extractor, obj/item/radio/radio)
|
||||
extractor.equipOutfit(/datum/outfit/admin/ghostbar_antag/syndicate)
|
||||
// Remove mindslaves
|
||||
var/list/mindslaves = SSticker.mode.implanted
|
||||
for(var/datum/mind/possible_slave in mindslaves)
|
||||
for(var/datum/antagonist/slavetag in possible_slave.antag_datums)
|
||||
if(!istype(slavetag, /datum/antagonist/mindslave))
|
||||
continue
|
||||
var/datum/antagonist/mindslave/slave = slavetag
|
||||
if(slave.master == extractor.mind)
|
||||
possible_slave.remove_antag_datum(/datum/antagonist/mindslave/implant)
|
||||
|
||||
radio.autosay("<b>--ZZZT!- Good work, $@gent [extractor.real_name]. Return to -^%&!-ZZT!-</b>", "Syndicate Operations", "Security")
|
||||
SSblackbox.record_feedback("tally", "successful_extraction", 1, "Traitor")
|
||||
/**
|
||||
* Create and assign a full set of randomized human traitor objectives.
|
||||
*/
|
||||
|
||||
@@ -24,6 +24,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
|
||||
/datum/spell/vampire/glare = 0,
|
||||
/datum/vampire_passive/vision = 100,
|
||||
/datum/spell/vampire/self/specialize = 150,
|
||||
/datum/spell/vampire/self/exfiltrate = 150,
|
||||
/datum/vampire_passive/regen = 200,
|
||||
/datum/vampire_passive/vision/advanced = 500)
|
||||
|
||||
@@ -34,6 +35,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
|
||||
blurb_g = 221
|
||||
blurb_b = 138
|
||||
blurb_a = 1
|
||||
boss_title = "Master Vampire"
|
||||
|
||||
/datum/antagonist/vampire/Destroy(force, ...)
|
||||
draining = null
|
||||
@@ -95,6 +97,23 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
|
||||
REMOVE_TRAITS_IN(owner.current, "vampire")
|
||||
UnregisterSignal(owner, COMSIG_ATOM_HOLY_ATTACK)
|
||||
|
||||
/datum/antagonist/vampire/exfiltrate(mob/living/carbon/human/extractor, obj/item/radio/radio)
|
||||
remove_all_powers()
|
||||
// Remove thralls
|
||||
if(istype(subclass, SUBCLASS_DANTALION))
|
||||
var/list/thralls = SSticker.mode.vampire_enthralled
|
||||
for(var/datum/mind/possible_thrall in thralls)
|
||||
for(var/datum/antagonist/slavetag in possible_thrall.antag_datums)
|
||||
if(!istype(slavetag, /datum/antagonist/mindslave))
|
||||
continue
|
||||
var/datum/antagonist/mindslave/slave = slavetag
|
||||
if(slave.master == extractor.mind)
|
||||
possible_thrall.remove_antag_datum(/datum/antagonist/mindslave/thrall)
|
||||
|
||||
extractor.equipOutfit(/datum/outfit/admin/ghostbar_antag/vampire)
|
||||
radio.autosay("<b>--ZZZT!- Wonderfully done, [extractor.real_name]. Welcome to -^%&!-ZZT!-</b>", "Ancient Vampire", "Security")
|
||||
SSblackbox.record_feedback("tally", "successful_extraction", 1, "Vampire")
|
||||
|
||||
#define BLOOD_GAINED_MODIFIER 0.5
|
||||
|
||||
/datum/antagonist/vampire/proc/handle_bloodsucking(mob/living/carbon/human/H, suck_rate = 5 SECONDS)
|
||||
|
||||
@@ -108,6 +108,22 @@
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/spell/vampire/self/exfiltrate
|
||||
name = "Conjure Blood Chalice"
|
||||
desc = "Congeal blood into a chalice that will generate a portal away from the station."
|
||||
gain_desc = "You can now decide to leave the station."
|
||||
base_cooldown = 2 SECONDS
|
||||
action_icon = 'icons/obj/items.dmi'
|
||||
action_icon_state = "blood-chalice"
|
||||
var/used = FALSE
|
||||
|
||||
/datum/spell/vampire/self/exfiltrate/cast(mob/user)
|
||||
if(used)
|
||||
to_chat(user, "<span class='warning'>You have already attempted to create a blood chalice!</span>")
|
||||
return
|
||||
var/datum/antagonist/vampire/vamp = user.mind.has_antag_datum(/datum/antagonist/vampire)
|
||||
vamp.prepare_exfiltration(user, /obj/item/wormhole_jaunter/extraction/vampire)
|
||||
used = TRUE
|
||||
|
||||
/datum/spell/vampire/self/specialize
|
||||
name = "Choose Specialization"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 107 KiB |
@@ -701,6 +701,7 @@
|
||||
#include "code\datums\outfits\outfit.dm"
|
||||
#include "code\datums\outfits\outfit_admin.dm"
|
||||
#include "code\datums\outfits\outfit_debug.dm"
|
||||
#include "code\datums\outfits\outfit_ghostbar.dm"
|
||||
#include "code\datums\outfits\plasmamen_outfits.dm"
|
||||
#include "code\datums\outfits\vv_outfit.dm"
|
||||
#include "code\datums\proximity\advanced_proximity_monitor.dm"
|
||||
@@ -1663,6 +1664,7 @@
|
||||
#include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm"
|
||||
#include "code\modules\admin\verbs\SDQL2\useful_procs.dm"
|
||||
#include "code\modules\antagonists\_common\antag_datum.dm"
|
||||
#include "code\modules\antagonists\_common\antag_exfiltration.dm"
|
||||
#include "code\modules\antagonists\_common\antag_helpers.dm"
|
||||
#include "code\modules\antagonists\_common\antag_hud.dm"
|
||||
#include "code\modules\antagonists\_common\antag_spawner.dm"
|
||||
@@ -1685,6 +1687,7 @@
|
||||
#include "code\modules\antagonists\changeling\powers\digitalcamo.dm"
|
||||
#include "code\modules\antagonists\changeling\powers\environmental_adaption.dm"
|
||||
#include "code\modules\antagonists\changeling\powers\epinephrine.dm"
|
||||
#include "code\modules\antagonists\changeling\powers\exfilling.dm"
|
||||
#include "code\modules\antagonists\changeling\powers\fakedeath.dm"
|
||||
#include "code\modules\antagonists\changeling\powers\fleshmend.dm"
|
||||
#include "code\modules\antagonists\changeling\powers\hivemind.dm"
|
||||
|
||||
Reference in New Issue
Block a user