Files
Bubberstation/code/game/machinery/doors/airlock.dm
Leland Kemble c53286a625 Changes bolt light wire to feedback wire, which disables both lights and feedback sounds (#92950)
## About The Pull Request

Changes the "Bolt Lights" wire to a "Feedback" wire, which as well as
disabling the door's lights also disables the deny access sound. Sounds
that happen because the door is actually moving or bolting are
unchanged.

## Why It's Good For The Game

closes #65244
Justification is mostly in mentioned issue, primarily the accessibility
issue of denied door feedback being sound-only when the lights are cut.
Also, "Bolt Lights" is just inaccurate, as it controls all of the lights
on the door.

## Changelog
🆑

balance: The "Bolt Lights" wire is now the "Feedback" wire, and controls
both lights and sounds for the airlock.

/🆑
2025-09-15 17:38:24 -04:00

2625 lines
89 KiB
Plaintext

/*
New methods:
pulse - sends a pulse into a wire for hacking purposes
cut - cuts a wire and makes any necessary state changes
mend - mends a wire and makes any necessary state changes
canAIControl - 1 if the AI can control the airlock, 0 if not (then check canAIHack to see if it can hack in)
canAIHack - 1 if the AI can hack into the airlock to recover control, 0 if not. Also returns 0 if the AI does not *need* to hack it.
hasPower - 1 if the main or backup power are functioning, 0 if not.
requiresIDs - 1 if the airlock is requiring IDs, 0 if not
isAllPowerCut - 1 if the main and backup power both have cut wires.
regainMainPower - handles the effect of main power coming back on.
loseMainPower - handles the effect of main power going offline. Usually (if one isn't already running) spawn a thread to count down how long it will be offline - counting down won't happen if main power was completely cut along with backup power, though, the thread will just sleep.
loseBackupPower - handles the effect of backup power going offline.
regainBackupPower - handles the effect of main power coming back on.
shock - has a chance of electrocuting its target.
isSecure - 1 if there some form of shielding in front of the airlock wires.
*/
/// Overlay cache. Why isn't this just in /obj/machinery/door/airlock? Because its used just a
/// tiny bit in door_assembly.dm Refactored so you don't have to make a null copy of airlock
/// to get to the damn thing
/// Someone, for the love of god, profile this. Is there a reason to cache mutable_appearance
/// if so, why are we JUST doing the airlocks when we can put this in mutable_appearance.dm for
/// everything
/proc/get_airlock_overlay(icon_state, icon_file, atom/offset_spokesman, em_block)
var/static/list/airlock_overlays = list()
var/base_icon_key = "[icon_state][REF(icon_file)]"
if(!(. = airlock_overlays[base_icon_key]))
. = airlock_overlays[base_icon_key] = mutable_appearance(icon_file, icon_state)
if(isnull(em_block))
return
var/turf/our_turf = get_turf(offset_spokesman)
var/em_block_key = "[base_icon_key][em_block][GET_TURF_PLANE_OFFSET(our_turf)]"
var/mutable_appearance/em_blocker = airlock_overlays[em_block_key]
if(!em_blocker)
em_blocker = airlock_overlays[em_block_key] = mutable_appearance(icon_file, icon_state, offset_spokesman = offset_spokesman, plane = EMISSIVE_PLANE, appearance_flags = EMISSIVE_APPEARANCE_FLAGS)
em_blocker.color = em_block ? GLOB.em_block_color : GLOB.emissive_color
return list(., em_blocker)
// Before you say this is a bad implmentation, look at what it was before then ask yourself
// "Would this be better with a global var"
// Wires for the airlock are located in the datum folder, inside the wires datum folder.
#define AIRLOCK_FRAME_CLOSED "closed"
#define AIRLOCK_FRAME_CLOSING "closing"
#define AIRLOCK_FRAME_OPEN "open"
#define AIRLOCK_FRAME_OPENING "opening"
#define AIRLOCK_SECURITY_NONE 0 //Normal airlock //Wires are not secured
#define AIRLOCK_SECURITY_IRON 1 //Medium security airlock //There is a simple iron plate over wires (use welder)
#define AIRLOCK_SECURITY_PLASTEEL_I_S 2 //Sliced inner plating (use crowbar), jumps to 0
#define AIRLOCK_SECURITY_PLASTEEL_I 3 //Removed outer plating, second layer here (use welder)
#define AIRLOCK_SECURITY_PLASTEEL_O_S 4 //Sliced outer plating (use crowbar)
#define AIRLOCK_SECURITY_PLASTEEL_O 5 //There is first layer of plasteel (use welder)
#define AIRLOCK_SECURITY_PLASTEEL 6 //Max security airlock //Fully secured wires (use wirecutters to remove grille, that is electrified)
#define AIRLOCK_INTEGRITY_N 300 // Normal airlock integrity
#define AIRLOCK_INTEGRITY_MULTIPLIER 1.5 // How much reinforced doors health increases
/// How much extra health airlocks get when braced with a seal
#define AIRLOCK_SEAL_MULTIPLIER 2
#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
#define AIRLOCK_DENY_ANIMATION_TIME (0.6 SECONDS) /// The amount of time for the airlock deny animation to show
#define DOOR_CLOSE_WAIT 60 /// Time before a door closes, if not overridden
#define DOOR_VISION_DISTANCE 11 ///The maximum distance a door will see out to
/obj/machinery/door/airlock
name = "Airlock"
icon = 'icons/obj/doors/airlocks/station/public.dmi'
icon_state = "closed"
base_icon_state = null
max_integrity = 300
var/normal_integrity = AIRLOCK_INTEGRITY_N
integrity_failure = 0.25
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
autoclose = TRUE
explosion_block = 1
hud_possible = list(DIAG_AIRLOCK_HUD)
smoothing_groups = SMOOTH_GROUP_AIRLOCK
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_OPEN
interaction_flags_click = ALLOW_SILICON_REACH
blocks_emissive = EMISSIVE_BLOCK_NONE // Custom emissive blocker. We don't want the normal behavior.
///The type of door frame to drop during deconstruction
var/assemblytype = /obj/structure/door_assembly
/// How much are wires secured
var/security_level = 0
/// If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
var/aiControlDisabled = AI_WIRE_NORMAL
/// If true, this door can't be hacked by the AI
var/hackProof = FALSE
/// Timer id, active when we are actively waiting for the main power to be restored
var/main_power_timer = 0
/// Paired with main_power_timer. Records its remaining time when something happens to interrupt power regen
var/main_power_time
/// Timer id, active when we are actively waiting for the backup power to be restored
var/backup_power_timer = 0
/// Paired with backup_power_timer. Records its remaining time when something happens to interrupt power regen
var/backup_power_time
/// Lights and sounds enabled by default
var/feedback = TRUE
var/aiDisabledIdScanner = FALSE
var/aiHacking = FALSE
/// Cyclelinking for airlocks that aren't on the same x or y coord as the target.
var/closeOtherId
var/obj/machinery/door/airlock/closeOther
var/list/obj/machinery/door/airlock/close_others = list()
var/obj/item/electronics/airlock/electronics
COOLDOWN_DECLARE(shockCooldown)
/// Any papers pinned to the airlock
var/obj/item/note
/// The seal on the airlock
var/obj/item/seal
var/abandoned = FALSE
/// Controls if the door closes quickly or not. FALSE = the door autocloses in 1.5 seconds, TRUE = 8 seconds - see autoclose_in()
var/normalspeed = TRUE
var/cutAiWire = FALSE
var/autoname = FALSE
var/doorOpen = 'sound/machines/airlock/airlock.ogg'
var/doorClose = 'sound/machines/airlock/airlockclose.ogg'
var/doorDeni = 'sound/machines/beep/deniedbeep.ogg' // i'm thinkin' Deni's
var/boltUp = 'sound/machines/airlock/boltsup.ogg'
var/boltDown = 'sound/machines/airlock/boltsdown.ogg'
var/noPower = 'sound/machines/airlock/doorclick.ogg'
/// What airlock assembly mineral plating was applied to
var/previous_airlock = /obj/structure/door_assembly
/// Material of inner filling; if its an airlock with glass, this should be set to "glass"
var/airlock_material
var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
/// Used for papers and photos pinned to the airlock
var/note_overlay_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
/// Airlock pump that overrides airlock controlls when set up for cycling
var/obj/machinery/atmospherics/components/unary/airlock_pump/cycle_pump
var/cyclelinkeddir = 0
var/obj/machinery/door/airlock/cyclelinkedairlock
var/shuttledocked = 0
/// TRUE means the door will automatically close the next time it's opened.
var/delayed_close_requested = FALSE
/// TRUE means density will be set as soon as the door begins to close
var/air_tight = FALSE
var/prying_so_hard = FALSE
/// Logging for door electrification.
var/shockedby
/// How many seconds remain until the door is no longer electrified. -1/MACHINE_ELECTRIFIED_PERMANENT = permanently electrified until someone fixes it.
var/secondsElectrified = MACHINE_NOT_ELECTRIFIED
flags_1 = HTML_USE_INITAL_ICON_1
rad_insulation = RAD_MEDIUM_INSULATION
/obj/machinery/door/airlock/get_save_vars()
. = ..()
. -= NAMEOF(src, icon_state) // airlocks ignore icon_state and instead use get_airlock_overlay()
// TODO save the wire data but need to include states for cute wires, signalers attached to wires, etc.
return .
/obj/machinery/door/airlock/Initialize(mapload)
if(glass)
airlock_material = "glass"
. = ..()
set_wires(get_wires())
if(security_level > AIRLOCK_SECURITY_IRON)
atom_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
max_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
else
atom_integrity = normal_integrity
max_integrity = normal_integrity
if(damage_deflection == AIRLOCK_DAMAGE_DEFLECTION_N && security_level > AIRLOCK_SECURITY_IRON)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
prepare_huds()
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_atom_to_hud(src)
diag_hud_set_electrified()
// Click on the floor to close airlocks
AddComponent(/datum/component/redirect_attack_hand_from_turf)
AddElement(/datum/element/nav_computer_icon, 'icons/effects/nav_computer_indicators.dmi', "airlock", TRUE)
RegisterSignal(src, COMSIG_MACHINERY_BROKEN, PROC_REF(on_break))
RegisterSignal(SSdcs, COMSIG_GLOB_GREY_TIDE, PROC_REF(grey_tide))
/obj/machinery/door/airlock/proc/grey_tide(datum/source, list/grey_tide_areas)
SIGNAL_HANDLER
if(!is_station_level(z) || critical_machine)
return //Skip doors in critical positions, such as the SM chamber.
for(var/area_type in grey_tide_areas)
if(!istype(get_area(src), area_type))
continue
INVOKE_ASYNC(src, PROC_REF(prison_open)) //Sleep gets called further down in open(), so we have to invoke async
/obj/machinery/door/airlock/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
if(id_tag)
id_tag = "[port.shuttle_id]_[id_tag]"
/obj/machinery/door/airlock/proc/update_other_id()
for(var/obj/machinery/door/airlock/Airlock as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/airlock))
if(Airlock.closeOtherId == closeOtherId && Airlock != src)
if(!(Airlock in close_others))
close_others += Airlock
if(!(src in Airlock.close_others))
Airlock.close_others += src
/obj/machinery/door/airlock/proc/cyclelinkairlock()
if (cyclelinkedairlock)
cyclelinkedairlock.cyclelinkedairlock = null
cyclelinkedairlock = null
if (!cyclelinkeddir)
return
var/limit = DOOR_VISION_DISTANCE
var/turf/T = get_turf(src)
var/obj/machinery/door/airlock/FoundDoor
do
T = get_step(T, cyclelinkeddir)
FoundDoor = locate() in T
if (FoundDoor && FoundDoor.cyclelinkeddir != get_dir(FoundDoor, src))
FoundDoor = null
limit--
while(!FoundDoor && limit)
if (!FoundDoor)
log_mapping("[src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
return
FoundDoor.cyclelinkedairlock = src
cyclelinkedairlock = FoundDoor
/obj/machinery/door/airlock/vv_edit_var(var_name, vval)
. = ..()
switch (var_name)
if (NAMEOF(src, cyclelinkeddir))
cyclelinkairlock()
if (NAMEOF(src, secondsElectrified))
set_electrified(vval < MACHINE_NOT_ELECTRIFIED ? MACHINE_ELECTRIFIED_PERMANENT : vval) //negative values are bad mkay (unless they're the intended negative value!)
/obj/machinery/door/airlock/lock()
bolt()
/obj/machinery/door/airlock/proc/bolt()
if(locked)
return
set_bolt(TRUE)
playsound(src,boltDown,30,FALSE,3)
audible_message(span_hear("You hear a click from the bottom of the door."), null, 1)
update_appearance()
/obj/machinery/door/airlock/proc/set_bolt(should_bolt)
if(locked == should_bolt)
return
SEND_SIGNAL(src, COMSIG_AIRLOCK_SET_BOLT, should_bolt)
. = locked
locked = should_bolt
/obj/machinery/door/airlock/unlock()
unbolt()
/obj/machinery/door/airlock/proc/unbolt()
if(!locked)
return
set_bolt(FALSE)
playsound(src,boltUp,30,FALSE,3)
audible_message(span_hear("You hear a click from the bottom of the door."), null, 1)
update_appearance()
/obj/machinery/door/airlock/narsie_act()
var/turf/T = get_turf(src)
var/obj/machinery/door/airlock/cult/A
if(GLOB.cult_narsie)
var/runed = prob(20)
if(glass)
if(runed)
A = new/obj/machinery/door/airlock/cult/glass(T)
else
A = new/obj/machinery/door/airlock/cult/unruned/glass(T)
else
if(runed)
A = new/obj/machinery/door/airlock/cult(T)
else
A = new/obj/machinery/door/airlock/cult/unruned(T)
A.name = name
else
A = new /obj/machinery/door/airlock/cult/weak(T)
qdel(src)
/obj/machinery/door/airlock/Destroy()
QDEL_NULL(electronics)
if (cyclelinkedairlock)
if (cyclelinkedairlock.cyclelinkedairlock == src)
cyclelinkedairlock.cyclelinkedairlock = null
cyclelinkedairlock = null
if(close_others) //remove this airlock from the list of every linked airlock
closeOtherId = null
for(var/obj/machinery/door/airlock/otherlock as anything in close_others)
otherlock.close_others -= src
close_others.Cut()
QDEL_NULL(note)
QDEL_NULL(seal)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.remove_atom_from_hud(src)
return ..()
/obj/machinery/door/airlock/Exited(atom/movable/gone, direction)
. = ..()
if(gone == note)
note = null
update_appearance()
if(gone == seal)
seal = null
update_appearance()
/obj/machinery/door/airlock/bumpopen(mob/living/user)
if(!hasPower())
return
if(issilicon(user) || !iscarbon(user))
return ..()
if(isElectrified() && shock(user, 100))
return
if(SEND_SIGNAL(user, COMSIG_CARBON_BUMPED_AIRLOCK_OPEN, src) & STOP_BUMP)
return
return ..()
/obj/machinery/door/airlock/proc/isElectrified()
return (secondsElectrified != MACHINE_NOT_ELECTRIFIED)
/obj/machinery/door/airlock/proc/canAIControl(mob/user)
return ((aiControlDisabled != AI_WIRE_DISABLED) && !isAllPowerCut())
/obj/machinery/door/airlock/proc/canAIHack()
return ((aiControlDisabled == AI_WIRE_DISABLED) && (!hackProof) && (!isAllPowerCut()));
/obj/machinery/door/airlock/hasPower()
return ((!remaining_main_outage() || !remaining_backup_outage()) && !(machine_stat & NOPOWER))
/obj/machinery/door/airlock/requiresID()
return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner)
/obj/machinery/door/airlock/proc/isAllPowerCut()
if((wires.is_cut(WIRE_POWER1) || wires.is_cut(WIRE_POWER2)) && (wires.is_cut(WIRE_BACKUP1) || wires.is_cut(WIRE_BACKUP2)))
return TRUE
/// Returns the amount of time we have to wait before main power comes back
/// Assuming it was actively regenerating
/// Returns 0 if it is active
/obj/machinery/door/airlock/proc/remaining_main_outage()
if(main_power_timer)
return timeleft(main_power_timer)
return main_power_time
/// Returns the amount of time we have to wait before backup power comes back
/// Assuming it was actively regenerating
/// Returns 0 if it is active
/obj/machinery/door/airlock/proc/remaining_backup_outage()
if(backup_power_timer)
return timeleft(backup_power_timer)
return backup_power_time
/obj/machinery/door/airlock/proc/set_main_outage(delay)
// Clear out the timer so we don't accidentially take from it later
if(main_power_timer)
deltimer(main_power_timer)
main_power_timer = null
var/old_time = main_power_time
main_power_time = delay
handle_main_power()
if(!!old_time != !!delay)
update_appearance()
/obj/machinery/door/airlock/proc/set_backup_outage(delay)
// Clear out the timer so we don't accidentially take from it later
if(backup_power_timer)
deltimer(backup_power_timer)
backup_power_timer = null
var/old_time = backup_power_time
backup_power_time = delay
handle_backup_power()
if(!!old_time != !!delay)
update_appearance()
/// Call to update our main power outage timer
/// Will trigger a proper timer if we're actively restoring power, if not we'll dump the remaining time in a var on the airlock
/obj/machinery/door/airlock/proc/handle_main_power()
if(main_power_time <= 0)
deltimer(main_power_timer)
main_power_timer = null
return
// If we can, we'll start a timer that hits when we're done
if(!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
if(!main_power_timer || timeleft(main_power_timer) != main_power_time)
main_power_timer = addtimer(CALLBACK(src, PROC_REF(regainMainPower)), main_power_time, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE|TIMER_DELETE_ME)
// Otherwise, we'll ensure the timer matches main_power_time
else if(main_power_timer)
main_power_time = timeleft(main_power_timer)
deltimer(main_power_timer)
main_power_timer = null
/// Call to update our backup power outage timer
/// Will trigger a proper timer if we're actively restoring power, if not we'll dump the remaining time in a var on the airlock
/obj/machinery/door/airlock/proc/handle_backup_power()
if(backup_power_time <= 0)
deltimer(backup_power_timer)
backup_power_timer = null
return
// If we can, we'll start a timer that hits when we're done
if(!wires.is_cut(WIRE_BACKUP1) && !wires.is_cut(WIRE_BACKUP2))
if(!backup_power_timer || timeleft(backup_power_timer) != backup_power_time)
backup_power_timer = addtimer(CALLBACK(src, PROC_REF(regainBackupPower)), backup_power_time, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE|TIMER_DELETE_ME)
// Otherwise, we'll ensure the timer matches backup_power_time
else if(backup_power_timer)
backup_power_time = timeleft(backup_power_timer)
deltimer(backup_power_timer)
backup_power_timer = null
// Alright, we're gonna do a meme here
/obj/machinery/door/airlock/set_wires(datum/wires/new_wires)
if(wires)
UnregisterSignal(wires, list(
COMSIG_CUT_WIRE(WIRE_POWER1),
COMSIG_CUT_WIRE(WIRE_POWER2),
COMSIG_CUT_WIRE(WIRE_BACKUP1),
COMSIG_CUT_WIRE(WIRE_BACKUP2),
COMSIG_MEND_WIRE(WIRE_POWER1),
COMSIG_MEND_WIRE(WIRE_POWER2),
COMSIG_MEND_WIRE(WIRE_BACKUP1),
COMSIG_MEND_WIRE(WIRE_BACKUP2),
))
. = ..()
if(new_wires)
RegisterSignals(new_wires, list(
COMSIG_CUT_WIRE(WIRE_POWER1),
COMSIG_CUT_WIRE(WIRE_POWER2),
COMSIG_CUT_WIRE(WIRE_BACKUP1),
COMSIG_CUT_WIRE(WIRE_BACKUP2),
COMSIG_MEND_WIRE(WIRE_POWER1),
COMSIG_MEND_WIRE(WIRE_POWER2),
COMSIG_MEND_WIRE(WIRE_BACKUP1),
COMSIG_MEND_WIRE(WIRE_BACKUP2),
), PROC_REF(power_wires_changed))
/// If our power wires have changed, then our backup/main power regen may have failed, so let's just check in yeah?
/obj/machinery/door/airlock/proc/power_wires_changed(datum/source, wire)
SIGNAL_HANDLER
handle_main_power()
handle_backup_power()
/obj/machinery/door/airlock/proc/regainMainPower()
set_main_outage(0 SECONDS)
/obj/machinery/door/airlock/proc/loseMainPower()
if(!remaining_main_outage())
set_main_outage(60 SECONDS)
if(remaining_backup_outage() < 10 SECONDS)
set_backup_outage(10 SECONDS)
/obj/machinery/door/airlock/proc/loseBackupPower()
if(remaining_backup_outage() < 60 SECONDS)
set_backup_outage(60 SECONDS)
/obj/machinery/door/airlock/proc/regainBackupPower()
set_backup_outage(0 SECONDS)
// shock user with probability prb (if all connections & power are working)
// returns TRUE if shocked, FALSE otherwise
// The preceding comment was borrowed from the grille's shock script
/obj/machinery/door/airlock/proc/shock(mob/living/user, prb)
if(!istype(user) || !hasPower()) // unpowered, no shock
return FALSE
if(HAS_TRAIT(user, TRAIT_AIRLOCK_SHOCKIMMUNE)) // Be a bit more clever man come on
return FALSE
if(!COOLDOWN_FINISHED(src, shockCooldown))
return FALSE //Already shocked someone recently?
if(!prob(prb))
return FALSE //you lucked out, no shock for you
do_sparks(5, TRUE, src)
var/check_range = TRUE
if(electrocute_mob(user, get_area(src), src, 1, check_range))
COOLDOWN_START(src, shockCooldown, 1 SECONDS)
// Provides timed airlock shock immunity, to prevent overly cheesy deathtraps
ADD_TRAIT(user, TRAIT_AIRLOCK_SHOCKIMMUNE, REF(src))
addtimer(TRAIT_CALLBACK_REMOVE(user, TRAIT_AIRLOCK_SHOCKIMMUNE, REF(src)), 1 SECONDS)
return TRUE
else
return FALSE
/obj/machinery/door/airlock/proc/is_secure()
return (security_level > 0)
/// Checks if this door would be affected by any currently active RETA grants
/obj/machinery/door/airlock/proc/has_active_reta_access()
if(!CONFIG_GET(flag/reta_enabled))
return FALSE
if(!length(req_access) && !length(req_one_access))
return FALSE
// Check if this door belongs to a department providing access via RETA
for(var/target_dept in GLOB.reta_active_grants)
var/list/active_origins = GLOB.reta_active_grants[target_dept]
for(var/origin_dept in active_origins)
var/list/origin_dept_access = GLOB.reta_dept_grants[origin_dept]
if(!origin_dept_access)
continue
for(var/required_access in req_access)
if(required_access in origin_dept_access)
return TRUE
for(var/required_access in req_one_access)
if(required_access in origin_dept_access)
return TRUE
return FALSE
/**
* Set the airlock state to a new value, change the icon state
* and run the associated animation if required.
*/
/obj/machinery/door/airlock/proc/set_airlock_state(new_state, animated = FALSE, force_type = DEFAULT_DOOR_CHECKS)
if(!new_state)
new_state = density ? AIRLOCK_CLOSED : AIRLOCK_OPEN
airlock_state = new_state
if(animated)
operating = TRUE
run_animation(airlock_state, force_type)
return
operating = FALSE
set_animation()
/obj/machinery/door/airlock/update_icon(updates = ALL)
if(!airlock_state)
airlock_state = icon_state
return ..()
/obj/machinery/door/airlock/update_icon_state()
. = ..()
if(animation)
icon_state = "[base_icon_state][animation]"
else if(airlock_state == AIRLOCK_OPEN)
icon_state = "[base_icon_state]open"
else
icon_state = "[base_icon_state]closed"
/obj/machinery/door/airlock/update_overlays()
. = ..()
var/frame_state
var/light_state
if(machine_stat & MAINT) // in the process of being emagged
frame_state = AIRLOCK_FRAME_CLOSED
else switch(airlock_state)
if(AIRLOCK_CLOSED)
frame_state = AIRLOCK_FRAME_CLOSED
if(locked)
light_state = AIRLOCK_LIGHT_BOLTS
else if(emergency)
light_state = AIRLOCK_LIGHT_EMERGENCY
else if(has_active_reta_access())
light_state = AIRLOCK_LIGHT_RETA
if(AIRLOCK_DENY)
frame_state = AIRLOCK_FRAME_CLOSED
light_state = AIRLOCK_LIGHT_DENIED
if(AIRLOCK_CLOSING)
frame_state = AIRLOCK_FRAME_CLOSING
light_state = AIRLOCK_LIGHT_CLOSING
if(AIRLOCK_OPEN)
frame_state = AIRLOCK_FRAME_OPEN
if(AIRLOCK_OPENING)
frame_state = AIRLOCK_FRAME_OPENING
light_state = AIRLOCK_LIGHT_OPENING
. += get_airlock_overlay(frame_state, icon, src, em_block = TRUE)
if(airlock_material)
. += get_airlock_overlay("[airlock_material]_[frame_state]", overlays_file, src, em_block = TRUE)
else
. += get_airlock_overlay("fill_[frame_state]", icon, src, em_block = TRUE)
if(feedback && hasPower() && light_state)
. += get_airlock_overlay("lights_[light_state]", overlays_file, src, em_block = FALSE)
if(panel_open)
. += get_airlock_overlay("panel_[frame_state][security_level ? "_protected" : null]", overlays_file, src, em_block = TRUE)
if(frame_state == AIRLOCK_FRAME_CLOSED && welded)
. += get_airlock_overlay("welded", overlays_file, src, em_block = TRUE)
if(machine_stat & MAINT) // in the process of being emagged
. += get_airlock_overlay("sparks", overlays_file, src, em_block = FALSE)
if(hasPower())
if(frame_state == AIRLOCK_FRAME_CLOSED)
if(atom_integrity < integrity_failure * max_integrity)
. += get_airlock_overlay("sparks_broken", overlays_file, src, em_block = FALSE)
else if(atom_integrity < (0.75 * max_integrity))
. += get_airlock_overlay("sparks_damaged", overlays_file, src, em_block = FALSE)
else if(frame_state == AIRLOCK_FRAME_OPEN)
if(atom_integrity < (0.75 * max_integrity))
. += get_airlock_overlay("sparks_open", overlays_file, src, em_block = FALSE)
if(note)
. += get_airlock_overlay(get_note_state(frame_state), note_overlay_file, src, em_block = TRUE)
if(frame_state == AIRLOCK_FRAME_CLOSED && seal)
. += get_airlock_overlay("sealed", overlays_file, src, em_block = TRUE)
if(hasPower() && unres_sides)
for(var/heading in list(NORTH,SOUTH,EAST,WEST))
if(!(unres_sides & heading))
continue
var/mutable_appearance/floorlight = mutable_appearance('icons/obj/doors/airlocks/station/overlays.dmi', "unres_[heading]", FLOAT_LAYER, src, O_LIGHTING_VISUAL_PLANE, appearance_flags = RESET_COLOR | KEEP_APART)
floorlight.color = LIGHT_COLOR_DEFAULT
switch (heading)
if (NORTH)
floorlight.pixel_w = 0
floorlight.pixel_z = 32
if (SOUTH)
floorlight.pixel_w = 0
floorlight.pixel_z = -32
if (EAST)
floorlight.pixel_w = 32
floorlight.pixel_z = 0
if (WEST)
floorlight.pixel_w = -32
floorlight.pixel_z = 0
. += floorlight
/obj/machinery/door/airlock/run_animation(animation, force_type = DEFAULT_DOOR_CHECKS)
if(animation == DOOR_DENY_ANIMATION)
if(machine_stat)
return
set_airlock_state(AIRLOCK_DENY, animated = FALSE, force_type = force_type)
return ..()
/obj/machinery/door/airlock/animation_effects(animation, force_type = DEFAULT_DOOR_CHECKS)
if(force_type == BYPASS_DOOR_CHECKS)
playsound(src, soundin = 'sound/machines/airlock/airlockforced.ogg', vol = 30, vary = TRUE)
return
switch(animation)
if(DOOR_OPENING_ANIMATION)
use_energy(50 JOULES)
playsound(src, soundin = doorOpen, vol = 30, vary = TRUE)
if(DOOR_CLOSING_ANIMATION)
use_energy(50 JOULES)
playsound(src, soundin = doorClose, vol = 30, vary = TRUE)
if(DOOR_DENY_ANIMATION)
if(feedback)
playsound(src, soundin = doorDeni, vol = 50, vary = FALSE, extrarange = 3)
addtimer(CALLBACK(src, PROC_REF(handle_deny_end)), AIRLOCK_DENY_ANIMATION_TIME)
/obj/machinery/door/airlock/proc/handle_deny_end()
if(airlock_state == AIRLOCK_DENY)
set_airlock_state(AIRLOCK_CLOSED, animated = FALSE)
/obj/machinery/door/airlock/animation_length(animation)
switch(animation)
if(DOOR_OPENING_ANIMATION)
return 0.6 SECONDS
if(DOOR_CLOSING_ANIMATION)
return 0.6 SECONDS
/obj/machinery/door/airlock/animation_segment_delay(animation)
switch(animation)
if(AIRLOCK_OPENING_TRANSPARENT)
return 0.1 SECONDS
if(AIRLOCK_OPENING_PASSABLE)
return 0.5 SECONDS
if(AIRLOCK_OPENING_FINISHED)
return 0.6 SECONDS
if(AIRLOCK_CLOSING_UNPASSABLE)
return 0.2 SECONDS
if(AIRLOCK_CLOSING_OPAQUE)
return 0.5 SECONDS
if(AIRLOCK_CLOSING_FINISHED)
return 0.6 SECONDS
/obj/machinery/door/airlock/examine(mob/user)
. = ..()
if(closeOtherId)
. += span_warning("This airlock cycles on ID: [sanitize(closeOtherId)].")
else if(cyclelinkedairlock)
. += span_warning("This airlock cycles with: [cyclelinkedairlock.name].")
else
. += span_warning("This airlock does not cycle.")
if(obj_flags & EMAGGED)
. += span_warning("Its access panel is smoking slightly.")
if(note)
if(!in_range(user, src))
. += "There's a [note.name] pinned to the front. You can't read it from here."
else
. += "There's a [note.name] pinned to the front..."
. += note.examine(user)
. += span_notice("The attached [note.name] can be taken down with some [EXAMINE_HINT("wirecutters")].")
if(seal)
. += "It's been braced with \a [seal]."
if(welded)
. += "It's welded shut."
if(panel_open)
switch(security_level)
if(AIRLOCK_SECURITY_NONE)
. += "Its wires are exposed!"
if(AIRLOCK_SECURITY_IRON)
. += "Its wires are hidden behind a welded iron cover."
if(AIRLOCK_SECURITY_PLASTEEL_I_S)
. += "There is some shredded plasteel inside."
if(AIRLOCK_SECURITY_PLASTEEL_I)
. += "Its wires are behind an inner layer of plasteel."
if(AIRLOCK_SECURITY_PLASTEEL_O_S)
. += "There is some shredded plasteel inside."
if(AIRLOCK_SECURITY_PLASTEEL_O)
. += "There is a welded plasteel cover hiding its wires."
if(AIRLOCK_SECURITY_PLASTEEL)
. += "There is a protective grille over its panel."
else if(security_level)
if(security_level == AIRLOCK_SECURITY_IRON)
. += "It looks a bit stronger."
else
. += "It looks very robust."
if(issilicon(user) && !(machine_stat & BROKEN))
. += span_notice("Shift-click [src] to [ density ? "open" : "close"] it.")
. += span_notice("Ctrl-click [src] to [ locked ? "raise" : "drop"] its bolts.")
. += span_notice("Alt-click [src] to [ secondsElectrified ? "un-electrify" : "permanently electrify"] it.")
. += span_notice("Ctrl-Shift-click [src] to [ emergency ? "disable" : "enable"] emergency access.")
/obj/machinery/door/airlock/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(isAI(user) || iscyborg(user))
if(!(machine_stat & BROKEN))
var/ui = SStgui.try_update_ui(user, src)
if(!ui && !held_item)
context[SCREENTIP_CONTEXT_LMB] = "Open UI"
context[SCREENTIP_CONTEXT_SHIFT_LMB] = density ? "Open" : "Close"
context[SCREENTIP_CONTEXT_CTRL_LMB] = locked ? "Unbolt" : "Bolt"
context[SCREENTIP_CONTEXT_ALT_LMB] = isElectrified() ? "Unelectrify" : "Electrify"
context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB] = emergency ? "Unset emergency access" : "Set emergency access"
. = CONTEXTUAL_SCREENTIP_SET
if(!isliving(user))
return .
if(!Adjacent(user))
return .
switch (held_item?.tool_behaviour)
if (TOOL_SCREWDRIVER)
if(has_access_panel)
context[SCREENTIP_CONTEXT_LMB] = panel_open ? "Close panel" : "Open panel"
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_CROWBAR)
if (panel_open)
if (security_level == AIRLOCK_SECURITY_PLASTEEL_O_S || security_level == AIRLOCK_SECURITY_PLASTEEL_I_S)
context[SCREENTIP_CONTEXT_LMB] = "Remove shielding"
return CONTEXTUAL_SCREENTIP_SET
else if (should_try_removing_electronics())
context[SCREENTIP_CONTEXT_LMB] = "Remove electronics"
return CONTEXTUAL_SCREENTIP_SET
// Not always contextually true, but is contextually false in ways that make gameplay interesting.
// For example, trying to pry open an airlock, only for the bolts to be down and the lights off.
context[SCREENTIP_CONTEXT_LMB] = "Pry open"
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_WELDER)
context[SCREENTIP_CONTEXT_RMB] = "Weld shut"
if (panel_open)
switch (security_level)
if (AIRLOCK_SECURITY_IRON, AIRLOCK_SECURITY_PLASTEEL_I, AIRLOCK_SECURITY_PLASTEEL_O)
context[SCREENTIP_CONTEXT_LMB] = "Cut shielding"
return CONTEXTUAL_SCREENTIP_SET
context[SCREENTIP_CONTEXT_LMB] = "Repair"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/wrench/bolter))
if(locked)
context[SCREENTIP_CONTEXT_LMB] = "Raise bolts"
return CONTEXTUAL_SCREENTIP_SET
return CONTEXTUAL_SCREENTIP_SET
return .
/obj/machinery/door/airlock/attack_ai(mob/user)
if(!canAIControl(user))
if(canAIHack())
hack(user)
return
else
to_chat(user, span_warning("Airlock AI control has been blocked with a firewall. Unable to hack."))
if(obj_flags & EMAGGED)
to_chat(user, span_warning("Unable to interface: Airlock is unresponsive."))
return
ui_interact(user)
///Performs basic checks to make sure we are still able to hack an airlock. If control is restored early through outside means, opens the airlock's control interface.
/obj/machinery/door/airlock/proc/check_hacking(mob/user, success_message)
if(QDELETED(src))
to_chat(user, span_warning("Connection lost! Unable to locate airlock on network."))
aiHacking = FALSE
return FALSE
if(canAIControl(user))
to_chat(user, span_notice("Alert cancelled. Airlock control has been restored without our assistance."))
aiHacking = FALSE
if(user)
attack_ai(user) //bring up airlock dialog
return
else if(!canAIHack())
to_chat(user, span_warning("Connection lost! Unable to hack airlock."))
aiHacking = FALSE
return
if(success_message)
to_chat(user, span_notice(success_message))
return TRUE
///Attemps to override airlocks that have the AI control wire disabled.
/obj/machinery/door/airlock/proc/hack(mob/user)
set waitfor = 0
if(!aiHacking)
aiHacking = TRUE
to_chat(user, span_warning("Airlock AI control has been blocked. Beginning fault-detection."))
sleep(5 SECONDS)
if(!check_hacking(user, "Fault confirmed: airlock control wire disabled or cut."))
return
sleep(2 SECONDS)
if(!check_hacking(user, "Attempting to hack into airlock. This may take some time."))
return
sleep(20 SECONDS)
if(!check_hacking(user, "Upload access confirmed. Loading control program into airlock software."))
return
sleep(17 SECONDS)
if(!check_hacking(user,"Transfer complete. Forcing airlock to execute program."))
return
sleep(5 SECONDS)
if(!check_hacking(user, "Receiving control information from airlock."))
return
aiControlDisabled = AI_WIRE_HACKED //disable blocked control
sleep(1 SECONDS)
aiHacking = FALSE
if(QDELETED(src))
to_chat(user, span_warning("Connection lost! Unable to locate airlock on network."))
return
if(user)
attack_ai(user) //bring up airlock dialog
/obj/machinery/door/airlock/attack_animal(mob/user, list/modifiers)
if(isElectrified() && shock(user, 100))
return
return ..()
/obj/machinery/door/airlock/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
/obj/machinery/door/airlock/attack_hand(mob/user, list/modifiers)
. = ..()
if(.)
return
if(!HAS_SILICON_ACCESS(user))
if(isElectrified() && shock(user, 100))
return
if(ishuman(user) && prob(40) && density)
var/mob/living/carbon/human/H = user
if((HAS_TRAIT(H, TRAIT_DUMB)) && Adjacent(user))
playsound(src, 'sound/effects/bang.ogg', 25, TRUE)
if(!istype(H.head, /obj/item/clothing/head/helmet))
H.visible_message(span_danger("[user] headbutts the airlock."), \
span_userdanger("You headbutt the airlock!"))
H.Paralyze(100)
H.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
else
visible_message(span_danger("[user] headbutts the airlock. Good thing [user.p_theyre()] wearing a helmet."))
/obj/machinery/door/airlock/attempt_wire_interaction(mob/user)
if(security_level)
to_chat(user, span_warning("Wires are protected!"))
return WIRE_INTERACTION_FAIL
return ..()
/obj/machinery/door/airlock/proc/electrified_loop()
while (secondsElectrified > MACHINE_NOT_ELECTRIFIED)
sleep(1 SECONDS)
if(QDELETED(src))
return
if(secondsElectrified <= MACHINE_NOT_ELECTRIFIED) //make sure they weren't unelectrified during the sleep.
break
secondsElectrified = max(MACHINE_NOT_ELECTRIFIED, secondsElectrified - 1) //safety to make sure we don't end up permanently electrified during a timed electrification.
// This is to protect against changing to permanent, mid loop.
if(secondsElectrified == MACHINE_NOT_ELECTRIFIED)
set_electrified(MACHINE_NOT_ELECTRIFIED)
else
set_electrified(MACHINE_ELECTRIFIED_PERMANENT)
/obj/machinery/door/airlock/screwdriver_act(mob/living/user, obj/item/tool)
if(!has_access_panel)
to_chat(user, span_warning("[src] has no maintenance panel!"))
return ITEM_INTERACT_SUCCESS
toggle_panel_open()
to_chat(user, span_notice("You [panel_open ? "open":"close"] the maintenance panel of the airlock."))
tool.play_tool_sound(src)
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/machinery/door/airlock/wirecutter_act(mob/living/user, obj/item/tool)
if(panel_open && security_level == AIRLOCK_SECURITY_PLASTEEL)
. = ITEM_INTERACT_SUCCESS // everything after this shouldn't result in attackby
if(hasPower() && shock(user, 60)) // Protective grille of wiring is electrified
return .
to_chat(user, span_notice("You start cutting through the outer grille."))
if(!tool.use_tool(src, user, 10, volume=100))
return .
if(!panel_open) // double check it wasn't closed while we were trying to snip
return .
user.visible_message(span_notice("[user] cut through [src]'s outer grille."),
span_notice("You cut through [src]'s outer grille."))
security_level = AIRLOCK_SECURITY_PLASTEEL_O
return .
if(note)
if(user.CanReach(src))
user.visible_message(span_notice("[user] cuts down [note] from [src]."), span_notice("You remove [note] from [src]."))
else //telekinesis
visible_message(span_notice("[tool] cuts down [note] from [src]."))
tool.play_tool_sound(src)
note.forceMove(tool.drop_location())
note = null
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/machinery/door/airlock/crowbar_act(mob/living/user, obj/item/tool)
if(!panel_open || security_level == AIRLOCK_SECURITY_NONE)
return ..()
var/layer_flavor
var/next_level
var/starting_level = security_level
switch(security_level)
if(AIRLOCK_SECURITY_PLASTEEL_O_S)
layer_flavor = "outer layer of shielding"
next_level = AIRLOCK_SECURITY_PLASTEEL_I
if(AIRLOCK_SECURITY_PLASTEEL_I_S)
layer_flavor = "inner layer of shielding"
next_level = AIRLOCK_SECURITY_NONE
else
return ITEM_INTERACT_SUCCESS
user.visible_message(span_notice("You start prying away [src]'s [layer_flavor]."))
if(!tool.use_tool(src, user, 40, volume=100))
return ITEM_INTERACT_SUCCESS
if(!panel_open || security_level != starting_level)
// if the plating's already been broken, don't break it again
return ITEM_INTERACT_SUCCESS
user.visible_message(span_notice("[user] removes [src]'s shielding."),
span_notice("You remove [src]'s [layer_flavor]."))
security_level = next_level
spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
if(next_level == AIRLOCK_SECURITY_NONE)
modify_max_integrity(max_integrity / AIRLOCK_INTEGRITY_MULTIPLIER)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/machinery/door/airlock/wrench_act(mob/living/user, obj/item/tool)
if(!locked)
return
if(!panel_open)
balloon_alert(user, "panel is closed!")
return
if(security_level != AIRLOCK_SECURITY_NONE)
balloon_alert(user, "airlock is reinforced!")
return
if(istype(tool, /obj/item/wrench/bolter))
balloon_alert(user, "raising bolts...")
if(!do_after(user, 5 SECONDS, src))
return
unbolt()
return ITEM_INTERACT_SUCCESS
/obj/machinery/door/airlock/welder_act(mob/living/user, obj/item/tool)
if(!panel_open || security_level == AIRLOCK_SECURITY_NONE)
return ..()
var/layer_flavor
var/next_level
var/starting_level = security_level
var/material_to_spawn
var/amount_to_spawn
switch(security_level)
if(AIRLOCK_SECURITY_IRON)
layer_flavor = "panel's shielding"
next_level = AIRLOCK_SECURITY_NONE
material_to_spawn = /obj/item/stack/sheet/iron
amount_to_spawn = 2
if(AIRLOCK_SECURITY_PLASTEEL_O)
layer_flavor = "outer layer of shielding"
next_level = AIRLOCK_SECURITY_PLASTEEL_O_S
if(AIRLOCK_SECURITY_PLASTEEL_I)
layer_flavor = "inner layer of shielding"
next_level = AIRLOCK_SECURITY_PLASTEEL_I_S
else
return ITEM_INTERACT_SUCCESS
if(!tool.tool_start_check(user, amount=1))
return ITEM_INTERACT_SUCCESS
to_chat(user, span_notice("You begin cutting the [layer_flavor]..."))
if(!tool.use_tool(src, user, 4 SECONDS, volume=50))
return ITEM_INTERACT_SUCCESS
if(!panel_open || security_level != starting_level)
// see if anyone's screwing with us
return ITEM_INTERACT_SUCCESS
user.visible_message(
span_notice("[user] cuts through [src]'s shielding."), // passers-by don't get the full picture
span_notice("You cut through [src]'s [layer_flavor]."),
span_hear("You hear welding.")
)
security_level = next_level
if(material_to_spawn)
spawn_atom_to_turf(material_to_spawn, user.loc, amount_to_spawn)
if(security_level == AIRLOCK_SECURITY_NONE)
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/machinery/door/airlock/proc/try_reinforce(mob/user, obj/item/stack/sheet/material, amt_required, new_security_level)
if(material.get_amount() < amt_required)
to_chat(user, span_warning("You need at least [amt_required] sheets of [material] to reinforce [src]."))
return FALSE
to_chat(user, span_notice("You start reinforcing [src]."))
if(!do_after(user, 2 SECONDS, src))
return FALSE
if(!panel_open || !material.use(amt_required))
return FALSE
user.visible_message(span_notice("[user] reinforces [src] with [material]."),
span_notice("You reinforce [src] with [material]."))
security_level = new_security_level
update_appearance()
return TRUE
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, list/modifiers, list/attack_modifiers)
if(!HAS_SILICON_ACCESS(user))
if(isElectrified() && (C.obj_flags & CONDUCTS_ELECTRICITY) && shock(user, 75))
return
add_fingerprint(user)
if(is_wire_tool(C) && panel_open)
attempt_wire_interaction(user)
return
else if(panel_open && security_level == AIRLOCK_SECURITY_NONE && istype(C, /obj/item/stack/sheet))
if(istype(C, /obj/item/stack/sheet/iron))
return try_reinforce(user, C, 2, AIRLOCK_SECURITY_IRON)
else if(istype(C, /obj/item/stack/sheet/plasteel))
if(!try_reinforce(user, C, 2, AIRLOCK_SECURITY_PLASTEEL))
return FALSE
modify_max_integrity(max_integrity * AIRLOCK_INTEGRITY_MULTIPLIER)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
update_appearance()
return TRUE
else if(istype(C, /obj/item/pai_cable))
var/obj/item/pai_cable/cable = C
cable.plugin(src, user)
else if(istype(C, /obj/item/airlock_painter))
change_paintjob(C, user)
else if(istype(C, /obj/item/door_seal)) //adding the seal
var/obj/item/door_seal/airlockseal = C
if(!density)
to_chat(user, span_warning("[src] must be closed before you can seal it!"))
return
if(seal)
to_chat(user, span_warning("[src] has already been sealed!"))
return
user.visible_message(span_notice("[user] begins sealing [src]."), span_notice("You begin sealing [src]."))
playsound(src, 'sound/items/tools/jaws_pry.ogg', 30, TRUE)
if(!do_after(user, airlockseal.seal_time, target = src))
return
if(!density)
to_chat(user, span_warning("[src] must be closed before you can seal it!"))
return
if(seal)
to_chat(user, span_warning("[src] has already been sealed!"))
return
if(!user.transferItemToLoc(airlockseal, src))
to_chat(user, span_warning("For some reason, you can't attach [airlockseal]!"))
return
playsound(src, 'sound/machines/airlock/airlockforced.ogg', 30, TRUE)
user.visible_message(span_notice("[user] finishes sealing [src]."), span_notice("You finish sealing [src]."))
seal = airlockseal
modify_max_integrity(max_integrity * AIRLOCK_SEAL_MULTIPLIER)
update_appearance()
else if(istype(C, /obj/item/paper) || istype(C, /obj/item/photo))
if(note)
to_chat(user, span_warning("There's already something pinned to this airlock! Use wirecutters to remove it."))
return
if(!user.transferItemToLoc(C, src))
to_chat(user, span_warning("For some reason, you can't attach [C]!"))
return
user.visible_message(span_notice("[user] pins [C] to [src]."), span_notice("You pin [C] to [src]."))
note = C
update_appearance()
else
return ..()
/obj/machinery/door/airlock/try_to_weld(obj/item/weldingtool/W, mob/living/user)
if(!operating && density)
if(seal)
to_chat(user, span_warning("[src] is blocked by a seal!"))
return
if(atom_integrity < max_integrity)
if(!W.tool_start_check(user, amount=1))
return
user.visible_message(span_notice("[user] begins welding the airlock."), \
span_notice("You begin repairing the airlock..."), \
span_hear("You hear welding."))
if(W.use_tool(src, user, 40, volume=50, extra_checks = CALLBACK(src, PROC_REF(weld_checks), W, user)))
atom_integrity = max_integrity
set_machine_stat(machine_stat & ~BROKEN)
user.visible_message(span_notice("[user] finishes welding [src]."), \
span_notice("You finish repairing the airlock."))
update_appearance()
else
to_chat(user, span_notice("The airlock doesn't need repairing."))
/obj/machinery/door/airlock/try_to_weld_secondary(obj/item/weldingtool/tool, mob/user)
if(!tool.tool_start_check(user, amount=1))
return
user.visible_message(span_notice("[user] begins [welded ? "unwelding":"welding"] the airlock."), \
span_notice("You begin [welded ? "unwelding":"welding"] the airlock..."), \
span_hear("You hear welding."))
if(!tool.use_tool(src, user, 40, volume=50, extra_checks = CALLBACK(src, PROC_REF(weld_checks), tool, user)))
return
welded = !welded
user.visible_message(span_notice("[user] [welded? "welds shut":"unwelds"] [src]."), \
span_notice("You [welded ? "weld the airlock shut":"unweld the airlock"]."))
user.log_message("[welded ? "welded":"unwelded"] airlock [src] with [tool].", LOG_GAME)
update_appearance()
/obj/machinery/door/airlock/proc/weld_checks(obj/item/weldingtool/W, mob/user)
return !operating && density
/**
* Used when attempting to remove a seal from an airlock
*
* Called when attacking an airlock with an empty hand, returns TRUE (there was a seal and we removed it, or failed to remove it)
* or FALSE (there was no seal)
* Arguments:
* * user - Whoever is attempting to remove the seal
*/
/obj/machinery/door/airlock/try_remove_seal(mob/living/user)
if(!seal)
return FALSE
var/obj/item/door_seal/airlockseal = seal
if(!ishuman(user))
to_chat(user, span_warning("You don't have the dexterity to remove the seal!"))
return TRUE
user.visible_message(span_notice("[user] begins removing the seal from [src]."), span_notice("You begin removing [src]'s pneumatic seal."))
playsound(src, 'sound/machines/airlock/airlockforced.ogg', 30, TRUE)
if(!do_after(user, airlockseal.unseal_time, target = src))
return TRUE
if(!seal)
return TRUE
playsound(src, 'sound/items/tools/jaws_pry.ogg', 30, TRUE)
airlockseal.forceMove(get_turf(user))
user.visible_message(span_notice("[user] finishes removing the seal from [src]."), span_notice("You finish removing [src]'s pneumatic seal."))
seal = null
modify_max_integrity(max_integrity / AIRLOCK_SEAL_MULTIPLIER)
update_appearance()
return TRUE
/// Returns if a crowbar would remove the airlock electronics
/obj/machinery/door/airlock/proc/should_try_removing_electronics()
if (security_level != 0)
return FALSE
if (!panel_open)
return FALSE
if (obj_flags & EMAGGED)
return TRUE
if (!density)
return FALSE
if (!welded)
return FALSE
if (hasPower())
return FALSE
if (locked)
return FALSE
return TRUE
/obj/machinery/door/airlock/try_to_crowbar(obj/item/tool, mob/living/user, forced = FALSE)
if(!isnull(tool) && tool.tool_behaviour == TOOL_CROWBAR && should_try_removing_electronics() && !operating)
user.visible_message(span_notice("[user] removes the electronics from the airlock assembly."), \
span_notice("You start to remove electronics from the airlock assembly..."))
if(tool.use_tool(src, user, 40, volume = 100))
deconstruct(TRUE, user)
return
if(seal)
to_chat(user, span_warning("Remove the seal first!"))
return
if(locked)
to_chat(user, span_warning("The airlock's bolts prevent it from being forced!"))
return
if(welded)
to_chat(user, span_warning("It's welded, it won't budge!"))
return
if(!hasPower())
if(operating)
return
if(istype(tool, /obj/item/fireaxe) && !HAS_TRAIT(tool, TRAIT_WIELDED)) //being fireaxe'd
to_chat(user, span_warning("You need to be wielding [tool] to do that!"))
return
INVOKE_ASYNC(src, density ? PROC_REF(open) : PROC_REF(close), BYPASS_DOOR_CHECKS)
return
if(!forced)
to_chat(user, span_warning("The airlock's motors resist your efforts to force it!"))
return
var/check_electrified = isElectrified() //setting this so we can check if the mob got shocked during the do_after below
if(check_electrified && shock(user,100))
return //it's like sticking a fork in a power socket
if(!density)//already open
return
if(prying_so_hard)
return
var/time_to_open = 5 SECONDS
playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE) //is it aliens or just the CE being a dick?
prying_so_hard = TRUE
if(!tool.use_tool(src, user, time_to_open, volume = 100))
prying_so_hard = FALSE
return
prying_so_hard = FALSE
if(check_electrified && shock(user, 100))
return
open(BYPASS_DOOR_CHECKS)
take_damage(AIRLOCK_PRY_DAMAGE, BRUTE, 0, 0) // Enough to sometimes spark
if(density && !open(BYPASS_DOOR_CHECKS))
to_chat(user, span_warning("Despite your attempts, [src] refuses to open."))
/obj/machinery/door/airlock/open(forced = DEFAULT_DOOR_CHECKS)
if(cycle_pump && !operating && !welded && !seal && locked && density)
cycle_pump.airlock_act(src)
return FALSE // The rest will be handled by the pump
if( operating || welded || locked || seal )
return FALSE
if(!density)
return TRUE
// Since we aren't physically held shut, do extra checks to see if we should open.
if(!try_to_force_door_open(forced))
return FALSE
if(autoclose)
autoclose_in(normalspeed ? 8 SECONDS : 1.5 SECONDS)
if(closeOther != null && istype(closeOther, /obj/machinery/door/airlock))
addtimer(CALLBACK(closeOther, PROC_REF(close)), BYPASS_DOOR_CHECKS)
if(close_others)
for(var/obj/machinery/door/airlock/otherlock as anything in close_others)
if(!shuttledocked && !emergency && !otherlock.shuttledocked && !otherlock.emergency)
if(otherlock.operating)
otherlock.delayed_close_requested = TRUE
else
addtimer(CALLBACK(otherlock, PROC_REF(close)), BYPASS_DOOR_CHECKS)
if(cyclelinkedairlock)
if(!shuttledocked && !emergency && !cyclelinkedairlock.shuttledocked && !cyclelinkedairlock.emergency)
if(cyclelinkedairlock.operating)
cyclelinkedairlock.delayed_close_requested = TRUE
else
addtimer(CALLBACK(cyclelinkedairlock, PROC_REF(close)), BYPASS_DOOR_CHECKS)
SEND_SIGNAL(src, COMSIG_AIRLOCK_OPEN, forced)
set_airlock_state(AIRLOCK_OPENING, animated = TRUE, force_type = forced)
var/transparent_delay = animation_segment_delay(AIRLOCK_OPENING_TRANSPARENT)
sleep(transparent_delay)
set_opacity(0)
if(multi_tile)
filler.set_opacity(FALSE)
update_freelook_sight()
var/passable_delay = animation_segment_delay(AIRLOCK_OPENING_PASSABLE) - transparent_delay
sleep(passable_delay)
set_density(FALSE)
if(multi_tile)
filler.set_density(FALSE)
flags_1 &= ~PREVENT_CLICK_UNDER_1
air_update_turf(TRUE, FALSE)
var/open_delay = animation_segment_delay(AIRLOCK_OPENING_FINISHED) - transparent_delay - passable_delay
sleep(open_delay)
layer = OPEN_DOOR_LAYER
set_airlock_state(AIRLOCK_OPEN, animated = FALSE)
if(delayed_close_requested)
delayed_close_requested = FALSE
addtimer(CALLBACK(src, PROC_REF(close)), FORCING_DOOR_CHECKS)
return TRUE
/// Additional checks depending on what we want to happen to door (should we try and open it normally, or do we want this open at all costs?)
/obj/machinery/door/airlock/try_to_force_door_open(force_type = DEFAULT_DOOR_CHECKS)
switch(force_type)
if(DEFAULT_DOOR_CHECKS) // Regular behavior.
if(!hasPower() || wires.is_cut(WIRE_OPEN) || (obj_flags & EMAGGED))
return FALSE
return TRUE
if(FORCING_DOOR_CHECKS) // Only one check.
if(obj_flags & EMAGGED)
return FALSE
return TRUE
if(BYPASS_DOOR_CHECKS) // No power usage, special sound, get it open.
return TRUE
else
stack_trace("Invalid forced argument '[force_type]' passed to open() on this airlock.")
// If we got here, shit's fucked, hope parent can help us out here
return ..()
/obj/machinery/door/airlock/close(forced = DEFAULT_DOOR_CHECKS, force_crush = FALSE)
if(operating || welded || locked || seal)
return FALSE
if(density)
return TRUE
if(forced == DEFAULT_DOOR_CHECKS) // Do this up here and outside of try_to_force_door_shut because if we don't have power, we shouldn't be doing any dangerous_close stuff.
if(!hasPower() || wires.is_cut(WIRE_BOLTS))
return FALSE
var/dangerous_close = !safe || force_crush
if(!dangerous_close)
for(var/turf/checked_turf in locs)
for(var/atom/movable/blocking in checked_turf)
if(blocking.density && blocking != src)
autoclose_in(DOOR_CLOSE_WAIT)
return FALSE
if(!try_to_force_door_shut(forced))
return FALSE
var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src))
if(killthis)
SSexplosions.med_mov_atom += killthis
SEND_SIGNAL(src, COMSIG_AIRLOCK_CLOSE, forced)
set_airlock_state(AIRLOCK_CLOSING, animated = TRUE, force_type = forced)
layer = CLOSED_DOOR_LAYER
if(air_tight)
set_density(TRUE)
if(multi_tile)
filler.set_density(TRUE)
flags_1 |= PREVENT_CLICK_UNDER_1
air_update_turf(TRUE, TRUE)
var/unpassable_delay = animation_segment_delay(AIRLOCK_CLOSING_UNPASSABLE)
sleep(unpassable_delay)
if(!air_tight)
set_density(TRUE)
if(multi_tile)
filler.set_density(TRUE)
flags_1 |= PREVENT_CLICK_UNDER_1
air_update_turf(TRUE, TRUE)
var/opaque_delay = animation_segment_delay(AIRLOCK_CLOSING_OPAQUE) - unpassable_delay
sleep(opaque_delay)
if(dangerous_close)
crush()
if(visible && !glass)
set_opacity(TRUE)
if(multi_tile)
filler.set_opacity(TRUE)
update_freelook_sight()
var/close_delay = animation_segment_delay(AIRLOCK_CLOSING_FINISHED) - unpassable_delay - opaque_delay
sleep(close_delay)
set_airlock_state(AIRLOCK_CLOSED, animated = FALSE)
delayed_close_requested = FALSE
if(!dangerous_close)
CheckForMobs()
return TRUE
/obj/machinery/door/airlock/try_to_force_door_shut(force_type = DEFAULT_DOOR_CHECKS)
switch(force_type)
if(DEFAULT_DOOR_CHECKS to FORCING_DOOR_CHECKS)
if(obj_flags & EMAGGED)
return FALSE
return TRUE
if(BYPASS_DOOR_CHECKS)
return TRUE
else
stack_trace("Invalid forced argument '[force_type]' passed to close() on this airlock.")
// shit's fucked, let's hope parent has something to handle it.
return ..()
/obj/machinery/door/airlock/proc/prison_open()
if(obj_flags & EMAGGED)
return
if(locked)
unbolt()
open()
bolt()
return
// gets called when a player uses an airlock painter on this airlock
/obj/machinery/door/airlock/proc/change_paintjob(obj/item/airlock_painter/painter, mob/user)
if((!in_range(src, user) && loc != user) || !painter.can_use(user)) // user should be adjacent to the airlock, and the painter should have a toner cartridge that isn't empty
return
// reads from the airlock painter's `available paintjob` list. lets the player choose a paint option, or cancel painting
var/current_paintjob = tgui_input_list(user, "Paintjob for this airlock", "Customize", sort_list(painter.available_paint_jobs))
if(isnull(current_paintjob)) // if the user clicked cancel on the popup, return
return
var/airlock_type = painter.available_paint_jobs["[current_paintjob]"] // get the airlock type path associated with the airlock name the user just chose
var/obj/machinery/door/airlock/airlock = airlock_type // we need to create a new instance of the airlock and assembly to read vars from them
var/obj/structure/door_assembly/assembly = initial(airlock.assemblytype)
if(airlock_material == "glass" && initial(assembly.noglass)) // prevents painting glass airlocks with a paint job that doesn't have a glass version, such as the freezer
to_chat(user, span_warning("This paint job can only be applied to non-glass airlocks."))
return
// applies the user-chosen airlock's icon, overlays and assemblytype to the src airlock
painter.use_paint(user)
if(initial(airlock.greyscale_config))
greyscale_config = initial(airlock.greyscale_config)
greyscale_colors = initial(airlock.greyscale_colors)
update_greyscale()
else
icon = initial(airlock.icon)
overlays_file = initial(airlock.overlays_file)
assemblytype = initial(airlock.assemblytype)
update_appearance()
/obj/machinery/door/airlock/CanAStarPass(to_dir, datum/can_pass_info/pass_info)
//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off)
return !density || (check_access_list(pass_info.access) && !locked && hasPower() && !pass_info.no_id)
/obj/machinery/door/airlock/emag_act(mob/user, obj/item/card/emag/emag_card)
if(!operating && density && hasPower() && !(obj_flags & EMAGGED))
if(istype(emag_card, /obj/item/card/emag/doorjack))
var/obj/item/card/emag/doorjack/doorjack_card = emag_card
doorjack_card.use_charge(user)
set_machine_stat(machine_stat | MAINT) // flash the airlock lights and display some sparks
set_airlock_state(AIRLOCK_CLOSED)
operating = TRUE
addtimer(CALLBACK(src, PROC_REF(finish_emag_act)), 0.6 SECONDS)
return TRUE
return FALSE
/// Timer proc, called ~0.6 seconds after [emag_act]. Finishes the emag sequence by breaking the airlock, permanently locking it, and disabling power.
/obj/machinery/door/airlock/proc/finish_emag_act()
if(QDELETED(src))
return FALSE
set_machine_stat(machine_stat & ~MAINT)
operating = FALSE
if(!open())
set_airlock_state(AIRLOCK_CLOSED)
obj_flags |= EMAGGED
feedback = FALSE
locked = TRUE
loseMainPower()
loseBackupPower()
/obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
if(isElectrified() && shock(user, 100)) //Mmm, fried xeno!
add_fingerprint(user)
return
if(!density) //Already open
return ..()
if(user.combat_mode)
return ..()
if(locked || welded || seal) //Extremely generic, as aliens only understand the basics of how airlocks work.
to_chat(user, span_warning("[src] refuses to budge!"))
user.log_message("Tried to pry open [src], located at [loc_name(src)], but failed due to the airlock being sealed.", LOG_GAME)
return
add_fingerprint(user)
user.visible_message(span_warning("[user] begins prying open [src]."),\
span_noticealien("You begin digging your claws into [src] with all your might!"),\
span_warning("You hear groaning metal..."))
user.log_message("Started prying open [src], located at [loc_name(src)].", LOG_GAME)
var/time_to_open = 5 //half a second
if(hasPower())
time_to_open = 5 SECONDS //Powered airlocks take longer to open, and are loud.
playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE)
if(do_after(user, time_to_open, src))
if(density && !open(BYPASS_DOOR_CHECKS)) //The airlock is still closed, but something prevented it opening. (Another player noticed and bolted/welded the airlock in time!)
to_chat(user, span_warning("Despite your efforts, [src] managed to resist your attempts to open it!"))
user.log_message("Tried and failed to pry open [src], located at [loc_name(src)], due to the airlock getting sealed during the do_after.", LOG_GAME)
return
user.log_message("Successfully pried open [src], located at [loc_name(src)].", LOG_GAME)
return
user.log_message("Tried and failed to pry open [src], located at [loc_name(src)], due to getting interrupted.", LOG_GAME)
/obj/machinery/door/airlock/hostile_lockdown(mob/origin)
// Must be powered and have working AI wire.
if(canAIControl(origin) && !machine_stat)
locked = FALSE //For airlocks that were bolted open.
safe = FALSE //DOOR CRUSH
close()
bolt() //Bolt it!
set_electrified(MACHINE_ELECTRIFIED_PERMANENT) //Shock it!
if(origin)
LAZYADD(shockedby, "\[[time_stamp()]\] [key_name(origin)]")
/obj/machinery/door/airlock/disable_lockdown()
// Must be powered and have working AI wire.
if(canAIControl() && !machine_stat)
unbolt()
set_electrified(MACHINE_NOT_ELECTRIFIED)
open()
safe = TRUE
/obj/machinery/door/airlock/proc/on_break()
SIGNAL_HANDLER
set_panel_open(TRUE)
wires.cut_all()
/obj/machinery/door/airlock/emp_act(severity)
. = ..()
if (. & EMP_PROTECT_SELF)
return
if(prob(severity*10 - 20) && (secondsElectrified < 30) && (secondsElectrified != MACHINE_ELECTRIFIED_PERMANENT))
set_electrified(30)
LAZYADD(shockedby, "\[[time_stamp()]\]EM Pulse")
/obj/machinery/door/airlock/proc/set_electrified(seconds, mob/user)
secondsElectrified = seconds
diag_hud_set_electrified()
if(secondsElectrified > MACHINE_NOT_ELECTRIFIED)
INVOKE_ASYNC(src, PROC_REF(electrified_loop))
if(user)
var/message
switch(secondsElectrified)
if(MACHINE_ELECTRIFIED_PERMANENT)
message = "permanently shocked"
if(MACHINE_NOT_ELECTRIFIED)
message = "unshocked"
else
message = "temp shocked for [secondsElectrified] seconds"
LAZYADD(shockedby, "\[[time_stamp()]\] [key_name(user)] - ([uppertext(message)])")
log_combat(user, src, message)
add_hiddenprint(user)
/obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
if((damage_amount >= atom_integrity) && (damage_flag == BOMB))
obj_flags |= NO_DEBRIS_AFTER_DECONSTRUCTION //If an explosive took us out, don't drop the assembly
. = ..()
if(atom_integrity < (0.75 * max_integrity))
update_appearance()
/obj/machinery/door/airlock/proc/prepare_deconstruction_assembly(obj/structure/door_assembly/assembly)
assembly.heat_proof_finished = heat_proof //tracks whether there's rglass in
assembly.set_anchored(TRUE)
assembly.glass = glass
assembly.state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
assembly.created_name = name
assembly.previous_assembly = previous_airlock
assembly.update_name()
assembly.update_appearance()
assembly.dir = dir
/obj/machinery/door/airlock/on_deconstruction(disassembled)
var/obj/structure/door_assembly/A
if(assemblytype)
A = new assemblytype(loc)
else
A = new /obj/structure/door_assembly(loc)
//If you come across a null assemblytype, it will produce the default assembly instead of disintegrating.
prepare_deconstruction_assembly(A)
if(!disassembled)
A?.update_integrity(A.max_integrity * 0.5)
else if(!(obj_flags & EMAGGED))
var/obj/item/electronics/airlock/ae
if(!electronics)
ae = new/obj/item/electronics/airlock(loc)
if(closeOtherId)
ae.passed_cycle_id = closeOtherId
if(length(req_one_access))
ae.one_access = 1
ae.accesses = req_one_access
else if(length(req_access))
ae.accesses = req_access
else
ae = electronics
electronics = null
ae.forceMove(drop_location())
/obj/machinery/door/airlock/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
if(RCD_DECONSTRUCT)
if(seal)
to_chat(user, span_notice("[src]'s seal needs to be removed first."))
return FALSE
if(security_level != AIRLOCK_SECURITY_NONE)
to_chat(user, span_notice("[src]'s reinforcement needs to be removed first."))
return FALSE
return list("delay" = 5 SECONDS, "cost" = 32)
return FALSE
/obj/machinery/door/airlock/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
switch(rcd_data["[RCD_DESIGN_MODE]"])
if(RCD_DECONSTRUCT)
qdel(src)
return TRUE
return FALSE
/**
* Returns a string representing the type of note pinned to this airlock
* Arguments:
* * frame_state - The AIRLOCK_FRAME_ value, as used in update_overlays()
**/
/obj/machinery/door/airlock/proc/get_note_state(frame_state)
if(!note)
return
else if(istype(note, /obj/item/paper))
var/obj/item/paper/pinned_paper = note
if(pinned_paper.get_total_length() && pinned_paper.show_written_words)
return "note_words_[frame_state]"
else
return "note_[frame_state]"
else if(istype(note, /obj/item/photo))
return "photo_[frame_state]"
/obj/machinery/door/airlock/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AiAirlock", name)
ui.open()
return TRUE
/obj/machinery/door/airlock/ui_data()
var/list/data = list()
var/list/power = list()
power["main"] = remaining_main_outage() ? 0 : 2 // boolean
power["main_timeleft"] = round(remaining_main_outage() / 10)
power["backup"] = remaining_backup_outage() ? 0 : 2 // boolean
power["backup_timeleft"] = round(remaining_backup_outage() / 10)
data["power"] = power
data["shock"] = secondsElectrified == MACHINE_NOT_ELECTRIFIED ? 2 : 0
data["shock_timeleft"] = secondsElectrified
data["id_scanner"] = !aiDisabledIdScanner
data["emergency"] = emergency // access
data["locked"] = locked // bolted
data["feedback"] = feedback // lights and sounds
data["safe"] = safe // safeties
data["speed"] = normalspeed // safe speed
data["welded"] = welded // welded
data["opened"] = !density // opened
var/list/wire = list()
wire["main_1"] = !wires.is_cut(WIRE_POWER1)
wire["main_2"] = !wires.is_cut(WIRE_POWER2)
wire["backup_1"] = !wires.is_cut(WIRE_BACKUP1)
wire["backup_2"] = !wires.is_cut(WIRE_BACKUP2)
wire["shock"] = !wires.is_cut(WIRE_SHOCK)
wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN)
wire["bolts"] = !wires.is_cut(WIRE_BOLTS)
wire["feedback"] = !wires.is_cut(WIRE_FEEDBACK)
wire["safe"] = !wires.is_cut(WIRE_SAFETY)
wire["timing"] = !wires.is_cut(WIRE_TIMING)
data["wires"] = wire
return data
/obj/machinery/door/airlock/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(!user_allowed(usr))
return
switch(action)
if("disrupt-main")
if(!main_power_timer)
loseMainPower()
update_appearance()
else
to_chat(usr, span_warning("Main power is already offline."))
. = TRUE
if("disrupt-backup")
if(!backup_power_timer)
loseBackupPower()
update_appearance()
else
to_chat(usr, span_warning("Backup power is already offline."))
. = TRUE
if("shock-restore")
shock_restore(usr)
. = TRUE
if("shock-temp")
shock_temp(usr)
. = TRUE
if("shock-perm")
shock_perm(usr)
. = TRUE
if("idscan-toggle")
aiDisabledIdScanner = !aiDisabledIdScanner
. = TRUE
if("emergency-toggle")
toggle_emergency(usr)
. = TRUE
if("bolt-toggle")
toggle_bolt(usr)
. = TRUE
if("light-toggle")
feedback = !feedback
update_appearance()
. = TRUE
if("safe-toggle")
safe = !safe
. = TRUE
if("speed-toggle")
normalspeed = !normalspeed
. = TRUE
if("open-close")
user_toggle_open(usr)
. = TRUE
/obj/machinery/door/airlock/proc/user_allowed(mob/user)
return (HAS_SILICON_ACCESS(user) && canAIControl(user)) || isAdminGhostAI(user)
/obj/machinery/door/airlock/proc/shock_restore(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, span_warning("Can't un-electrify the airlock - The electrification wire is cut."))
else if(isElectrified())
set_electrified(MACHINE_NOT_ELECTRIFIED, user)
/obj/machinery/door/airlock/proc/shock_temp(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, span_warning("The electrification wire has been cut."))
else
set_electrified(MACHINE_DEFAULT_ELECTRIFY_TIME, user)
/obj/machinery/door/airlock/proc/shock_perm(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, span_warning("The electrification wire has been cut."))
else
set_electrified(MACHINE_ELECTRIFIED_PERMANENT, user)
/obj/machinery/door/airlock/proc/toggle_bolt(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_BOLTS))
to_chat(user, span_warning("The door bolt drop wire is cut - you can't toggle the door bolts."))
return
if(locked)
if(!hasPower())
to_chat(user, span_warning("The door has no power - you can't raise the door bolts."))
else
unbolt()
log_combat(user, src, "unbolted")
else
bolt()
log_combat(user, src, "bolted")
/obj/machinery/door/airlock/proc/toggle_emergency(mob/user)
if(!user_allowed(user))
return
emergency = !emergency
update_appearance()
/obj/machinery/door/airlock/proc/user_toggle_open(mob/user)
if(!user_allowed(user))
return
if(welded)
to_chat(user, span_warning("The airlock has been welded shut!"))
else if(locked)
to_chat(user, span_warning("The door bolts are down!"))
else if(!density)
close()
else
open()
/**
* Generates the airlock's wire layout based on the current area the airlock resides in.
*
* Returns a new /datum/wires/ with the appropriate wire layout based on the airlock_wires
* of the area the airlock is in.
*/
/obj/machinery/door/airlock/proc/get_wires()
var/area/source_area = get_area(src)
return source_area?.airlock_wires ? new source_area.airlock_wires(src) : new /datum/wires/airlock(src)
/obj/structure/fluff/airlock_filler/Destroy(force)
filled_airlock = null
return ..()
/**
* Create a ref to our parent airlock and listen for a QDEL, which we will al
*/
/obj/structure/fluff/airlock_filler/proc/pair_airlock(obj/machinery/door/parent_airlock)
if(isnull(parent_airlock))
stack_trace("Attempted to pair an airlock filler with no parent airlock specified!")
return
filled_airlock = parent_airlock
RegisterSignal(filled_airlock, COMSIG_QDELETING, PROC_REF(no_airlock))
/**
* Multi-tile airlocks pair with a filler panel, if one goes so does the other.
*/
/obj/structure/fluff/airlock_filler/proc/no_airlock()
SIGNAL_HANDLER
qdel(src)
/**
* Multi-tile airlocks (using a filler panel) have special handling for movables with PASSGLASS
*/
/obj/structure/fluff/airlock_filler/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
if(.)
return
if(istype(mover) && (mover.pass_flags & PASSGLASS))
return !opacity
/obj/structure/fluff/airlock_filler/can_be_pulled(user, force)
return FALSE
/obj/structure/fluff/airlock_filler/singularity_act()
return
/obj/structure/fluff/airlock_filler/singularity_pull(atom/singularity, current_size)
return
/obj/machinery/door/airlock/proc/set_cycle_pump(obj/machinery/atmospherics/components/unary/airlock_pump/pump)
RegisterSignal(pump, COMSIG_QDELETING, PROC_REF(unset_cycle_pump))
cycle_pump = pump
/obj/machinery/door/airlock/proc/unset_cycle_pump()
SIGNAL_HANDLER
if(locked)
unbolt()
say("Link broken, unbolting.")
cycle_pump = null
// Station Airlocks Regular
/obj/machinery/door/airlock/command
name = "command airlock"
icon = 'icons/obj/doors/airlocks/station/command.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_com
normal_integrity = 450
/obj/machinery/door/airlock/security
name = "security airlock"
icon = 'icons/obj/doors/airlocks/station/security.dmi'
var/id = null
assemblytype = /obj/structure/door_assembly/door_assembly_sec
normal_integrity = 450
/obj/machinery/door/airlock/engineering
name = "engineering airlock"
icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_eng
/obj/machinery/door/airlock/medical
name = "medical airlock"
icon = 'icons/obj/doors/airlocks/station/medical.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_med
/obj/machinery/door/airlock/hydroponics //Hydroponics front doors!
name = "hydroponics airlock"
icon = 'icons/obj/doors/airlocks/station/hydroponics.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_hydro
/obj/machinery/door/airlock/maintenance
name = "maintenance access"
icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_mai
normal_integrity = 250
/obj/machinery/door/airlock/maintenance/external
name = "external airlock access"
icon = 'icons/obj/doors/airlocks/station/maintenanceexternal.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_extmai
/obj/machinery/door/airlock/mining
name = "mining airlock"
icon = 'icons/obj/doors/airlocks/station/mining.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_min
/obj/machinery/door/airlock/atmos
name = "atmospherics airlock"
icon = 'icons/obj/doors/airlocks/station/atmos.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_atmo
/obj/machinery/door/airlock/research
name = "research airlock"
icon = 'icons/obj/doors/airlocks/station/research.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_research
/obj/machinery/door/airlock/freezer
name = "freezer airlock"
icon = 'icons/obj/doors/airlocks/station/freezer.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_fre
can_be_glass = FALSE
/obj/machinery/door/airlock/science
name = "science airlock"
icon = 'icons/obj/doors/airlocks/station/science.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_science
/obj/machinery/door/airlock/virology
name = "virology airlock"
icon = 'icons/obj/doors/airlocks/station/virology.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_viro
// Station Airlocks Glass
/obj/machinery/door/airlock/glass
name = "glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/glass/incinerator
autoclose = FALSE
heat_proof = TRUE
req_access = list(ACCESS_SYNDICATE)
/obj/machinery/door/airlock/glass/incinerator/syndicatelava_interior
name = "Turbine Interior Airlock"
id_tag = INCINERATOR_SYNDICATELAVA_AIRLOCK_INTERIOR
/obj/machinery/door/airlock/glass/incinerator/syndicatelava_exterior
name = "Turbine Exterior Airlock"
id_tag = INCINERATOR_SYNDICATELAVA_AIRLOCK_EXTERIOR
/obj/machinery/door/airlock/command/glass
name = "command glass airlock"
opacity = FALSE
glass = TRUE
normal_integrity = 400
/obj/machinery/door/airlock/engineering/glass
name = "engineering glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/engineering/glass/critical
critical_machine = TRUE //stops greytide virus from opening & bolting doors in critical positions, such as the SM chamber.
/obj/machinery/door/airlock/security/glass
name = "security glass airlock"
opacity = FALSE
glass = TRUE
normal_integrity = 400
/obj/machinery/door/airlock/medical/glass
name = "medical glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/hydroponics/glass //Uses same icon as medical/glass, maybe update it with its own unique icon one day?
name = "hydroponics glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/research/glass
name = "research glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/research/glass/incinerator
autoclose = FALSE
heat_proof = TRUE
/obj/machinery/door/airlock/research/glass/incinerator/ordmix_interior
name = "Mixing Room Interior Airlock"
id_tag = INCINERATOR_ORDMIX_AIRLOCK_INTERIOR
/obj/machinery/door/airlock/research/glass/incinerator/ordmix_exterior
name = "Mixing Room Exterior Airlock"
id_tag = INCINERATOR_ORDMIX_AIRLOCK_EXTERIOR
/obj/machinery/door/airlock/mining/glass
name = "mining glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/atmos/glass
name = "atmospheric glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/atmos/glass/critical
critical_machine = TRUE //stops greytide virus from opening & bolting doors in critical positions, such as the SM chamber.
/obj/machinery/door/airlock/science/glass
name = "science glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/virology/glass
name = "virology glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/maintenance/glass
name = "maintenance glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/maintenance/external/glass
name = "maintenance external glass airlock"
opacity = FALSE
glass = TRUE
normal_integrity = 200
// Station Airlocks Mineral
/obj/machinery/door/airlock/gold
name = "gold airlock"
icon = 'icons/obj/doors/airlocks/station/gold.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_gold
/obj/machinery/door/airlock/gold/discoinferno
heat_proof = TRUE
resistance_flags = FIRE_PROOF
armor_type = /datum/armor/discoinferno_airlock
/datum/armor/discoinferno_airlock
melee = 30
bullet = 30
laser = 20
energy = 20
bomb = 10
fire = 100
acid = 100
/obj/machinery/door/airlock/gold/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/silver
name = "silver airlock"
icon = 'icons/obj/doors/airlocks/station/silver.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_silver
/obj/machinery/door/airlock/silver/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/diamond
name = "diamond airlock"
icon = 'icons/obj/doors/airlocks/station/diamond.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_diamond
normal_integrity = 1000
explosion_block = 2
/obj/machinery/door/airlock/diamond/glass
normal_integrity = 950
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/uranium
name = "uranium airlock"
icon = 'icons/obj/doors/airlocks/station/uranium.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_uranium
var/last_event = 0
//Is this airlock actually radioactive?
var/actually_radioactive = TRUE
/obj/machinery/door/airlock/uranium/process()
if(actually_radioactive && world.time > last_event+20)
if(prob(50))
radiate()
last_event = world.time
/obj/machinery/door/airlock/uranium/proc/radiate()
radiation_pulse(
src,
max_range = 2,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
)
/obj/machinery/door/airlock/uranium/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/uranium/safe
actually_radioactive = FALSE
/obj/machinery/door/airlock/uranium/glass/safe
actually_radioactive = FALSE
/obj/machinery/door/airlock/plasma
name = "plasma airlock"
desc = "No way this can end badly."
icon = 'icons/obj/doors/airlocks/station/plasma.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_plasma
material_flags = MATERIAL_EFFECTS
material_modifier = 0.25
/obj/machinery/door/airlock/plasma/Initialize(mapload)
custom_materials = custom_materials ? custom_materials : list(/datum/material/plasma = SHEET_MATERIAL_AMOUNT * 10)
. = ..()
/obj/machinery/door/airlock/plasma/block_superconductivity() //we don't stop the heat~
return 0
/obj/machinery/door/airlock/plasma/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/bananium
name = "bananium airlock"
desc = "Honkhonkhonk"
icon = 'icons/obj/doors/airlocks/station/bananium.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_bananium
doorOpen = 'sound/items/bikehorn.ogg'
/obj/machinery/door/airlock/bananium/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/sandstone
name = "sandstone airlock"
icon = 'icons/obj/doors/airlocks/station/sandstone.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_sandstone
/obj/machinery/door/airlock/sandstone/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/wood
name = "wooden airlock"
icon = 'icons/obj/doors/airlocks/station/wood.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_wood
/obj/machinery/door/airlock/wood/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/titanium
name = "shuttle airlock"
assemblytype = /obj/structure/door_assembly/door_assembly_titanium
icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
normal_integrity = 400
/obj/machinery/door/airlock/titanium/glass
normal_integrity = 350
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/bronze
name = "bronze airlock"
icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi'
overlays_file = 'icons/obj/doors/airlocks/clockwork/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_bronze
/obj/machinery/door/airlock/bronze/seethru
assemblytype = /obj/structure/door_assembly/door_assembly_bronze/seethru
opacity = FALSE
glass = TRUE
// Public Airlocks
/obj/machinery/door/airlock/public
name = "public airlock"
icon = 'icons/obj/doors/airlocks/public/glass.dmi'
overlays_file = 'icons/obj/doors/airlocks/public/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_public
/obj/machinery/door/airlock/public/glass
name = "public glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/public/glass/incinerator
autoclose = FALSE
heat_proof = TRUE
/obj/machinery/door/airlock/public/glass/incinerator/atmos_interior
name = "Turbine Interior Airlock"
id_tag = INCINERATOR_ATMOS_AIRLOCK_INTERIOR
/obj/machinery/door/airlock/public/glass/incinerator/atmos_exterior
name = "Turbine Exterior Airlock"
id_tag = INCINERATOR_ATMOS_AIRLOCK_EXTERIOR
// External Airlocks
/obj/machinery/door/airlock/external
name = "external airlock"
icon = 'icons/obj/doors/airlocks/external/external.dmi'
overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
note_overlay_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_ext
/// Whether or not the airlock can be opened without access from a certain direction while powered, or with bare hands from any direction while unpowered OR pressurized.
var/space_dir = null
/obj/machinery/door/airlock/external/Initialize(mapload, ...)
// default setting is for mapping only, let overrides work
if(!mapload)
req_access = null
return ..()
/obj/machinery/door/airlock/external/post_machine_initialize()
. = ..()
if(space_dir)
unres_sides |= space_dir
/obj/machinery/door/airlock/external/examine(mob/user)
. = ..()
if(space_dir)
. += span_notice("It has labels indicating that it has an emergency mechanism to open from the [dir2text(space_dir)] side with <b>just your hands</b> even if there's no power.")
/obj/machinery/door/airlock/external/cyclelinkairlock()
. = ..()
var/obj/machinery/door/airlock/external/cycle_linked_external_airlock = cyclelinkedairlock
if(istype(cycle_linked_external_airlock))
cycle_linked_external_airlock.space_dir |= space_dir
space_dir |= cycle_linked_external_airlock.space_dir
/obj/machinery/door/airlock/external/try_safety_unlock(mob/user)
if(space_dir && density)
if(!hasPower())
to_chat(user, span_notice("You begin unlocking the airlock safety mechanism..."))
if(do_after(user, 15 SECONDS, target = src))
try_to_crowbar(null, user, TRUE)
return TRUE
else
// always open from the space side
// get_dir(src, user) & space_dir, checked in unresricted_sides
var/should_safety_open = shuttledocked || cyclelinkedairlock?.shuttledocked || is_safe_turf(get_step(src, space_dir), TRUE, FALSE)
return try_to_activate_door(user, should_safety_open)
return ..()
// Access free external airlocks
/obj/machinery/door/airlock/external/ruin
/obj/machinery/door/airlock/external/glass
name = "external glass airlock"
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/external/glass/ruin
// CentCom Airlocks
/obj/machinery/door/airlock/centcom //Use grunge as a station side version, as these have special effects related to them via phobias and such.
icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi'
overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_centcom
can_be_glass = FALSE
normal_integrity = 1000
security_level = 6
explosion_block = 2
/obj/machinery/door/airlock/grunge
icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi'
overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_grunge
can_be_glass = FALSE
// Vault Airlocks
/obj/machinery/door/airlock/vault
name = "vault door"
icon = 'icons/obj/doors/airlocks/vault/vault.dmi'
overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_vault
can_be_glass = FALSE
explosion_block = 2
normal_integrity = 400 // reverse engieneerd: 400 * 1.5 (sec lvl 6) = 600 = original
security_level = 6
// Hatch Airlocks
/obj/machinery/door/airlock/hatch
name = "airtight hatch"
icon = 'icons/obj/doors/airlocks/hatch/centcom.dmi'
overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
note_overlay_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_hatch
can_be_glass = FALSE
/obj/machinery/door/airlock/maintenance_hatch
name = "maintenance hatch"
icon = 'icons/obj/doors/airlocks/hatch/maintenance.dmi'
overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
note_overlay_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_mhatch
can_be_glass = FALSE
// High Security Airlocks
/obj/machinery/door/airlock/highsecurity
name = "high tech security airlock"
icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi'
overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_highsecurity
can_be_glass = FALSE
explosion_block = 2
normal_integrity = 500
security_level = 1
damage_deflection = 30
// Shuttle Airlocks
/obj/machinery/door/airlock/shuttle
name = "shuttle airlock"
icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_shuttle
/obj/machinery/door/airlock/shuttle/glass
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/abductor
name = "alien airlock"
desc = "With humanity's current technological level, it could take years to hack this advanced airlock... or maybe we should give a screwdriver a try?"
icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi'
overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_abductor
can_be_glass = FALSE
note_overlay_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
damage_deflection = 30
explosion_block = 3
hackProof = TRUE
aiControlDisabled = AI_WIRE_DISABLED
normal_integrity = 700
security_level = 1
// Cult Airlocks
/obj/machinery/door/airlock/cult
name = "cult airlock"
icon = 'icons/obj/doors/airlocks/cult/runed/cult.dmi'
overlays_file = 'icons/obj/doors/airlocks/cult/runed/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_cult
hackProof = TRUE
aiControlDisabled = AI_WIRE_DISABLED
req_access = list(ACCESS_BLOODCULT)
damage_deflection = 10
var/openingoverlaytype = /obj/effect/temp_visual/cult/door
var/friendly = FALSE
var/stealthy = FALSE
/obj/machinery/door/airlock/cult/Initialize(mapload)
. = ..()
new openingoverlaytype(loc)
AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/machinery/door/airlock/cult/canAIControl(mob/user)
return (IS_CULTIST(user) && !isAllPowerCut())
/obj/machinery/door/airlock/cult/on_break()
set_panel_open(TRUE)
/obj/machinery/door/airlock/cult/isElectrified()
return FALSE
/obj/machinery/door/airlock/cult/hasPower()
return TRUE
/obj/machinery/door/airlock/cult/allowed(mob/living/L)
if(!density)
return TRUE
if(friendly || IS_CULTIST(L) || isshade(L) || isconstruct(L))
if(!stealthy)
new openingoverlaytype(loc)
return TRUE
else
if(!stealthy)
new /obj/effect/temp_visual/cult/sac(loc)
var/atom/throwtarget
throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
SEND_SOUND(L, sound(SFX_HALLUCINATION_TURN_AROUND,0,1,50))
flash_color(L, flash_color=COLOR_CULT_RED, flash_time=20)
L.Paralyze(40)
L.throw_at(throwtarget, 5, 1)
return FALSE
/obj/machinery/door/airlock/cult/proc/conceal()
icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
name = "Airlock"
desc = "It opens and closes."
stealthy = TRUE
update_appearance()
/obj/machinery/door/airlock/cult/proc/reveal()
icon = initial(icon)
overlays_file = initial(overlays_file)
name = initial(name)
desc = initial(desc)
stealthy = initial(stealthy)
update_appearance()
/obj/machinery/door/airlock/cult/narsie_act()
return
/obj/machinery/door/airlock/cult/friendly
friendly = TRUE
/obj/machinery/door/airlock/cult/glass
glass = TRUE
opacity = FALSE
/obj/machinery/door/airlock/cult/glass/friendly
friendly = TRUE
/obj/machinery/door/airlock/cult/unruned
icon = 'icons/obj/doors/airlocks/cult/unruned/cult.dmi'
overlays_file = 'icons/obj/doors/airlocks/cult/unruned/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_cult/unruned
openingoverlaytype = /obj/effect/temp_visual/cult/door/unruned
/obj/machinery/door/airlock/cult/unruned/friendly
friendly = TRUE
/obj/machinery/door/airlock/cult/unruned/glass
glass = TRUE
opacity = FALSE
/obj/machinery/door/airlock/cult/unruned/glass/friendly
friendly = TRUE
/obj/machinery/door/airlock/cult/weak
name = "brittle cult airlock"
desc = "An airlock hastily corrupted by blood magic, it is unusually brittle in this state."
normal_integrity = 150
damage_deflection = 5
armor_type = /datum/armor/none
// Material Airlocks
/obj/machinery/door/airlock/material
name = "Airlock"
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_GREYSCALE | MATERIAL_AFFECT_STATISTICS
greyscale_config = /datum/greyscale_config/material_airlock
assemblytype = /obj/structure/door_assembly/door_assembly_material
/obj/machinery/door/airlock/material/close(forced, force_crush)
. = ..()
if(!.)
return
for(var/datum/material/mat in custom_materials)
if(mat.alpha < 255)
set_opacity(FALSE)
break
/obj/machinery/door/airlock/material/prepare_deconstruction_assembly(obj/structure/door_assembly/assembly)
assembly.set_custom_materials(custom_materials)
..()
/obj/machinery/door/airlock/material/glass
opacity = FALSE
glass = TRUE
// Multi-tile (Large) Airlocks
/obj/machinery/door/airlock/multi_tile
icon = 'icons/obj/doors/airlocks/multi_tile/public/glass.dmi'
overlays_file = 'icons/obj/doors/airlocks/multi_tile/public/overlays.dmi'
assemblytype = /obj/structure/door_assembly/multi_tile/door_assembly_public
multi_tile = TRUE
opacity = FALSE
glass = TRUE
/obj/machinery/door/airlock/multi_tile/setDir(newdir)
. = ..()
set_bounds()
/obj/structure/fluff/airlock_filler
name = "airlock fluff"
desc = "You shouldn't be able to see this fluff!"
icon = null
icon_state = null
density = TRUE
opacity = TRUE
anchored = TRUE
invisibility = INVISIBILITY_MAXIMUM
can_atmos_pass = ATMOS_PASS_DENSITY
/// The door/airlock this fluff panel is attached to
var/obj/machinery/door/filled_airlock
/obj/machinery/door/airlock/multi_tile/public/glass
/obj/machinery/door/airlock/multi_tile/narsie_act()
return
/*
* Subtype used in unit tests to ensure instant airlock opening/closing.
*
* Pretty much just excises everything that would delay the process or is un-needed
* for the sake of the test (sleeps, icon animations).
*/
/obj/machinery/door/airlock/instant
// set_density on both open and close procs has a check and return builtin.
/obj/machinery/door/airlock/instant/open(forced = DEFAULT_DOOR_CHECKS)
SEND_SIGNAL(src, COMSIG_AIRLOCK_OPEN, forced)
operating = TRUE
set_density(FALSE)
set_airlock_state(AIRLOCK_OPEN, animated = FALSE)
return TRUE
/obj/machinery/door/airlock/instant/close(forced = DEFAULT_DOOR_CHECKS, force_crush = FALSE)
SEND_SIGNAL(src, COMSIG_AIRLOCK_CLOSE, forced)
operating = TRUE
set_density(TRUE)
set_airlock_state(AIRLOCK_CLOSED, animated = FALSE)
return TRUE
/obj/machinery/door/airlock/instant/glass
opacity = FALSE
glass = TRUE
#undef AIRLOCK_SECURITY_NONE
#undef AIRLOCK_SECURITY_IRON
#undef AIRLOCK_SECURITY_PLASTEEL_I_S
#undef AIRLOCK_SECURITY_PLASTEEL_I
#undef AIRLOCK_SECURITY_PLASTEEL_O_S
#undef AIRLOCK_SECURITY_PLASTEEL_O
#undef AIRLOCK_SECURITY_PLASTEEL
#undef AIRLOCK_INTEGRITY_N
#undef AIRLOCK_INTEGRITY_MULTIPLIER
#undef AIRLOCK_SEAL_MULTIPLIER
#undef AIRLOCK_DAMAGE_DEFLECTION_N
#undef AIRLOCK_DAMAGE_DEFLECTION_R
#undef AIRLOCK_DENY_ANIMATION_TIME
#undef DOOR_CLOSE_WAIT
#undef DOOR_VISION_DISTANCE
#undef AIRLOCK_FRAME_CLOSED
#undef AIRLOCK_FRAME_CLOSING
#undef AIRLOCK_FRAME_OPEN
#undef AIRLOCK_FRAME_OPENING