Files

1666 lines
59 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.
*/
// Wires for the airlock are located in the datum folder, inside the wires datum folder.
#define AIRLOCK_CLOSED 1
#define AIRLOCK_CLOSING 2
#define AIRLOCK_OPEN 3
#define AIRLOCK_OPENING 4
#define AIRLOCK_DENY 5
#define AIRLOCK_EMAG 6
#define AIRLOCK_SECURITY_NONE 0 //Normal airlock //Wires are not secured
#define AIRLOCK_SECURITY_METAL 1 //Medium security airlock //There is a simple metal 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
#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
#define NOT_ELECTRIFIED 0
#define ELECTRIFIED_PERMANENT -1
#define AI_ELECTRIFY_DOOR_TIME 30
/obj/machinery/door/airlock
name = "airlock"
icon = 'GainStation13/icons/obj/doors/airlocks/station/public.dmi' //GS13 Edit: perspective sprites
icon_state = "closed"
max_integrity = 300
var/normal_integrity = AIRLOCK_INTEGRITY_N
integrity_failure = 0.25
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
autoclose = TRUE
secondsElectrified = NOT_ELECTRIFIED //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
assemblytype = /obj/structure/door_assembly
normalspeed = 1
explosion_block = 1
wave_explosion_block = EXPLOSION_BLOCK_WALL
wave_explosion_multiply = EXPLOSION_DAMPEN_WALL
hud_possible = list(DIAG_AIRLOCK_HUD)
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN
var/security_level = 0 //How much are wires secured
var/aiControlDisabled = 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/hackProof = FALSE // if true, this door can't be hacked by the AI
var/secondsMainPowerLost = 0 //The number of seconds until power is restored.
var/secondsBackupPowerLost = 0 //The number of seconds until power is restored.
var/spawnPowerRestoreRunning = FALSE
var/lights = TRUE // bolt lights show by default
var/aiDisabledIdScanner = FALSE
var/aiHacking = FALSE
var/closeOtherId //Cyclelinking for airlocks that aren't on the same x or y coord as the target.
var/obj/machinery/door/airlock/closeOther
var/justzap = FALSE
var/obj/item/electronics/airlock/electronics
var/shockCooldown = FALSE //Prevents multiple shocks from happening
var/obj/item/doorCharge/charge //If applied, causes an explosion upon opening the door
var/obj/item/note //Any papers pinned to the airlock
var/detonated = FALSE
var/abandoned = FALSE
var/doorOpen = 'sound/machines/airlock.ogg'
var/doorClose = 'sound/machines/airlockclose.ogg'
var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's
var/boltUp = 'sound/machines/boltsup.ogg'
var/boltDown = 'sound/machines/boltsdown.ogg'
var/noPower = 'sound/machines/doorclick.ogg'
var/previous_airlock = /obj/structure/door_assembly //what airlock assembly mineral plating was applied to
var/wiretypepath = /datum/wires/airlock // which set of per round randomized wires this airlock type has.
var/airlock_material //material of inner filling; if its an airlock with glass, this should be set to "glass"
//GS13 Edit Start: perspective sprites
var/overlays_file = 'GainStation13/icons/obj/doors/airlocks/station/overlays.dmi'
var/note_overlay_file = 'GainStation13/icons/obj/doors/airlocks/station/overlays.dmi' //Used for papers and photos pinned to the airlock
//GS13 End
var/cyclelinkeddir = 0
var/obj/machinery/door/airlock/cyclelinkedairlock
var/shuttledocked = 0
var/delayed_close_requested = FALSE // TRUE means the door will automatically close the next time it's opened.
air_tight = FALSE
var/prying_so_hard = FALSE
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
rad_insulation = RAD_MEDIUM_INSULATION
var/static/list/airlock_overlays = list()
/// sigh
var/unelectrify_timerid
/obj/machinery/door/airlock/Initialize(mapload)
. = ..()
wires = new wiretypepath(src) //CIT CHANGE - makes it possible for airlocks to have different wire datums
if(frequency)
set_frequency(frequency)
if(closeOtherId != null)
addtimer(CALLBACK(PROC_REF(update_other_id)), 5)
if(glass)
airlock_material = "glass"
if(security_level > AIRLOCK_SECURITY_METAL)
obj_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
max_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
else
obj_integrity = normal_integrity
max_integrity = normal_integrity
if(damage_deflection == AIRLOCK_DAMAGE_DEFLECTION_N && security_level > AIRLOCK_SECURITY_METAL)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
prepare_huds()
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
diag_hud_set_electrified()
return INITIALIZE_HINT_LATELOAD
/obj/machinery/door/airlock/LateInitialize()
. = ..()
if (cyclelinkeddir)
cyclelinkairlock()
if(abandoned)
var/outcome = rand(1,100)
switch(outcome)
if(1 to 9)
var/turf/here = get_turf(src)
for(var/turf/closed/T in range(2, src))
here.PlaceOnTop(T.type)
qdel(src)
return
here.PlaceOnTop(/turf/closed/wall)
qdel(src)
return
if(9 to 11)
lights = FALSE
locked = TRUE
if(12 to 15)
locked = TRUE
if(16 to 23)
welded = TRUE
if(24 to 30)
panel_open = TRUE
update_icon()
/obj/machinery/door/airlock/ComponentInitialize()
. = ..()
AddComponent(/datum/component/ntnet_interface)
/obj/machinery/door/airlock/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
if(id_tag)
id_tag = "[idnum][id_tag]"
/obj/machinery/door/airlock/proc/update_other_id()
for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
if(A.closeOtherId == closeOtherId && A != src)
closeOther = A
break
/obj/machinery/door/airlock/proc/cyclelinkairlock()
if (cyclelinkedairlock)
cyclelinkedairlock.cyclelinkedairlock = null
cyclelinkedairlock = null
if (!cyclelinkeddir)
return
var/limit = world.view
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)
. = ..()
switch (var_name)
if (NAMEOF(src, cyclelinkeddir))
cyclelinkairlock()
/obj/machinery/door/airlock/check_access_ntnet(datum/netdata/data)
return !requiresID() || ..()
/obj/machinery/door/airlock/ntnet_receive(datum/netdata/data)
// Check if the airlock is powered and can accept control packets.
if(!hasPower() || !canAIControl())
return
// Check packet access level.
if(!check_access_ntnet(data))
return
// Handle received packet.
var/command = lowertext(data.data["data"])
var/command_value = lowertext(data.data["data_secondary"])
switch(command)
if("open")
if(command_value == "on" && !density)
return
if(command_value == "off" && density)
return
if(density)
INVOKE_ASYNC(src, PROC_REF(open))
else
INVOKE_ASYNC(src, PROC_REF(close))
if("bolt")
if(command_value == "on" && locked)
return
if(command_value == "off" && !locked)
return
if(locked)
unbolt()
else
bolt()
if("emergency")
if(command_value == "on" && emergency)
return
if(command_value == "off" && !emergency)
return
emergency = !emergency
update_icon()
/obj/machinery/door/airlock/lock()
bolt()
/obj/machinery/door/airlock/proc/bolt()
if(locked)
return
locked = TRUE
playsound(src,boltDown,30,0,3)
audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
update_icon()
/obj/machinery/door/airlock/unlock()
unbolt()
/obj/machinery/door/airlock/proc/unbolt()
if(!locked)
return
locked = FALSE
playsound(src,boltUp,30,0,3)
audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
update_icon()
/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/ratvar_act() //Airlocks become pinion airlocks that only allow servants
var/obj/machinery/door/airlock/clockwork/A
if(glass)
A = new/obj/machinery/door/airlock/clockwork/brass(get_turf(src))
else
A = new/obj/machinery/door/airlock/clockwork(get_turf(src))
A.name = name
qdel(src)
/obj/machinery/door/airlock/Destroy()
QDEL_NULL(wires)
QDEL_NULL(electronics)
if(charge)
qdel(charge)
charge = null
if (cyclelinkedairlock)
if (cyclelinkedairlock.cyclelinkedairlock == src)
cyclelinkedairlock.cyclelinkedairlock = null
cyclelinkedairlock = null
if(id_tag)
for(var/obj/machinery/doorButtons/D in GLOB.machines)
D.removeMe(src)
QDEL_NULL(note)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.remove_from_hud(src)
return ..()
/obj/machinery/door/airlock/handle_atom_del(atom/A)
if(A == note)
note = null
update_icon()
/obj/machinery/door/airlock/bumpopen(mob/living/user) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite
if(!issilicon(usr))
if(isElectrified())
if(!justzap)
if(shock(user, 100))
justzap = TRUE
addtimer(VARSET_CALLBACK(src, justzap, FALSE) , 10)
return
else
return
else if(user.hallucinating() && ishuman(user) && prob(1) && !operating)
var/mob/living/carbon/human/H = user
if(H.gloves)
var/obj/item/clothing/gloves/G = H.gloves
if(G.siemens_coefficient)//not insulated
new /datum/hallucination/shock(H)
return
if (cyclelinkedairlock)
if (!shuttledocked && !emergency && !cyclelinkedairlock.shuttledocked && !cyclelinkedairlock.emergency && allowed(user))
if(cyclelinkedairlock.operating)
cyclelinkedairlock.delayed_close_requested = TRUE
else
addtimer(CALLBACK(cyclelinkedairlock, PROC_REF(close)), 2)
..()
/obj/machinery/door/airlock/proc/isElectrified()
if(src.secondsElectrified != NOT_ELECTRIFIED)
return TRUE
return FALSE
/obj/machinery/door/airlock/proc/canAIControl(mob/user)
return ((aiControlDisabled != 1) && !isAllPowerCut())
/obj/machinery/door/airlock/proc/canAIHack()
return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerCut()));
/obj/machinery/door/airlock/hasPower()
return ((!secondsMainPowerLost || !secondsBackupPowerLost) && !(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
/obj/machinery/door/airlock/proc/regainMainPower()
if(src.secondsMainPowerLost > 0)
src.secondsMainPowerLost = 0
update_icon()
/obj/machinery/door/airlock/proc/handlePowerRestore()
var/cont = TRUE
while (cont)
sleep(10)
if(QDELETED(src))
return
cont = FALSE
if(secondsMainPowerLost>0)
if(!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
secondsMainPowerLost -= 1
updateDialog()
cont = TRUE
if(secondsBackupPowerLost>0)
if(!wires.is_cut(WIRE_BACKUP1) && !wires.is_cut(WIRE_BACKUP2))
secondsBackupPowerLost -= 1
updateDialog()
cont = TRUE
spawnPowerRestoreRunning = FALSE
updateDialog()
update_icon()
/obj/machinery/door/airlock/proc/loseMainPower()
if(secondsMainPowerLost <= 0)
secondsMainPowerLost = 60
if(secondsBackupPowerLost < 10)
secondsBackupPowerLost = 10
if(!spawnPowerRestoreRunning)
spawnPowerRestoreRunning = TRUE
INVOKE_ASYNC(src, PROC_REF(handlePowerRestore))
update_icon()
/obj/machinery/door/airlock/proc/loseBackupPower()
if(src.secondsBackupPowerLost < 60)
src.secondsBackupPowerLost = 60
if(!spawnPowerRestoreRunning)
spawnPowerRestoreRunning = TRUE
INVOKE_ASYNC(src, PROC_REF(handlePowerRestore))
update_icon()
/obj/machinery/door/airlock/proc/regainBackupPower()
if(src.secondsBackupPowerLost > 0)
src.secondsBackupPowerLost = 0
update_icon()
// 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(shockCooldown > world.time)
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))
shockCooldown = world.time + 10
return TRUE
else
return FALSE
/obj/machinery/door/airlock/update_icon(state=0, override=0)
if(operating && !override)
return
switch(state)
if(0)
if(density)
state = AIRLOCK_CLOSED
else
state = AIRLOCK_OPEN
icon_state = ""
if(AIRLOCK_OPEN, AIRLOCK_CLOSED)
icon_state = ""
if(AIRLOCK_DENY, AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG)
icon_state = "nonexistenticonstate" //MADNESS
set_airlock_overlays(state)
/obj/machinery/door/airlock/proc/set_airlock_overlays(state)
var/mutable_appearance/frame_overlay
var/mutable_appearance/filling_overlay
var/mutable_appearance/lights_overlay
var/mutable_appearance/panel_overlay
var/mutable_appearance/weld_overlay
var/mutable_appearance/damag_overlay
var/mutable_appearance/sparks_overlay
var/mutable_appearance/note_overlay
var/notetype = note_type()
switch(state)
if(AIRLOCK_CLOSED)
frame_overlay = get_airlock_overlay("closed", icon)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(obj_integrity < integrity_failure * max_integrity)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(lights && hasPower())
if(locked)
lights_overlay = get_airlock_overlay("lights_bolts", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(emergency)
lights_overlay = get_airlock_overlay("lights_emergency", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(note)
note_overlay = get_airlock_overlay(notetype, note_overlay_file)
if(AIRLOCK_DENY)
if(!hasPower())
return
frame_overlay = get_airlock_overlay("closed", icon)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(obj_integrity < integrity_failure * max_integrity)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
lights_overlay = get_airlock_overlay("lights_denied", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(note)
note_overlay = get_airlock_overlay(notetype, note_overlay_file)
if(AIRLOCK_EMAG)
frame_overlay = get_airlock_overlay("closed", icon)
sparks_overlay = get_airlock_overlay("sparks", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(obj_integrity < integrity_failure * max_integrity)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(note)
note_overlay = get_airlock_overlay(notetype, note_overlay_file)
if(AIRLOCK_CLOSING)
frame_overlay = get_airlock_overlay("closing", icon)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_closing", overlays_file)
else
filling_overlay = get_airlock_overlay("fill_closing", icon)
if(lights && hasPower())
lights_overlay = get_airlock_overlay("lights_closing", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closing_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closing", overlays_file)
if(note)
note_overlay = get_airlock_overlay("[notetype]_closing", note_overlay_file)
if(AIRLOCK_OPEN)
frame_overlay = get_airlock_overlay("open", icon)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_open", overlays_file)
else
filling_overlay = get_airlock_overlay("fill_open", icon)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_open_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_open", overlays_file)
if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_open", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(note)
note_overlay = get_airlock_overlay("[notetype]_open", note_overlay_file)
if(AIRLOCK_OPENING)
frame_overlay = get_airlock_overlay("opening", icon)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_opening", overlays_file)
else
filling_overlay = get_airlock_overlay("fill_opening", icon)
if(lights && hasPower())
lights_overlay = get_airlock_overlay("lights_opening", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_opening_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_opening", overlays_file)
if(note)
note_overlay = get_airlock_overlay("[notetype]_opening", note_overlay_file)
cut_overlays()
add_overlay(frame_overlay)
add_overlay(filling_overlay)
if(lights_overlay)
add_overlay(lights_overlay)
var/mutable_appearance/lights_vis = mutable_appearance(lights_overlay.icon, lights_overlay.icon_state)
add_overlay(lights_vis)
add_overlay(panel_overlay)
add_overlay(weld_overlay)
add_overlay(sparks_overlay)
if(damag_overlay)
add_overlay(damag_overlay)
var/mutable_appearance/damage_vis = mutable_appearance(damag_overlay.icon, damag_overlay.icon_state)
add_overlay(damage_vis)
add_overlay(note_overlay)
check_unres()
/proc/get_airlock_overlay(icon_state, icon_file, targetlayer = FLOAT_LAYER, targetplane = FLOAT_PLANE)
var/obj/machinery/door/airlock/A
pass(A) //suppress unused warning
var/list/airlock_overlays = A.airlock_overlays
var/iconkey = "[icon_state][icon_file]"
if((!(. = airlock_overlays[iconkey])))
. = airlock_overlays[iconkey] = mutable_appearance(icon_file, icon_state, targetlayer, targetplane)
/obj/machinery/door/airlock/proc/check_unres() //unrestricted sides. This overlay indicates which directions the player can access even without an ID
if(hasPower() && unres_sides)
if(unres_sides & NORTH)
var/image/I = image(icon='GainStation13/icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_n") //layer=src.layer+1 //GS13 Edit: perspective sprites
I.pixel_y = 32
set_light(l_range = 2, l_power = 1)
add_overlay(I)
if(unres_sides & SOUTH)
var/image/I = image(icon='GainStation13/icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_s") //layer=src.layer+1 //GS13 Edit: perspective sprites
I.pixel_y = -32
set_light(l_range = 2, l_power = 1)
add_overlay(I)
if(unres_sides & EAST)
var/image/I = image(icon='GainStation13/icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_e") //layer=src.layer+1 //GS13 Edit: perspective sprites
I.pixel_x = 32
set_light(l_range = 2, l_power = 1)
add_overlay(I)
if(unres_sides & WEST)
var/image/I = image(icon='GainStation13/icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_w") //layer=src.layer+1 //GS13 Edit: perspective sprites
I.pixel_x = -32
set_light(l_range = 2, l_power = 1)
add_overlay(I)
else
if(lights && hasPower())
if(locked)
set_light(1, 0.1, "#0000FF")
else if(emergency)
set_light(1, 0.1, "#FFFF00")
else
set_light(0)
else
set_light(0)
/obj/machinery/door/airlock/do_animate(animation)
switch(animation)
if("opening")
update_icon(AIRLOCK_OPENING)
if("closing")
update_icon(AIRLOCK_CLOSING)
if("deny")
if(!machine_stat)
update_icon(AIRLOCK_DENY)
playsound(src,doorDeni,50,0,3)
sleep(6)
update_icon(AIRLOCK_CLOSED)
/obj/machinery/door/airlock/examine(mob/user)
. = ..()
if(obj_flags & EMAGGED)
. += "<span class='warning'>Its access panel is smoking slightly.</span>"
if(charge && !panel_open && in_range(user, src))
. += "<span class='warning'>The maintenance panel seems haphazardly fastened.</span>"
if(charge && panel_open)
. += "<span class='warning'>Something is wired up to the airlock's electronics!</span>"
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)
if(panel_open)
switch(security_level)
if(AIRLOCK_SECURITY_NONE)
. += "Its wires are exposed!"
if(AIRLOCK_SECURITY_METAL)
. += "Its wires are hidden behind a welded metal 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_METAL)
. += "It looks a bit stronger."
else
. += "It looks very robust."
if(hasSiliconAccessInArea(user) && !(machine_stat & BROKEN))
. += "<span class='notice'>Shift-click [src] to [ density ? "open" : "close"] it.</span>"
. += "<span class='notice'>Ctrl-click [src] to [ locked ? "raise" : "drop"] its bolts.</span>"
. += "<span class='notice'>Alt-click [src] to [ secondsElectrified ? "un-electrify" : "permanently electrify"] it.</span>"
. += "<span class='notice'>Ctrl-Shift-click [src] to [ emergency ? "disable" : "enable"] emergency access.</span>"
/obj/machinery/door/airlock/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if(hasSiliconAccessInArea(user))
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Open interface")
LAZYSET(context[SCREENTIP_CONTEXT_SHIFT_LMB], INTENT_ANY, density ? "Open" : "Close")
LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, locked ? "Unbolt" : "Bolt")
LAZYSET(context[SCREENTIP_CONTEXT_ALT_LMB], INTENT_ANY, secondsElectrified ? "Un-electrify" : "Electrify")
LAZYSET(context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB], INTENT_ANY, emergency ? "Disable emergency access" : "Enable emergency access")
. = CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/stack/sheet/plasteel))
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Reinforce")
return CONTEXTUAL_SCREENTIP_SET
switch (held_item?.tool_behaviour)
if (TOOL_CROWBAR)
if (panel_open)
if (security_level == AIRLOCK_SECURITY_PLASTEEL_O_S || security_level == AIRLOCK_SECURITY_PLASTEEL_I_S)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Remove shielding")
return CONTEXTUAL_SCREENTIP_SET
else if (should_try_removing_electronics())
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "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.
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Pry open")
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_WELDER)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Weld shut")
if (panel_open)
switch (security_level)
if (AIRLOCK_SECURITY_METAL, AIRLOCK_SECURITY_PLASTEEL_I, AIRLOCK_SECURITY_PLASTEEL_O)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Cut shielding")
return CONTEXTUAL_SCREENTIP_SET
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Repair")
return CONTEXTUAL_SCREENTIP_SET
return .
/obj/machinery/door/airlock/attack_ai(mob/user)
if(!src.canAIControl(user))
if(src.canAIHack())
src.hack(user)
return
else
to_chat(user, "<span class='warning'>Airlock AI control has been blocked with a firewall. Unable to hack.</span>")
if(obj_flags & EMAGGED)
to_chat(user, "<span class='warning'>Unable to interface: Airlock is unresponsive.</span>")
return
if(detonated)
to_chat(user, "<span class='warning'>Unable to interface. Airlock control panel damaged.</span>")
return
ui_interact(user)
/obj/machinery/door/airlock/proc/hack(mob/user)
set waitfor = 0
if(!aiHacking)
aiHacking = TRUE
to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.")
sleep(50)
if(src.canAIControl(user))
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
return
else if(!src.canAIHack())
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking = FALSE
return
to_chat(user, "Fault confirmed: airlock control wire disabled or cut.")
sleep(20)
to_chat(user, "Attempting to hack into airlock. This may take some time.")
sleep(200)
if(src.canAIControl(user))
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
return
else if(!src.canAIHack())
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking = FALSE
return
to_chat(user, "Upload access confirmed. Loading control program into airlock software.")
sleep(170)
if(src.canAIControl(user))
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
return
else if(!src.canAIHack())
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking = FALSE
return
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
sleep(50)
//disable blocked control
src.aiControlDisabled = 2
to_chat(user, "Receiving control information from airlock.")
sleep(10)
//bring up airlock dialog
aiHacking = FALSE
if(user)
src.attack_ai(user)
/obj/machinery/door/airlock/attack_animal(mob/user)
. = ..()
if(isElectrified())
shock(user, 100)
/obj/machinery/door/airlock/attack_paw(mob/user)
return attack_hand(user)
/obj/machinery/door/airlock/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!(issilicon(user) || IsAdminGhost(user)))
if(isElectrified())
if(shock(user, 100))
return
if(ishuman(user) && prob(40) && src.density)
var/mob/living/carbon/human/H = user
if((HAS_TRAIT(H, TRAIT_DUMB)) && Adjacent(user))
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)
if(!istype(H.head, /obj/item/clothing/head/helmet))
H.visible_message("<span class='danger'>[user] headbutts the airlock.</span>", \
"<span class='userdanger'>You headbutt the airlock!</span>")
H.DefaultCombatKnockdown(100)
H.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
else
visible_message("<span class='danger'>[user] headbutts the airlock. Good thing [user.p_theyre()] wearing a helmet.</span>")
else
return ..()
/obj/machinery/door/airlock/attempt_wire_interaction(mob/user)
if(security_level)
to_chat(user, "<span class='warning'>Wires are protected!</span>")
return WIRE_INTERACTION_FAIL
return ..()
/obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0)
// If you add an if(..()) check you must first remove the var/nowindow parameter.
// Otherwise it will runtime with this kind of error: null.Topic()
if(!nowindow)
..()
if(!usr.canUseTopic(src) && !IsAdminGhost(usr))
return
add_fingerprint(usr)
if((in_range(src, usr) && isturf(loc)) && panel_open)
usr.set_machine(src)
add_fingerprint(usr)
if(!nowindow)
updateUsrDialog()
else
updateDialog()
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
if(!issilicon(user) && !IsAdminGhost(user))
if(isElectrified())
if(shock(user, 75))
return
add_fingerprint(user)
if(panel_open)
switch(security_level)
if(AIRLOCK_SECURITY_NONE)
if(istype(C, /obj/item/stack/sheet/metal))
var/obj/item/stack/sheet/metal/S = C
if(S.get_amount() < 2)
to_chat(user, "<span class='warning'>You need at least 2 metal sheets to reinforce [src].</span>")
return
to_chat(user, "<span class='notice'>You start reinforcing [src].</span>")
if(do_after(user, 20, TRUE, target = src))
if(!panel_open || !S.use(2))
return
user.visible_message("<span class='notice'>[user] reinforces \the [src] with metal.</span>",
"<span class='notice'>You reinforce \the [src] with metal.</span>")
security_level = AIRLOCK_SECURITY_METAL
update_icon()
return
else if(istype(C, /obj/item/stack/sheet/plasteel))
var/obj/item/stack/sheet/plasteel/S = C
if(S.get_amount() < 2)
to_chat(user, "<span class='warning'>You need at least 2 plasteel sheets to reinforce [src].</span>")
return
to_chat(user, "<span class='notice'>You start reinforcing [src].</span>")
if(do_after(user, 20, 1, target = src))
if(!panel_open || !S.use(2))
return
user.visible_message("<span class='notice'>[user] reinforces \the [src] with plasteel.</span>",
"<span class='notice'>You reinforce \the [src] with plasteel.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL
modify_max_integrity(normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
update_icon()
return
if(AIRLOCK_SECURITY_METAL)
if(C.tool_behaviour == TOOL_WELDER)
if(!C.tool_start_check(user, amount=2))
return
to_chat(user, "<span class='notice'>You begin cutting the panel's shielding...</span>")
if(C.use_tool(src, user, 40, volume=50, amount = 2))
if(!panel_open)
return
user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
"<span class='notice'>You cut through \the [src]'s shielding.</span>",
"<span class='italics'>You hear welding.</span>")
security_level = AIRLOCK_SECURITY_NONE
spawn_atom_to_turf(/obj/item/stack/sheet/metal, user.loc, 2)
update_icon()
return
if(AIRLOCK_SECURITY_PLASTEEL_I_S)
if(C.tool_behaviour == TOOL_CROWBAR)
to_chat(user, "<span class='notice'>You start removing the inner layer of shielding...</span>")
if(C.use_tool(src, user, 40, volume=100))
if(!panel_open)
return
if(security_level != AIRLOCK_SECURITY_PLASTEEL_I_S)
return
user.visible_message("<span class='notice'>[user] remove \the [src]'s shielding.</span>",
"<span class='notice'>You remove \the [src]'s inner shielding.</span>")
security_level = AIRLOCK_SECURITY_NONE
modify_max_integrity(normal_integrity)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
update_icon()
return
if(AIRLOCK_SECURITY_PLASTEEL_I)
if(C.tool_behaviour == TOOL_WELDER)
if(!C.tool_start_check(user, amount=2))
return
to_chat(user, "<span class='notice'>You begin cutting the inner layer of shielding...</span>")
if(C.use_tool(src, user, 40, volume=50, amount=2))
if(!panel_open)
return
user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
"<span class='notice'>You cut through \the [src]'s shielding.</span>",
"<span class='italics'>You hear welding.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_I_S
return
if(AIRLOCK_SECURITY_PLASTEEL_O_S)
if(C.tool_behaviour == TOOL_CROWBAR)
to_chat(user, "<span class='notice'>You start removing outer layer of shielding...</span>")
if(C.use_tool(src, user, 40, volume=100))
if(!panel_open)
return
if(security_level != AIRLOCK_SECURITY_PLASTEEL_O_S)
return
user.visible_message("<span class='notice'>[user] remove \the [src]'s shielding.</span>",
"<span class='notice'>You remove \the [src]'s shielding.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_I
spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
return
if(AIRLOCK_SECURITY_PLASTEEL_O)
if(C.tool_behaviour == TOOL_WELDER)
if(!C.tool_start_check(user, amount=2))
return
to_chat(user, "<span class='notice'>You begin cutting the outer layer of shielding...</span>")
if(C.use_tool(src, user, 40, volume=50, amount=2))
if(!panel_open)
return
user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
"<span class='notice'>You cut through \the [src]'s shielding.</span>",
"<span class='italics'>You hear welding.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_O_S
return
if(AIRLOCK_SECURITY_PLASTEEL)
if(C.tool_behaviour == TOOL_WIRECUTTER)
if(src.hasPower() && src.shock(user, 60)) // Protective grille of wiring is electrified
return
to_chat(user, "<span class='notice'>You start cutting through the outer grille.</span>")
if(C.use_tool(src, user, 10, volume=100))
if(!panel_open)
return
user.visible_message("<span class='notice'>[user] cut through \the [src]'s outer grille.</span>",
"<span class='notice'>You cut through \the [src]'s outer grille.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_O
return
if(C.tool_behaviour == TOOL_SCREWDRIVER)
if(panel_open && detonated)
to_chat(user, "<span class='warning'>[src] has no maintenance panel!</span>")
return
panel_open = !panel_open
to_chat(user, "<span class='notice'>You [panel_open ? "open":"close"] the maintenance panel of the airlock.</span>")
C.play_tool_sound(src)
src.update_icon()
else if(C.tool_behaviour == TOOL_WIRECUTTER && note)
user.visible_message("<span class='notice'>[user] cuts down [note] from [src].</span>", "<span class='notice'>You remove [note] from [src].</span>")
C.play_tool_sound(src)
note.forceMove(get_turf(user))
note = null
update_icon()
else if(is_wire_tool(C) && panel_open)
attempt_wire_interaction(user)
return
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/doorCharge))
if(!panel_open || security_level)
to_chat(user, "<span class='warning'>The maintenance panel must be open to apply [C]!</span>")
return
if(obj_flags & EMAGGED)
return
if(charge && !detonated)
to_chat(user, "<span class='warning'>There's already a charge hooked up to this door!</span>")
return
if(detonated)
to_chat(user, "<span class='warning'>The maintenance panel is destroyed!</span>")
return
to_chat(user, "<span class='warning'>You apply [C]. Next time someone opens the door, it will explode.</span>")
panel_open = FALSE
update_icon()
user.transferItemToLoc(C, src, TRUE)
charge = C
else if(istype(C, /obj/item/paper) || istype(C, /obj/item/photo))
if(note)
to_chat(user, "<span class='warning'>There's already something pinned to this airlock! Use wirecutters to remove it.</span>")
return
if(!user.transferItemToLoc(C, src))
to_chat(user, "<span class='warning'>For some reason, you can't attach [C]!</span>")
return
user.visible_message("<span class='notice'>[user] pins [C] to [src].</span>", "<span class='notice'>You pin [C] to [src].</span>")
note = C
update_icon()
else
return ..()
/obj/machinery/door/airlock/try_to_weld(obj/item/W, mob/user)
if(!W.tool_behaviour == TOOL_WELDER)
return
if(!operating && density)
if(user.a_intent != INTENT_HELP)
if(!W.tool_start_check(user, amount=0))
return
user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \
"<span class='notice'>You begin [welded ? "unwelding":"welding"] the airlock...</span>", \
"<span class='italics'>You hear welding.</span>")
if(W.use_tool(src, user, 40, volume=50, extra_checks = CALLBACK(src, PROC_REF(weld_checks), W, user)))
welded = !welded
user.visible_message("[user.name] has [welded? "welded shut":"unwelded"] [src].", \
"<span class='notice'>You [welded ? "weld the airlock shut":"unweld the airlock"].</span>")
update_icon()
else
if(obj_integrity < max_integrity)
if(!W.tool_start_check(user, amount=0))
return
user.visible_message("[user] is welding the airlock.", \
"<span class='notice'>You begin repairing the airlock...</span>", \
"<span class='italics'>You hear welding.</span>")
if(W.use_tool(src, user, 40, volume=50, extra_checks = CALLBACK(src, PROC_REF(weld_checks), W, user)))
obj_integrity = max_integrity
machine_stat &= ~BROKEN
user.visible_message("[user.name] has repaired [src].", \
"<span class='notice'>You finish repairing the airlock.</span>")
update_icon()
else
to_chat(user, "<span class='notice'>The airlock doesn't need repairing.</span>")
/obj/machinery/door/airlock/proc/weld_checks(obj/item/W, mob/user)
if(!W.tool_behaviour == TOOL_WELDER)
return
return !operating && density
/// 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/I, mob/living/user)
var/beingcrowbarred = null
if(I.tool_behaviour == TOOL_CROWBAR)
beingcrowbarred = 1
else
beingcrowbarred = 0
if(panel_open && charge)
to_chat(user, "<span class='notice'>You carefully start removing [charge] from [src]...</span>")
if(!I.use_tool(src, user, 150, volume=50))
to_chat(user, "<span class='warning'>You slip and [charge] detonates!</span>")
charge.ex_act(EXPLODE_DEVASTATE)
user.DefaultCombatKnockdown(60)
return
user.visible_message("<span class='notice'>[user] removes [charge] from [src].</span>", \
"<span class='notice'>You gently pry out [charge] from [src] and unhook its wires.</span>")
charge.forceMove(get_turf(user))
charge = null
return
if(beingcrowbarred && 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(I.use_tool(src, user, 40, volume=100))
deconstruct(TRUE, user)
return
else if(hasPower())
to_chat(user, "<span class='warning'>The airlock's motors resist your efforts to force it!</span>")
else if(locked)
to_chat(user, "<span class='warning'>The airlock's bolts prevent it from being forced!</span>")
else if(!welded && !operating)
if(!beingcrowbarred) //being fireaxe'd
var/obj/item/fireaxe/axe = I
if(!axe.wielded)
to_chat(user, "<span class='warning'>You need to be wielding \the [axe] to do that!</span>")
return
INVOKE_ASYNC(src, (density ? PROC_REF(open) : PROC_REF(close)), 2)
else
INVOKE_ASYNC(src, (density ? PROC_REF(open) : PROC_REF(close)), 2)
if(I.tool_behaviour == TOOL_CROWBAR)
if(!I.can_force_powered)
return
if(hasPower() && isElectrified())
shock(user,100)//it's like sticking a forck in a power socket
return
if(!density)//already open
return
if(locked)
to_chat(user, "<span class='warning'>The bolts are down, it won't budge!</span>")
return
if(welded)
to_chat(user, "<span class='warning'>It's welded, it won't budge!</span>")
return
var/time_to_open = 5
if(hasPower() && !prying_so_hard)
time_to_open = 50
playsound(src, 'sound/machines/airlock_alien_prying.ogg',100,1) //is it aliens or just the CE being a dick?
prying_so_hard = TRUE
if(do_after(user, time_to_open,target = src))
open(2)
if(density && !open(2))
to_chat(user, "<span class='warning'>Despite your attempts, [src] refuses to open.</span>")
prying_so_hard = FALSE
/obj/machinery/door/airlock/open(forced=0)
if( operating || welded || locked )
return FALSE
if(!forced)
if(!hasPower() || wires.is_cut(WIRE_OPEN))
return FALSE
if(charge && !detonated)
panel_open = TRUE
update_icon(AIRLOCK_OPENING)
visible_message("<span class='warning'>[src]'s panel is blown off in a spray of deadly shrapnel!</span>")
charge.forceMove(drop_location())
charge.ex_act(EXPLODE_DEVASTATE)
detonated = 1
charge = null
for(var/mob/living/carbon/human/H in orange(2,src))
H.adjust_fire_stacks(20)
H.IgniteMob() //Guaranteed knockout and ignition for nearby people
H.apply_damage(40, BRUTE, BODY_ZONE_CHEST)
return
if(forced < 2)
if(obj_flags & EMAGGED)
return FALSE
use_power(50)
playsound(src, doorOpen, 30, 1)
if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density)
src.closeOther.close()
else
playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1)
if(autoclose)
autoclose_in(normalspeed ? 150 : 15)
if(!density)
return TRUE
operating = TRUE
update_icon(AIRLOCK_OPENING, 1)
sleep(1)
set_opacity(0)
update_freelook_sight()
sleep(4)
density = FALSE
air_update_turf(1)
sleep(1)
layer = OPEN_DOOR_LAYER
update_icon(AIRLOCK_OPEN, 1)
operating = FALSE
if(delayed_close_requested)
delayed_close_requested = FALSE
addtimer(CALLBACK(src, PROC_REF(close)), 1)
return TRUE
/obj/machinery/door/airlock/close(forced=0)
if(operating || welded || locked)
return
if(density)
return TRUE
if(!forced)
if(!hasPower() || wires.is_cut(WIRE_BOLTS))
return
if(safe)
for(var/atom/movable/M in get_turf(src))
if(M.density && M != src) //something is blocking the door
autoclose_in(60)
return
if(forced < 2)
if(obj_flags & EMAGGED)
return
use_power(50)
playsound(src.loc, doorClose, 30, 1)
else
playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1)
var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src))
if(killthis)
killthis.ex_act(EXPLODE_HEAVY)//Smashin windows
operating = TRUE
update_icon(AIRLOCK_CLOSING, 1)
layer = CLOSED_DOOR_LAYER
if(air_tight)
density = TRUE
air_update_turf(1)
sleep(1)
if(!air_tight)
density = TRUE
air_update_turf(1)
sleep(4)
if(!safe)
crush()
if(visible && !glass)
set_opacity(1)
update_freelook_sight()
sleep(1)
update_icon(AIRLOCK_CLOSED, 1)
operating = FALSE
delayed_close_requested = FALSE
if(safe)
CheckForMobs()
return TRUE
/obj/machinery/door/airlock/proc/prison_open()
if(obj_flags & EMAGGED)
return
locked = FALSE
src.open()
locked = TRUE
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)
icon = initial(airlock.icon)
overlays_file = initial(airlock.overlays_file)
assemblytype = initial(airlock.assemblytype)
update_icon()
/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off)
return !density || (check_access(ID) && !locked && hasPower())
/obj/machinery/door/airlock/emag_act(mob/user)
. = ..()
if(operating || !density || !hasPower() || obj_flags & EMAGGED)
return
operating = TRUE
update_icon(AIRLOCK_EMAG, 1)
addtimer(CALLBACK(src, PROC_REF(open_sesame)), 6)
return TRUE
/obj/machinery/door/airlock/proc/open_sesame()
operating = FALSE
if(!open())
update_icon(AIRLOCK_CLOSED, 1)
obj_flags |= EMAGGED
lights = FALSE
locked = TRUE
loseMainPower()
loseBackupPower()
/obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/humanoid/user)
add_fingerprint(user)
if(isElectrified())
shock(user, 100) //Mmm, fried xeno!
return
if(!density) //Already open
return
if(locked || welded) //Extremely generic, as aliens only understand the basics of how airlocks work.
to_chat(user, "<span class='warning'>[src] refuses to budge!</span>")
return
user.visible_message("<span class='warning'>[user] begins prying open [src].</span>",\
"<span class='noticealien'>You begin digging your claws into [src] with all your might!</span>",\
"<span class='warning'>You hear groaning metal...</span>")
var/time_to_open = 5
if(hasPower())
time_to_open = 50 //Powered airlocks take longer to open, and are loud.
playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1)
if(do_after(user, time_to_open, target = src))
if(density && !open(2)) //The airlock is still closed, but something prevented it opening. (Another player noticed and bolted/welded the airlock in time!)
to_chat(user, "<span class='warning'>Despite your efforts, [src] managed to resist your attempts to open it!</span>")
/obj/machinery/door/airlock/hostile_lockdown(mob/origin, aicontrolneeded = TRUE)
// Must be powered and have working AI wire.
if((aicontrolneeded && canAIControl(src) && !machine_stat) || !aicontrolneeded)
locked = FALSE //For airlocks that were bolted open.
safe = FALSE //DOOR CRUSH
close()
bolt() //Bolt it!
set_electrified(ELECTRIFIED_PERMANENT) //Shock it!
if(origin)
LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(origin)]")
/obj/machinery/door/airlock/disable_lockdown(aicontrolneeded = TRUE)
// Must be powered and have working AI wire.
if((aicontrolneeded && canAIControl(src) && !machine_stat) || !aicontrolneeded)
unbolt()
set_electrified(NOT_ELECTRIFIED)
open()
safe = TRUE
/obj/machinery/door/airlock/obj_break(damage_flag)
if(!(flags_1 & BROKEN) && !(flags_1 & NODECONSTRUCT_1))
machine_stat |= BROKEN
if(!panel_open)
panel_open = TRUE
wires.cut_all()
update_icon()
/obj/machinery/door/airlock/proc/remove_electrify()
secondsElectrified = NOT_ELECTRIFIED
unelectrify_timerid = null
diag_hud_set_electrified()
/obj/machinery/door/airlock/proc/set_electrified(seconds, mob/user)
secondsElectrified = seconds
if(unelectrify_timerid)
deltimer(unelectrify_timerid)
unelectrify_timerid = null
if(secondsElectrified != ELECTRIFIED_PERMANENT)
unelectrify_timerid = addtimer(CALLBACK(src, PROC_REF(remove_electrify)), secondsElectrified SECONDS, TIMER_STOPPABLE)
diag_hud_set_electrified()
if(user)
var/message
switch(secondsElectrified)
if(ELECTRIFIED_PERMANENT)
message = "permanently shocked"
if(NOT_ELECTRIFIED)
message = "unshocked"
else
message = "temp shocked for [secondsElectrified] seconds"
LAZYADD(shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [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(obj_integrity < (0.75 * max_integrity))
update_icon()
/obj/machinery/door/airlock/deconstruct(disassembled = TRUE, mob/user)
if(!(flags_1 & NODECONSTRUCT_1))
var/obj/structure/door_assembly/A
if(assemblytype)
A = new assemblytype(src.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.
A.heat_proof_finished = src.heat_proof //tracks whether there's rglass in
A.setAnchored(TRUE)
A.glass = src.glass
A.state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
A.created_name = name
A.previous_assembly = previous_airlock
A.update_name()
A.update_icon()
if(!disassembled)
if(A)
A.obj_integrity = A.max_integrity * 0.5
else if(obj_flags & EMAGGED)
if(user)
to_chat(user, "<span class='warning'>You discard the damaged electronics.</span>")
else
if(user)
to_chat(user, "<span class='notice'>You remove the airlock electronics.</span>")
var/obj/item/electronics/airlock/ae
if(!electronics)
ae = new/obj/item/electronics/airlock( src.loc )
gen_access()
if(req_one_access.len)
ae.one_access = 1
ae.accesses = src.req_one_access
else
ae.accesses = src.req_access
else
ae = electronics
electronics = null
ae.forceMove(drop_location())
qdel(src)
/obj/machinery/door/airlock/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
if(RCD_DECONSTRUCT)
if(security_level != AIRLOCK_SECURITY_NONE && the_rcd.canRturf != TRUE)
to_chat(user, "<span class='notice'>[src]'s reinforcement needs to be removed first.</span>")
return FALSE
return list("mode" = RCD_DECONSTRUCT, "delay" = 50, "cost" = 32)
return FALSE
/obj/machinery/door/airlock/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
switch(passed_mode)
if(RCD_DECONSTRUCT)
to_chat(user, "<span class='notice'>You deconstruct the airlock.</span>")
qdel(src)
return TRUE
return FALSE
/obj/machinery/door/airlock/proc/note_type() //Returns a string representing the type of note pinned to this airlock
if(!note)
return
else if(istype(note, /obj/item/paper))
return "note"
else if(istype(note, /obj/item/photo))
return "photo"
/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_status(mob/user)
. = ..()
if (!issilicon(user) && hasSiliconAccessInArea(user))
. = UI_INTERACTIVE
/obj/machinery/door/airlock/can_interact(mob/user)
. = ..()
if (!issilicon(user) && hasSiliconAccessInArea(user))
return TRUE
/obj/machinery/door/airlock/ui_data()
var/list/data = list()
var/list/power = list()
power["main"] = secondsMainPowerLost ? 0 : 2 // boolean
power["main_timeleft"] = secondsMainPowerLost
power["backup"] = secondsBackupPowerLost ? 0 : 2 // boolean
power["backup_timeleft"] = secondsBackupPowerLost
data["power"] = power
data["shock"] = secondsElectrified == NOT_ELECTRIFIED ? 2 : 0
data["shock_timeleft"] = secondsElectrified
data["id_scanner"] = !aiDisabledIdScanner
data["emergency"] = emergency // access
data["locked"] = locked // bolted
data["lights"] = lights // bolt lights
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["lights"] = !wires.is_cut(WIRE_LIGHT)
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, params)
if(..())
return
if(!user_allowed(usr))
return
switch(action)
if("disrupt-main")
if(!secondsMainPowerLost)
loseMainPower()
update_icon()
else
to_chat(usr, "<span class='warning'>Main power is already offline.</span>")
. = TRUE
if("disrupt-backup")
if(!secondsBackupPowerLost)
loseBackupPower()
update_icon()
else
to_chat(usr, "<span class='warning'>Backup power is already offline.</span>")
. = 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")
lights = !lights
update_icon()
. = 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 (hasSiliconAccessInArea(user) && canAIControl(user)) || IsAdminGhost(user)
/obj/machinery/door/airlock/proc/shock_restore(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, "Can't un-electrify the airlock - The electrification wire is cut.")
else if(isElectrified())
set_electrified(0)
/obj/machinery/door/airlock/proc/shock_temp(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, "The electrification wire has been cut")
else
LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(user)]")
log_combat(user, src, "electrified")
set_electrified(AI_ELECTRIFY_DOOR_TIME)
/obj/machinery/door/airlock/proc/shock_perm(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, "The electrification wire has been cut")
else
LAZYADD(shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(user)]"))
log_combat(user, src, "electrified")
set_electrified(ELECTRIFIED_PERMANENT)
/obj/machinery/door/airlock/proc/toggle_bolt(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_BOLTS))
to_chat(user, "<span class='warning'>The door bolt drop wire is cut - you can't toggle the door bolts.</span>")
return
if(locked)
if(!hasPower())
to_chat(user, "<span class='warning'>The door has no power - you can't raise the door bolts.</span>")
else
unbolt()
else
bolt()
/obj/machinery/door/airlock/proc/toggle_emergency(mob/user)
if(!user_allowed(user))
return
emergency = !emergency
update_icon()
/obj/machinery/door/airlock/proc/user_toggle_open(mob/user)
if(!user_allowed(user))
return
if(welded)
to_chat(user, text("The airlock has been welded shut!"))
else if(locked)
to_chat(user, text("The door bolts are down!"))
else if(!density)
close()
else
open()
#undef AIRLOCK_CLOSED
#undef AIRLOCK_CLOSING
#undef AIRLOCK_OPEN
#undef AIRLOCK_OPENING
#undef AIRLOCK_DENY
#undef AIRLOCK_EMAG
#undef AIRLOCK_SECURITY_NONE
#undef AIRLOCK_SECURITY_METAL
#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_DAMAGE_DEFLECTION_N
#undef AIRLOCK_DAMAGE_DEFLECTION_R
#undef NOT_ELECTRIFIED
#undef ELECTRIFIED_PERMANENT
#undef AI_ELECTRIFY_DOOR_TIME