You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1498 lines
52 KiB

  1. /*
  2. New methods:
  3. pulse - sends a pulse into a wire for hacking purposes
  4. cut - cuts a wire and makes any necessary state changes
  5. mend - mends a wire and makes any necessary state changes
  6. canAIControl - 1 if the AI can control the airlock, 0 if not (then check canAIHack to see if it can hack in)
  7. 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.
  8. hasPower - 1 if the main or backup power are functioning, 0 if not.
  9. requiresIDs - 1 if the airlock is requiring IDs, 0 if not
  10. isAllPowerCut - 1 if the main and backup power both have cut wires.
  11. regainMainPower - handles the effect of main power coming back on.
  12. 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.
  13. loseBackupPower - handles the effect of backup power going offline.
  14. regainBackupPower - handles the effect of main power coming back on.
  15. shock - has a chance of electrocuting its target.
  16. */
  17. // Wires for the airlock are located in the datum folder, inside the wires datum folder.
  18. #define AIRLOCK_CLOSED 1
  19. #define AIRLOCK_CLOSING 2
  20. #define AIRLOCK_OPEN 3
  21. #define AIRLOCK_OPENING 4
  22. #define AIRLOCK_DENY 5
  23. #define AIRLOCK_EMAG 6
  24. #define AIRLOCK_SECURITY_NONE 0 //Normal airlock //Wires are not secured
  25. #define AIRLOCK_SECURITY_METAL 1 //Medium security airlock //There is a simple metal over wires (use welder)
  26. #define AIRLOCK_SECURITY_PLASTEEL_I_S 2 //Sliced inner plating (use crowbar), jumps to 0
  27. #define AIRLOCK_SECURITY_PLASTEEL_I 3 //Removed outer plating, second layer here (use welder)
  28. #define AIRLOCK_SECURITY_PLASTEEL_O_S 4 //Sliced outer plating (use crowbar)
  29. #define AIRLOCK_SECURITY_PLASTEEL_O 5 //There is first layer of plasteel (use welder)
  30. #define AIRLOCK_SECURITY_PLASTEEL 6 //Max security airlock //Fully secured wires (use wirecutters to remove grille, that is electrified)
  31. #define AIRLOCK_INTEGRITY_N 300 // Normal airlock integrity
  32. #define AIRLOCK_INTEGRITY_MULTIPLIER 1.5 // How much reinforced doors health increases
  33. #define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
  34. #define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
  35. /obj/machinery/door/airlock
  36. name = "airlock"
  37. icon = 'icons/obj/doors/airlocks/station/public.dmi'
  38. icon_state = "closed"
  39. max_integrity = 300
  40. var/normal_integrity = AIRLOCK_INTEGRITY_N
  41. integrity_failure = 0.25
  42. damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
  43. autoclose = TRUE
  44. secondsElectrified = MACHINE_NOT_ELECTRIFIED //How many seconds remain until the door is no longer electrified. -1/MACHINE_ELECTRIFIED_PERMANENT = permanently electrified until someone fixes it.
  45. assemblytype = /obj/structure/door_assembly
  46. normalspeed = 1
  47. explosion_block = 1
  48. hud_possible = list(DIAG_AIRLOCK_HUD)
  49. interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN
  50. var/security_level = 0 //How much are wires secured
  51. 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.
  52. var/hackProof = FALSE // if true, this door can't be hacked by the AI
  53. var/secondsMainPowerLost = 0 //The number of seconds until power is restored.
  54. var/secondsBackupPowerLost = 0 //The number of seconds until power is restored.
  55. var/spawnPowerRestoreRunning = FALSE
  56. var/lights = TRUE // bolt lights show by default
  57. var/aiDisabledIdScanner = FALSE
  58. var/aiHacking = FALSE
  59. var/closeOtherId //Cyclelinking for airlocks that aren't on the same x or y coord as the target.
  60. var/obj/machinery/door/airlock/closeOther
  61. var/justzap = FALSE
  62. var/obj/item/electronics/airlock/electronics
  63. var/shockCooldown = FALSE //Prevents multiple shocks from happening
  64. var/obj/item/note //Any papers pinned to the airlock
  65. var/detonated = FALSE
  66. var/abandoned = FALSE
  67. var/doorOpen = 'sound/machines/airlock.ogg'
  68. var/doorClose = 'sound/machines/airlockclose.ogg'
  69. var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's
  70. var/boltUp = 'sound/machines/boltsup.ogg'
  71. var/boltDown = 'sound/machines/boltsdown.ogg'
  72. var/noPower = 'sound/machines/doorclick.ogg'
  73. var/previous_airlock = /obj/structure/door_assembly //what airlock assembly mineral plating was applied to
  74. var/airlock_material //material of inner filling; if its an airlock with glass, this should be set to "glass"
  75. var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
  76. var/note_overlay_file = 'icons/obj/doors/airlocks/station/overlays.dmi' //Used for papers and photos pinned to the airlock
  77. var/cyclelinkeddir = 0
  78. var/obj/machinery/door/airlock/cyclelinkedairlock
  79. var/shuttledocked = 0
  80. var/delayed_close_requested = FALSE // TRUE means the door will automatically close the next time it's opened.
  81. var/air_tight = FALSE //TRUE means density will be set as soon as the door begins to close
  82. var/prying_so_hard = FALSE
  83. rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
  84. rad_insulation = RAD_MEDIUM_INSULATION
  85. var/static/list/airlock_overlays = list()
  86. /obj/machinery/door/airlock/Initialize()
  87. . = ..()
  88. wires = new /datum/wires/airlock(src)
  89. if(frequency)
  90. set_frequency(frequency)
  91. if(closeOtherId != null)
  92. addtimer(CALLBACK(.proc/update_other_id), 5)
  93. if(glass)
  94. airlock_material = "glass"
  95. if(security_level > AIRLOCK_SECURITY_METAL)
  96. obj_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
  97. max_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
  98. else
  99. obj_integrity = normal_integrity
  100. max_integrity = normal_integrity
  101. if(damage_deflection == AIRLOCK_DAMAGE_DEFLECTION_N && security_level > AIRLOCK_SECURITY_METAL)
  102. damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
  103. prepare_huds()
  104. for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
  105. diag_hud.add_to_hud(src)
  106. diag_hud_set_electrified()
  107. RegisterSignal(src, COMSIG_MACHINERY_BROKEN, .proc/on_break)
  108. return INITIALIZE_HINT_LATELOAD
  109. /obj/machinery/door/airlock/LateInitialize()
  110. . = ..()
  111. if (cyclelinkeddir)
  112. cyclelinkairlock()
  113. if(abandoned)
  114. var/outcome = rand(1,100)
  115. switch(outcome)
  116. if(1 to 9)
  117. var/turf/here = get_turf(src)
  118. for(var/turf/closed/T in range(2, src))
  119. here.PlaceOnTop(T.type)
  120. qdel(src)
  121. return
  122. here.PlaceOnTop(/turf/closed/wall)
  123. qdel(src)
  124. return
  125. if(9 to 11)
  126. lights = FALSE
  127. locked = TRUE
  128. if(12 to 15)
  129. locked = TRUE
  130. if(16 to 23)
  131. welded = TRUE
  132. if(24 to 30)
  133. panel_open = TRUE
  134. update_icon()
  135. /obj/machinery/door/airlock/ComponentInitialize()
  136. . = ..()
  137. AddComponent(/datum/component/ntnet_interface)
  138. /obj/machinery/door/airlock/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
  139. if(id_tag)
  140. id_tag = "[idnum][id_tag]"
  141. /obj/machinery/door/airlock/proc/update_other_id()
  142. for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
  143. if(A.closeOtherId == closeOtherId && A != src)
  144. closeOther = A
  145. break
  146. /obj/machinery/door/airlock/proc/cyclelinkairlock()
  147. if (cyclelinkedairlock)
  148. cyclelinkedairlock.cyclelinkedairlock = null
  149. cyclelinkedairlock = null
  150. if (!cyclelinkeddir)
  151. return
  152. var/limit = world.view
  153. var/turf/T = get_turf(src)
  154. var/obj/machinery/door/airlock/FoundDoor
  155. do
  156. T = get_step(T, cyclelinkeddir)
  157. FoundDoor = locate() in T
  158. if (FoundDoor && FoundDoor.cyclelinkeddir != get_dir(FoundDoor, src))
  159. FoundDoor = null
  160. limit--
  161. while(!FoundDoor && limit)
  162. if (!FoundDoor)
  163. log_mapping("[src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
  164. return
  165. FoundDoor.cyclelinkedairlock = src
  166. cyclelinkedairlock = FoundDoor
  167. /obj/machinery/door/airlock/vv_edit_var(var_name)
  168. . = ..()
  169. switch (var_name)
  170. if ("cyclelinkeddir")
  171. cyclelinkairlock()
  172. /obj/machinery/door/airlock/check_access_ntnet(datum/netdata/data)
  173. return !requiresID() || ..()
  174. /obj/machinery/door/airlock/ntnet_receive(datum/netdata/data)
  175. // Check if the airlock is powered and can accept control packets.
  176. if(!hasPower() || !canAIControl())
  177. return
  178. // Check packet access level.
  179. if(!check_access_ntnet(data))
  180. return
  181. // Handle received packet.
  182. var/command = lowertext(data.data["data"])
  183. var/command_value = lowertext(data.data["data_secondary"])
  184. switch(command)
  185. if("open")
  186. if(command_value == "on" && !density)
  187. return
  188. if(command_value == "off" && density)
  189. return
  190. if(density)
  191. INVOKE_ASYNC(src, .proc/open)
  192. else
  193. INVOKE_ASYNC(src, .proc/close)
  194. if("bolt")
  195. if(command_value == "on" && locked)
  196. return
  197. if(command_value == "off" && !locked)
  198. return
  199. if(locked)
  200. unbolt()
  201. else
  202. bolt()
  203. if("emergency")
  204. if(command_value == "on" && emergency)
  205. return
  206. if(command_value == "off" && !emergency)
  207. return
  208. emergency = !emergency
  209. update_icon()
  210. /obj/machinery/door/airlock/lock()
  211. bolt()
  212. /obj/machinery/door/airlock/proc/bolt()
  213. if(locked)
  214. return
  215. locked = TRUE
  216. playsound(src,boltDown,30,FALSE,3)
  217. audible_message("<span class='hear'>You hear a click from the bottom of the door.</span>", null, 1)
  218. update_icon()
  219. /obj/machinery/door/airlock/unlock()
  220. unbolt()
  221. /obj/machinery/door/airlock/proc/unbolt()
  222. if(!locked)
  223. return
  224. locked = FALSE
  225. playsound(src,boltUp,30,FALSE,3)
  226. audible_message("<span class='hear'>You hear a click from the bottom of the door.</span>", null, 1)
  227. update_icon()
  228. /obj/machinery/door/airlock/narsie_act()
  229. var/turf/T = get_turf(src)
  230. var/obj/machinery/door/airlock/cult/A
  231. if(GLOB.cult_narsie)
  232. var/runed = prob(20)
  233. if(glass)
  234. if(runed)
  235. A = new/obj/machinery/door/airlock/cult/glass(T)
  236. else
  237. A = new/obj/machinery/door/airlock/cult/unruned/glass(T)
  238. else
  239. if(runed)
  240. A = new/obj/machinery/door/airlock/cult(T)
  241. else
  242. A = new/obj/machinery/door/airlock/cult/unruned(T)
  243. A.name = name
  244. else
  245. A = new /obj/machinery/door/airlock/cult/weak(T)
  246. qdel(src)
  247. /obj/machinery/door/airlock/Destroy()
  248. QDEL_NULL(wires)
  249. QDEL_NULL(electronics)
  250. if (cyclelinkedairlock)
  251. if (cyclelinkedairlock.cyclelinkedairlock == src)
  252. cyclelinkedairlock.cyclelinkedairlock = null
  253. cyclelinkedairlock = null
  254. if(id_tag)
  255. for(var/obj/machinery/doorButtons/D in GLOB.machines)
  256. D.removeMe(src)
  257. QDEL_NULL(note)
  258. for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
  259. diag_hud.remove_from_hud(src)
  260. return ..()
  261. /obj/machinery/door/airlock/handle_atom_del(atom/A)
  262. if(A == note)
  263. note = null
  264. update_icon()
  265. /obj/machinery/door/airlock/bumpopen(mob/living/user) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite
  266. if(!issilicon(usr))
  267. if(isElectrified())
  268. if(!justzap)
  269. if(shock(user, 100))
  270. justzap = TRUE
  271. addtimer(VARSET_CALLBACK(src, justzap, FALSE) , 10)
  272. return
  273. else
  274. return
  275. else if(user.hallucinating() && ishuman(user) && prob(1) && !operating)
  276. var/mob/living/carbon/human/H = user
  277. if(H.gloves)
  278. var/obj/item/clothing/gloves/G = H.gloves
  279. if(G.siemens_coefficient)//not insulated
  280. new /datum/hallucination/shock(H)
  281. return
  282. if (cyclelinkedairlock)
  283. if (!shuttledocked && !emergency && !cyclelinkedairlock.shuttledocked && !cyclelinkedairlock.emergency && allowed(user))
  284. if(cyclelinkedairlock.operating)
  285. cyclelinkedairlock.delayed_close_requested = TRUE
  286. else
  287. addtimer(CALLBACK(cyclelinkedairlock, .proc/close), 2)
  288. ..()
  289. /obj/machinery/door/airlock/proc/isElectrified()
  290. if(secondsElectrified != MACHINE_NOT_ELECTRIFIED)
  291. return TRUE
  292. return FALSE
  293. /obj/machinery/door/airlock/proc/canAIControl(mob/user)
  294. return ((aiControlDisabled != 1) && !isAllPowerCut())
  295. /obj/machinery/door/airlock/proc/canAIHack()
  296. return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerCut()));
  297. /obj/machinery/door/airlock/hasPower()
  298. return ((!secondsMainPowerLost || !secondsBackupPowerLost) && !(machine_stat & NOPOWER))
  299. /obj/machinery/door/airlock/requiresID()
  300. return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner)
  301. /obj/machinery/door/airlock/proc/isAllPowerCut()
  302. if((wires.is_cut(WIRE_POWER1) || wires.is_cut(WIRE_POWER2)) && (wires.is_cut(WIRE_BACKUP1) || wires.is_cut(WIRE_BACKUP2)))
  303. return TRUE
  304. /obj/machinery/door/airlock/proc/regainMainPower()
  305. if(secondsMainPowerLost > 0)
  306. secondsMainPowerLost = 0
  307. update_icon()
  308. /obj/machinery/door/airlock/proc/handlePowerRestore()
  309. var/cont = TRUE
  310. while (cont)
  311. sleep(10)
  312. if(QDELETED(src))
  313. return
  314. cont = FALSE
  315. if(secondsMainPowerLost>0)
  316. if(!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
  317. secondsMainPowerLost -= 1
  318. updateDialog()
  319. cont = TRUE
  320. if(secondsBackupPowerLost>0)
  321. if(!wires.is_cut(WIRE_BACKUP1) && !wires.is_cut(WIRE_BACKUP2))
  322. secondsBackupPowerLost -= 1
  323. updateDialog()
  324. cont = TRUE
  325. spawnPowerRestoreRunning = FALSE
  326. updateDialog()
  327. update_icon()
  328. /obj/machinery/door/airlock/proc/loseMainPower()
  329. if(secondsMainPowerLost <= 0)
  330. secondsMainPowerLost = 60
  331. if(secondsBackupPowerLost < 10)
  332. secondsBackupPowerLost = 10
  333. if(!spawnPowerRestoreRunning)
  334. spawnPowerRestoreRunning = TRUE
  335. INVOKE_ASYNC(src, .proc/handlePowerRestore)
  336. update_icon()
  337. /obj/machinery/door/airlock/proc/loseBackupPower()
  338. if(secondsBackupPowerLost < 60)
  339. secondsBackupPowerLost = 60
  340. if(!spawnPowerRestoreRunning)
  341. spawnPowerRestoreRunning = TRUE
  342. INVOKE_ASYNC(src, .proc/handlePowerRestore)
  343. update_icon()
  344. /obj/machinery/door/airlock/proc/regainBackupPower()
  345. if(secondsBackupPowerLost > 0)
  346. secondsBackupPowerLost = 0
  347. update_icon()
  348. // shock user with probability prb (if all connections & power are working)
  349. // returns TRUE if shocked, FALSE otherwise
  350. // The preceding comment was borrowed from the grille's shock script
  351. /obj/machinery/door/airlock/proc/shock(mob/living/user, prb)
  352. if(!istype(user) || !hasPower()) // unpowered, no shock
  353. return FALSE
  354. if(shockCooldown > world.time)
  355. return FALSE //Already shocked someone recently?
  356. if(!prob(prb))
  357. return FALSE //you lucked out, no shock for you
  358. do_sparks(5, TRUE, src)
  359. var/check_range = TRUE
  360. if(electrocute_mob(user, get_area(src), src, 1, check_range))
  361. shockCooldown = world.time + 10
  362. return TRUE
  363. else
  364. return FALSE
  365. /obj/machinery/door/airlock/update_icon(state=0, override=0)
  366. if(operating && !override)
  367. return
  368. switch(state)
  369. if(0)
  370. if(density)
  371. state = AIRLOCK_CLOSED
  372. else
  373. state = AIRLOCK_OPEN
  374. icon_state = ""
  375. if(AIRLOCK_OPEN, AIRLOCK_CLOSED)
  376. icon_state = ""
  377. if(AIRLOCK_DENY, AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG)
  378. icon_state = "nonexistenticonstate" //MADNESS
  379. set_airlock_overlays(state)
  380. SSdemo.mark_dirty(src)
  381. /obj/machinery/door/airlock/proc/set_airlock_overlays(state)
  382. var/mutable_appearance/frame_overlay
  383. var/mutable_appearance/filling_overlay
  384. var/mutable_appearance/lights_overlay
  385. var/mutable_appearance/panel_overlay
  386. var/mutable_appearance/weld_overlay
  387. var/mutable_appearance/damag_overlay
  388. var/mutable_appearance/sparks_overlay
  389. var/mutable_appearance/note_overlay
  390. var/notetype = note_type()
  391. switch(state)
  392. if(AIRLOCK_CLOSED)
  393. frame_overlay = get_airlock_overlay("closed", icon)
  394. if(airlock_material)
  395. filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
  396. else
  397. filling_overlay = get_airlock_overlay("fill_closed", icon)
  398. if(panel_open)
  399. if(security_level)
  400. panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
  401. else
  402. panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
  403. if(welded)
  404. weld_overlay = get_airlock_overlay("welded", overlays_file)
  405. if(obj_integrity < integrity_failure * max_integrity)
  406. damag_overlay = get_airlock_overlay("sparks_broken", overlays_file)
  407. else if(obj_integrity < (0.75 * max_integrity))
  408. damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file)
  409. if(lights && hasPower())
  410. if(locked)
  411. lights_overlay = get_airlock_overlay("lights_bolts", overlays_file)
  412. else if(emergency)
  413. lights_overlay = get_airlock_overlay("lights_emergency", overlays_file)
  414. if(note)
  415. note_overlay = get_airlock_overlay(notetype, note_overlay_file)
  416. if(AIRLOCK_DENY)
  417. if(!hasPower())
  418. return
  419. frame_overlay = get_airlock_overlay("closed", icon)
  420. if(airlock_material)
  421. filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
  422. else
  423. filling_overlay = get_airlock_overlay("fill_closed", icon)
  424. if(panel_open)
  425. if(security_level)
  426. panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
  427. else
  428. panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
  429. if(obj_integrity < integrity_failure * max_integrity)
  430. damag_overlay = get_airlock_overlay("sparks_broken", overlays_file)
  431. else if(obj_integrity < (0.75 * max_integrity))
  432. damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file)
  433. if(welded)
  434. weld_overlay = get_airlock_overlay("welded", overlays_file)
  435. lights_overlay = get_airlock_overlay("lights_denied", overlays_file)
  436. if(note)
  437. note_overlay = get_airlock_overlay(notetype, note_overlay_file)
  438. if(AIRLOCK_EMAG)
  439. frame_overlay = get_airlock_overlay("closed", icon)
  440. sparks_overlay = get_airlock_overlay("sparks", overlays_file)
  441. if(airlock_material)
  442. filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
  443. else
  444. filling_overlay = get_airlock_overlay("fill_closed", icon)
  445. if(panel_open)
  446. if(security_level)
  447. panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
  448. else
  449. panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
  450. if(obj_integrity < integrity_failure * max_integrity)
  451. damag_overlay = get_airlock_overlay("sparks_broken", overlays_file)
  452. else if(obj_integrity < (0.75 * max_integrity))
  453. damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file)
  454. if(welded)
  455. weld_overlay = get_airlock_overlay("welded", overlays_file)
  456. if(note)
  457. note_overlay = get_airlock_overlay(notetype, note_overlay_file)
  458. if(AIRLOCK_CLOSING)
  459. frame_overlay = get_airlock_overlay("closing", icon)
  460. if(airlock_material)
  461. filling_overlay = get_airlock_overlay("[airlock_material]_closing", overlays_file)
  462. else
  463. filling_overlay = get_airlock_overlay("fill_closing", icon)
  464. if(lights && hasPower())
  465. lights_overlay = get_airlock_overlay("lights_closing", overlays_file)
  466. if(panel_open)
  467. if(security_level)
  468. panel_overlay = get_airlock_overlay("panel_closing_protected", overlays_file)
  469. else
  470. panel_overlay = get_airlock_overlay("panel_closing", overlays_file)
  471. if(note)
  472. note_overlay = get_airlock_overlay("[notetype]_closing", note_overlay_file)
  473. if(AIRLOCK_OPEN)
  474. frame_overlay = get_airlock_overlay("open", icon)
  475. if(airlock_material)
  476. filling_overlay = get_airlock_overlay("[airlock_material]_open", overlays_file)
  477. else
  478. filling_overlay = get_airlock_overlay("fill_open", icon)
  479. if(panel_open)
  480. if(security_level)
  481. panel_overlay = get_airlock_overlay("panel_open_protected", overlays_file)
  482. else
  483. panel_overlay = get_airlock_overlay("panel_open", overlays_file)
  484. if(obj_integrity < (0.75 * max_integrity))
  485. damag_overlay = get_airlock_overlay("sparks_open", overlays_file)
  486. if(note)
  487. note_overlay = get_airlock_overlay("[notetype]_open", note_overlay_file)
  488. if(AIRLOCK_OPENING)
  489. frame_overlay = get_airlock_overlay("opening", icon)
  490. if(airlock_material)
  491. filling_overlay = get_airlock_overlay("[airlock_material]_opening", overlays_file)
  492. else
  493. filling_overlay = get_airlock_overlay("fill_opening", icon)
  494. if(lights && hasPower())
  495. lights_overlay = get_airlock_overlay("lights_opening", overlays_file)
  496. if(panel_open)
  497. if(security_level)
  498. panel_overlay = get_airlock_overlay("panel_opening_protected", overlays_file)
  499. else
  500. panel_overlay = get_airlock_overlay("panel_opening", overlays_file)
  501. if(note)
  502. note_overlay = get_airlock_overlay("[notetype]_opening", note_overlay_file)
  503. cut_overlays()
  504. add_overlay(frame_overlay)
  505. add_overlay(filling_overlay)
  506. add_overlay(lights_overlay)
  507. add_overlay(panel_overlay)
  508. add_overlay(weld_overlay)
  509. add_overlay(sparks_overlay)
  510. add_overlay(damag_overlay)
  511. add_overlay(note_overlay)
  512. check_unres()
  513. /proc/get_airlock_overlay(icon_state, icon_file)
  514. var/obj/machinery/door/airlock/A
  515. pass(A) //suppress unused warning
  516. var/list/airlock_overlays = A.airlock_overlays
  517. var/iconkey = "[icon_state][icon_file]"
  518. if((!(. = airlock_overlays[iconkey])))
  519. . = airlock_overlays[iconkey] = mutable_appearance(icon_file, icon_state)
  520. /obj/machinery/door/airlock/proc/check_unres() //unrestricted sides. This overlay indicates which directions the player can access even without an ID
  521. if(hasPower() && unres_sides)
  522. if(unres_sides & NORTH)
  523. var/image/I = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_n")
  524. I.pixel_y = 32
  525. set_light(l_range = 2, l_power = 1)
  526. add_overlay(I)
  527. if(unres_sides & SOUTH)
  528. var/image/I = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_s")
  529. I.pixel_y = -32
  530. set_light(l_range = 2, l_power = 1)
  531. add_overlay(I)
  532. if(unres_sides & EAST)
  533. var/image/I = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_e")
  534. I.pixel_x = 32
  535. set_light(l_range = 2, l_power = 1)
  536. add_overlay(I)
  537. if(unres_sides & WEST)
  538. var/image/I = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_w")
  539. I.pixel_x = -32
  540. set_light(l_range = 2, l_power = 1)
  541. add_overlay(I)
  542. else
  543. set_light(0)
  544. /obj/machinery/door/airlock/do_animate(animation)
  545. switch(animation)
  546. if("opening")
  547. update_icon(AIRLOCK_OPENING)
  548. if("closing")
  549. update_icon(AIRLOCK_CLOSING)
  550. if("deny")
  551. if(!machine_stat)
  552. update_icon(AIRLOCK_DENY)
  553. playsound(src,doorDeni,50,FALSE,3)
  554. sleep(6)
  555. update_icon(AIRLOCK_CLOSED)
  556. /obj/machinery/door/airlock/examine(mob/user)
  557. . = ..()
  558. if(obj_flags & EMAGGED)
  559. . += "<span class='warning'>Its access panel is smoking slightly.</span>"
  560. if(note)
  561. if(!in_range(user, src))
  562. . += "There's a [note.name] pinned to the front. You can't read it from here."
  563. else
  564. . += "There's a [note.name] pinned to the front..."
  565. . += note.examine(user)
  566. if(panel_open)
  567. switch(security_level)
  568. if(AIRLOCK_SECURITY_NONE)
  569. . += "Its wires are exposed!"
  570. if(AIRLOCK_SECURITY_METAL)
  571. . += "Its wires are hidden behind a welded metal cover."
  572. if(AIRLOCK_SECURITY_PLASTEEL_I_S)
  573. . += "There is some shredded plasteel inside."
  574. if(AIRLOCK_SECURITY_PLASTEEL_I)
  575. . += "Its wires are behind an inner layer of plasteel."
  576. if(AIRLOCK_SECURITY_PLASTEEL_O_S)
  577. . += "There is some shredded plasteel inside."
  578. if(AIRLOCK_SECURITY_PLASTEEL_O)
  579. . += "There is a welded plasteel cover hiding its wires."
  580. if(AIRLOCK_SECURITY_PLASTEEL)
  581. . += "There is a protective grille over its panel."
  582. else if(security_level)
  583. if(security_level == AIRLOCK_SECURITY_METAL)
  584. . += "It looks a bit stronger."
  585. else
  586. . += "It looks very robust."
  587. if(issilicon(user) && (!machine_stat & BROKEN))
  588. . += "<span class='notice'>Shift-click [src] to [ density ? "open" : "close"] it.</span>"
  589. . += "<span class='notice'>Ctrl-click [src] to [ locked ? "raise" : "drop"] its bolts.</span>"
  590. . += "<span class='notice'>Alt-click [src] to [ secondsElectrified ? "un-electrify" : "permanently electrify"] it.</span>"
  591. . += "<span class='notice'>Ctrl-Shift-click [src] to [ emergency ? "disable" : "enable"] emergency access.</span>"
  592. /obj/machinery/door/airlock/attack_ai(mob/user)
  593. if(!canAIControl(user))
  594. if(canAIHack())
  595. hack(user)
  596. return
  597. else
  598. to_chat(user, "<span class='warning'>Airlock AI control has been blocked with a firewall. Unable to hack.</span>")
  599. if(obj_flags & EMAGGED)
  600. to_chat(user, "<span class='warning'>Unable to interface: Airlock is unresponsive.</span>")
  601. return
  602. if(detonated)
  603. to_chat(user, "<span class='warning'>Unable to interface. Airlock control panel damaged.</span>")
  604. return
  605. ui_interact(user)
  606. /obj/machinery/door/airlock/proc/hack(mob/user)
  607. set waitfor = 0
  608. if(!aiHacking)
  609. aiHacking = TRUE
  610. to_chat(user, "<span class='warning'>Airlock AI control has been blocked. Beginning fault-detection.</span>")
  611. sleep(50)
  612. if(canAIControl(user))
  613. to_chat(user, "<span class='notice'>Alert cancelled. Airlock control has been restored without our assistance.</span>")
  614. aiHacking = FALSE
  615. return
  616. else if(!canAIHack())
  617. to_chat(user, "<span class='warning'>Connection lost! Unable to hack airlock.</span>")
  618. aiHacking = FALSE
  619. return
  620. to_chat(user, "<span class='notice'>Fault confirmed: airlock control wire disabled or cut.</span>")
  621. sleep(20)
  622. to_chat(user, "<span class='notice'>Attempting to hack into airlock. This may take some time.</span>")
  623. sleep(200)
  624. if(canAIControl(user))
  625. to_chat(user, "<span class='notice'>Alert cancelled. Airlock control has been restored without our assistance.</span>")
  626. aiHacking = FALSE
  627. return
  628. else if(!canAIHack())
  629. to_chat(user, "<span class='warning'>Connection lost! Unable to hack airlock.</span>")
  630. aiHacking = FALSE
  631. return
  632. to_chat(user, "<span class='notice'>Upload access confirmed. Loading control program into airlock software.</span>")
  633. sleep(170)
  634. if(canAIControl(user))
  635. to_chat(user, "<span class='notice'>Alert cancelled. Airlock control has been restored without our assistance.</span>")
  636. aiHacking = FALSE
  637. return
  638. else if(!canAIHack())
  639. to_chat(user, "<span class='warning'>Connection lost! Unable to hack airlock.</span>")
  640. aiHacking = FALSE
  641. return
  642. to_chat(user, "<span class='notice'>Transfer complete. Forcing airlock to execute program.</span>")
  643. sleep(50)
  644. //disable blocked control
  645. aiControlDisabled = 2
  646. to_chat(user, "<span class='notice'>Receiving control information from airlock.</span>")
  647. sleep(10)
  648. //bring up airlock dialog
  649. aiHacking = FALSE
  650. if(user)
  651. attack_ai(user)
  652. /obj/machinery/door/airlock/attack_animal(mob/user)
  653. . = ..()
  654. if(isElectrified())
  655. shock(user, 100)
  656. /obj/machinery/door/airlock/attack_paw(mob/user)
  657. return attack_hand(user)
  658. /obj/machinery/door/airlock/attack_hand(mob/user)
  659. . = ..()
  660. if(.)
  661. return
  662. if(!(issilicon(user) || IsAdminGhost(user)))
  663. if(isElectrified())
  664. if(shock(user, 100))
  665. return
  666. if(ishuman(user) && prob(40) && density)
  667. var/mob/living/carbon/human/H = user
  668. if((HAS_TRAIT(H, TRAIT_DUMB)) && Adjacent(user))
  669. playsound(src, 'sound/effects/bang.ogg', 25, TRUE)
  670. if(!istype(H.head, /obj/item/clothing/head/helmet))
  671. H.visible_message("<span class='danger'>[user] headbutts the airlock.</span>", \
  672. "<span class='userdanger'>You headbutt the airlock!</span>")
  673. H.Paralyze(100)
  674. H.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
  675. else
  676. visible_message("<span class='danger'>[user] headbutts the airlock. Good thing [user.p_theyre()] wearing a helmet.</span>")
  677. /obj/machinery/door/airlock/attempt_wire_interaction(mob/user)
  678. if(security_level)
  679. to_chat(user, "<span class='warning'>Wires are protected!</span>")
  680. return WIRE_INTERACTION_FAIL
  681. return ..()
  682. /obj/machinery/door/airlock/proc/electrified_loop()
  683. while (secondsElectrified > MACHINE_NOT_ELECTRIFIED)
  684. sleep(10)
  685. if(QDELETED(src))
  686. return
  687. secondsElectrified--
  688. updateDialog()
  689. // This is to protect against changing to permanent, mid loop.
  690. if(secondsElectrified == MACHINE_NOT_ELECTRIFIED)
  691. set_electrified(MACHINE_NOT_ELECTRIFIED)
  692. else
  693. set_electrified(MACHINE_ELECTRIFIED_PERMANENT)
  694. updateDialog()
  695. /obj/machinery/door/airlock/Topic(href, href_list, nowindow = 0)
  696. // If you add an if(..()) check you must first remove the var/nowindow parameter.
  697. // Otherwise it will runtime with this kind of error: null.Topic()
  698. if(!nowindow)
  699. ..()
  700. if(!usr.canUseTopic(src) && !IsAdminGhost(usr))
  701. return
  702. add_fingerprint(usr)
  703. if((in_range(src, usr) && isturf(loc)) && panel_open)
  704. usr.set_machine(src)
  705. add_fingerprint(usr)
  706. if(!nowindow)
  707. updateUsrDialog()
  708. else
  709. updateDialog()
  710. /obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
  711. if(!issilicon(user) && !IsAdminGhost(user))
  712. if(isElectrified())
  713. if(shock(user, 75))
  714. return
  715. add_fingerprint(user)
  716. if(panel_open)
  717. switch(security_level)
  718. if(AIRLOCK_SECURITY_NONE)
  719. if(istype(C, /obj/item/stack/sheet/metal))
  720. var/obj/item/stack/sheet/metal/S = C
  721. if(S.get_amount() < 2)
  722. to_chat(user, "<span class='warning'>You need at least 2 metal sheets to reinforce [src].</span>")
  723. return
  724. to_chat(user, "<span class='notice'>You start reinforcing [src].</span>")
  725. if(do_after(user, 20, TRUE, src))
  726. if(!panel_open || !S.use(2))
  727. return
  728. user.visible_message("<span class='notice'>[user] reinforces \the [src] with metal.</span>",
  729. "<span class='notice'>You reinforce \the [src] with metal.</span>")
  730. security_level = AIRLOCK_SECURITY_METAL
  731. update_icon()
  732. return
  733. else if(istype(C, /obj/item/stack/sheet/plasteel))
  734. var/obj/item/stack/sheet/plasteel/S = C
  735. if(S.get_amount() < 2)
  736. to_chat(user, "<span class='warning'>You need at least 2 plasteel sheets to reinforce [src].</span>")
  737. return
  738. to_chat(user, "<span class='notice'>You start reinforcing [src].</span>")
  739. if(do_after(user, 20, TRUE, src))
  740. if(!panel_open || !S.use(2))
  741. return
  742. user.visible_message("<span class='notice'>[user] reinforces \the [src] with plasteel.</span>",
  743. "<span class='notice'>You reinforce \the [src] with plasteel.</span>")
  744. security_level = AIRLOCK_SECURITY_PLASTEEL
  745. modify_max_integrity(normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER)
  746. damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
  747. update_icon()
  748. return
  749. if(AIRLOCK_SECURITY_METAL)
  750. if(C.tool_behaviour == TOOL_WELDER)
  751. if(!C.tool_start_check(user, amount=2))
  752. return
  753. to_chat(user, "<span class='notice'>You begin cutting the panel's shielding...</span>")
  754. if(C.use_tool(src, user, 40, volume=50, amount = 2))
  755. if(!panel_open)
  756. return
  757. user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
  758. "<span class='notice'>You cut through \the [src]'s shielding.</span>",
  759. "<span class='hear'>You hear welding.</span>")
  760. security_level = AIRLOCK_SECURITY_NONE
  761. spawn_atom_to_turf(/obj/item/stack/sheet/metal, user.loc, 2)
  762. update_icon()
  763. return
  764. if(AIRLOCK_SECURITY_PLASTEEL_I_S)
  765. if(C.tool_behaviour == TOOL_CROWBAR)
  766. var/obj/item/crowbar/W = C
  767. to_chat(user, "<span class='notice'>You start removing the inner layer of shielding...</span>")
  768. if(W.use_tool(src, user, 40, volume=100))
  769. if(!panel_open)
  770. return
  771. if(security_level != AIRLOCK_SECURITY_PLASTEEL_I_S)
  772. return
  773. user.visible_message("<span class='notice'>[user] remove \the [src]'s shielding.</span>",
  774. "<span class='notice'>You remove \the [src]'s inner shielding.</span>")
  775. security_level = AIRLOCK_SECURITY_NONE
  776. modify_max_integrity(normal_integrity)
  777. damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
  778. spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
  779. update_icon()
  780. return
  781. if(AIRLOCK_SECURITY_PLASTEEL_I)
  782. if(C.tool_behaviour == TOOL_WELDER)
  783. if(!C.tool_start_check(user, amount=2))
  784. return
  785. to_chat(user, "<span class='notice'>You begin cutting the inner layer of shielding...</span>")
  786. if(C.use_tool(src, user, 40, volume=50, amount=2))
  787. if(!panel_open)
  788. return
  789. user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
  790. "<span class='notice'>You cut through \the [src]'s shielding.</span>",
  791. "<span class='hear'>You hear welding.</span>")
  792. security_level = AIRLOCK_SECURITY_PLASTEEL_I_S
  793. return
  794. if(AIRLOCK_SECURITY_PLASTEEL_O_S)
  795. if(C.tool_behaviour == TOOL_CROWBAR)
  796. to_chat(user, "<span class='notice'>You start removing outer layer of shielding...</span>")
  797. if(C.use_tool(src, user, 40, volume=100))
  798. if(!panel_open)
  799. return
  800. if(security_level != AIRLOCK_SECURITY_PLASTEEL_O_S)
  801. return
  802. user.visible_message("<span class='notice'>[user] remove \the [src]'s shielding.</span>",
  803. "<span class='notice'>You remove \the [src]'s shielding.</span>")
  804. security_level = AIRLOCK_SECURITY_PLASTEEL_I
  805. spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
  806. return
  807. if(AIRLOCK_SECURITY_PLASTEEL_O)
  808. if(C.tool_behaviour == TOOL_WELDER)
  809. if(!C.tool_start_check(user, amount=2))
  810. return
  811. to_chat(user, "<span class='notice'>You begin cutting the outer layer of shielding...</span>")
  812. if(C.use_tool(src, user, 40, volume=50, amount=2))
  813. if(!panel_open)
  814. return
  815. user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
  816. "<span class='notice'>You cut through \the [src]'s shielding.</span>",
  817. "<span class='hear'>You hear welding.</span>")
  818. security_level = AIRLOCK_SECURITY_PLASTEEL_O_S
  819. return
  820. if(AIRLOCK_SECURITY_PLASTEEL)
  821. if(C.tool_behaviour == TOOL_WIRECUTTER)
  822. if(hasPower() && shock(user, 60)) // Protective grille of wiring is electrified
  823. return
  824. to_chat(user, "<span class='notice'>You start cutting through the outer grille.</span>")
  825. if(C.use_tool(src, user, 10, volume=100))
  826. if(!panel_open)
  827. return
  828. user.visible_message("<span class='notice'>[user] cut through \the [src]'s outer grille.</span>",
  829. "<span class='notice'>You cut through \the [src]'s outer grille.</span>")
  830. security_level = AIRLOCK_SECURITY_PLASTEEL_O
  831. return
  832. if(C.tool_behaviour == TOOL_SCREWDRIVER)
  833. if(panel_open && detonated)
  834. to_chat(user, "<span class='warning'>[src] has no maintenance panel!</span>")
  835. return
  836. panel_open = !panel_open
  837. to_chat(user, "<span class='notice'>You [panel_open ? "open":"close"] the maintenance panel of the airlock.</span>")
  838. C.play_tool_sound(src)
  839. update_icon()
  840. else if((C.tool_behaviour == TOOL_WIRECUTTER) && note)
  841. user.visible_message("<span class='notice'>[user] cuts down [note] from [src].</span>", "<span class='notice'>You remove [note] from [src].</span>")
  842. C.play_tool_sound(src)
  843. note.forceMove(get_turf(user))
  844. note = null
  845. update_icon()
  846. else if(is_wire_tool(C) && panel_open)
  847. attempt_wire_interaction(user)
  848. return
  849. else if(istype(C, /obj/item/pai_cable))
  850. var/obj/item/pai_cable/cable = C
  851. cable.plugin(src, user)
  852. else if(istype(C, /obj/item/airlock_painter))
  853. change_paintjob(C, user)
  854. else if(istype(C, /obj/item/paper) || istype(C, /obj/item/photo))
  855. if(note)
  856. to_chat(user, "<span class='warning'>There's already something pinned to this airlock! Use wirecutters to remove it.</span>")
  857. return
  858. if(!user.transferItemToLoc(C, src))
  859. to_chat(user, "<span class='warning'>For some reason, you can't attach [C]!</span>")
  860. return
  861. user.visible_message("<span class='notice'>[user] pins [C] to [src].</span>", "<span class='notice'>You pin [C] to [src].</span>")
  862. note = C
  863. update_icon()
  864. else
  865. return ..()
  866. /obj/machinery/door/airlock/try_to_weld(obj/item/weldingtool/W, mob/user)
  867. if(!operating && density)
  868. if(user.a_intent != INTENT_HELP)
  869. if(!W.tool_start_check(user, amount=0))
  870. return
  871. user.visible_message("<span class='notice'>[user] begins [welded ? "unwelding":"welding"] the airlock.</span>", \
  872. "<span class='notice'>You begin [welded ? "unwelding":"welding"] the airlock...</span>", \
  873. "<span class='hear'>You hear welding.</span>")
  874. if(W.use_tool(src, user, 40, volume=50, extra_checks = CALLBACK(src, .proc/weld_checks, W, user)))
  875. welded = !welded
  876. user.visible_message("<span class='notice'>[user] [welded? "welds shut":"unwelds"] [src].</span>", \
  877. "<span class='notice'>You [welded ? "weld the airlock shut":"unweld the airlock"].</span>")
  878. update_icon()
  879. else
  880. if(obj_integrity < max_integrity)
  881. if(!W.tool_start_check(user, amount=0))
  882. return
  883. user.visible_message("<span class='notice'>[user] begins welding the airlock.</span>", \
  884. "<span class='notice'>You begin repairing the airlock...</span>", \
  885. "<span class='hear'>You hear welding.</span>")
  886. if(W.use_tool(src, user, 40, volume=50, extra_checks = CALLBACK(src, .proc/weld_checks, W, user)))
  887. obj_integrity = max_integrity
  888. machine_stat &= ~BROKEN
  889. user.visible_message("<span class='notice'>[user] finishes welding [src].</span>", \
  890. "<span class='notice'>You finish repairing the airlock.</span>")
  891. update_icon()
  892. else
  893. to_chat(user, "<span class='notice'>The airlock doesn't need repairing.</span>")
  894. /obj/machinery/door/airlock/proc/weld_checks(obj/item/weldingtool/W, mob/user)
  895. return !operating && density
  896. /obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user, forced = FALSE)
  897. if(I)
  898. var/beingcrowbarred = (I.tool_behaviour == TOOL_CROWBAR)
  899. if(!security_level && (beingcrowbarred && panel_open && ((obj_flags & EMAGGED) || (density && welded && !operating && !hasPower() && !locked))))
  900. user.visible_message("<span class='notice'>[user] removes the electronics from the airlock assembly.</span>", \
  901. "<span class='notice'>You start to remove electronics from the airlock assembly...</span>")
  902. if(I.use_tool(src, user, 40, volume=100))
  903. deconstruct(TRUE, user)
  904. return
  905. if(locked)
  906. to_chat(user, "<span class='warning'>The airlock's bolts prevent it from being forced!</span>")
  907. return
  908. if(welded)
  909. to_chat(user, "<span class='warning'>It's welded, it won't budge!</span>")
  910. return
  911. if(hasPower())
  912. if(forced)
  913. if(isElectrified())
  914. shock(user,100)//it's like sticking a forck in a power socket
  915. return
  916. if(!density)//already open
  917. return
  918. if(!prying_so_hard)
  919. var/time_to_open = 50
  920. playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE) //is it aliens or just the CE being a dick?
  921. prying_so_hard = TRUE
  922. if(do_after(user, time_to_open, TRUE, src))
  923. open(2)
  924. if(density && !open(2))
  925. to_chat(user, "<span class='warning'>Despite your attempts, [src] refuses to open.</span>")
  926. prying_so_hard = FALSE
  927. return
  928. to_chat(user, "<span class='warning'>The airlock's motors resist your efforts to force it!</span>")
  929. return
  930. if(!operating)
  931. if(istype(I, /obj/item/fireaxe)) //being fireaxe'd
  932. var/obj/item/fireaxe/axe = I
  933. if(axe && !axe.wielded)
  934. to_chat(user, "<span class='warning'>You need to be wielding \the [axe] to do that!</span>")
  935. return
  936. INVOKE_ASYNC(src, (density ? .proc/open : .proc/close), 2)
  937. /obj/machinery/door/airlock/open(forced=0)
  938. if( operating || welded || locked )
  939. return FALSE
  940. if(!forced)
  941. if(!hasPower() || wires.is_cut(WIRE_OPEN))
  942. return FALSE
  943. if(forced < 2)
  944. if(obj_flags & EMAGGED)
  945. return FALSE
  946. use_power(50)
  947. playsound(src, doorOpen, 30, TRUE)
  948. if(closeOther != null && istype(closeOther, /obj/machinery/door/airlock/) && !closeOther.density)
  949. closeOther.close()
  950. else
  951. playsound(src, 'sound/machines/airlockforced.ogg', 30, TRUE)
  952. if(autoclose)
  953. autoclose_in(normalspeed ? 150 : 15)
  954. if(!density)
  955. return TRUE
  956. operating = TRUE
  957. update_icon(AIRLOCK_OPENING, 1)
  958. sleep(1)
  959. set_opacity(0)
  960. update_freelook_sight()
  961. sleep(4)
  962. density = FALSE
  963. flags_1 &= ~PREVENT_CLICK_UNDER_1
  964. air_update_turf(1)
  965. sleep(1)
  966. layer = OPEN_DOOR_LAYER
  967. update_icon(AIRLOCK_OPEN, 1)
  968. operating = FALSE
  969. if(delayed_close_requested)
  970. delayed_close_requested = FALSE
  971. addtimer(CALLBACK(src, .proc/close), 1)
  972. return TRUE
  973. /obj/machinery/door/airlock/close(forced=0)
  974. if(operating || welded || locked)
  975. return
  976. if(density)
  977. return TRUE
  978. if(!forced)
  979. if(!hasPower() || wires.is_cut(WIRE_BOLTS))
  980. return
  981. if(safe)
  982. for(var/atom/movable/M in get_turf(src))
  983. if(M.density && M != src) //something is blocking the door
  984. autoclose_in(60)
  985. return
  986. if(forced < 2)
  987. if(obj_flags & EMAGGED)
  988. return
  989. use_power(50)
  990. playsound(src, doorClose, 30, TRUE)
  991. else
  992. playsound(src, 'sound/machines/airlockforced.ogg', 30, TRUE)
  993. var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src))
  994. if(killthis)
  995. killthis.ex_act(EXPLODE_HEAVY)//Smashin windows
  996. operating = TRUE
  997. update_icon(AIRLOCK_CLOSING, 1)
  998. layer = CLOSED_DOOR_LAYER
  999. if(air_tight)
  1000. density = TRUE
  1001. flags_1 |= PREVENT_CLICK_UNDER_1
  1002. air_update_turf(1)
  1003. sleep(1)
  1004. if(!air_tight)
  1005. density = TRUE
  1006. flags_1 |= PREVENT_CLICK_UNDER_1
  1007. air_update_turf(1)
  1008. sleep(4)
  1009. if(!safe)
  1010. crush()
  1011. if(visible && !glass)
  1012. set_opacity(1)
  1013. update_freelook_sight()
  1014. sleep(1)
  1015. update_icon(AIRLOCK_CLOSED, 1)
  1016. operating = FALSE
  1017. delayed_close_requested = FALSE
  1018. if(safe)
  1019. CheckForMobs()
  1020. return TRUE
  1021. /obj/machinery/door/airlock/proc/prison_open()
  1022. if(obj_flags & EMAGGED)
  1023. return
  1024. locked = FALSE
  1025. open()
  1026. locked = TRUE
  1027. return
  1028. // gets called when a player uses an airlock painter on this airlock
  1029. /obj/machinery/door/airlock/proc/change_paintjob(obj/item/airlock_painter/painter, mob/user)
  1030. 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
  1031. return
  1032. // reads from the airlock painter's `available paintjob` list. lets the player choose a paint option, or cancel painting
  1033. var/current_paintjob = input(user, "Please select a paintjob for this airlock.") as null|anything in sortList(painter.available_paint_jobs)
  1034. if(!current_paintjob) // if the user clicked cancel on the popup, return
  1035. return
  1036. var/airlock_type = painter.available_paint_jobs["[current_paintjob]"] // get the airlock type path associated with the airlock name the user just chose
  1037. var/obj/machinery/door/airlock/airlock = new airlock_type // we need to create an new instance of the airlock and assembly to read vars from them
  1038. var/obj/structure/door_assembly/assembly = new airlock.assemblytype
  1039. if(airlock_material == "glass" && assembly.noglass) // prevents painting glass airlocks with a paint job that doesn't have a glass version, such as the freezer
  1040. to_chat(user, "<span class='warning'>This paint job can only be applied to non-glass airlocks.</span>")
  1041. else
  1042. // applies the user-chosen airlock's icon, overlays and assemblytype to the src airlock
  1043. painter.use_paint(user)
  1044. icon = airlock.icon
  1045. overlays_file = airlock.overlays_file
  1046. assemblytype = airlock.assemblytype
  1047. update_icon()
  1048. // these are just hanging around but are never placed, we need to delete them
  1049. qdel(airlock)
  1050. qdel(assembly)
  1051. /obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID)
  1052. //Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off)
  1053. return !density || (check_access(ID) && !locked && hasPower())
  1054. /obj/machinery/door/airlock/emag_act(mob/user)
  1055. if(!operating && density && hasPower() && !(obj_flags & EMAGGED))
  1056. operating = TRUE
  1057. update_icon(AIRLOCK_EMAG, 1)
  1058. sleep(6)
  1059. if(QDELETED(src))
  1060. return
  1061. operating = FALSE
  1062. if(!open())
  1063. update_icon(AIRLOCK_CLOSED, 1)
  1064. obj_flags |= EMAGGED
  1065. lights = FALSE
  1066. locked = TRUE
  1067. loseMainPower()
  1068. loseBackupPower()
  1069. /obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/humanoid/user)
  1070. if(isElectrified())
  1071. add_fingerprint(user)
  1072. shock(user, 100) //Mmm, fried xeno!
  1073. return
  1074. if(!density) //Already open
  1075. return ..()
  1076. if(locked || welded) //Extremely generic, as aliens only understand the basics of how airlocks work.
  1077. if(user.a_intent == INTENT_HARM)
  1078. return ..()
  1079. to_chat(user, "<span class='warning'>[src] refuses to budge!</span>")
  1080. return
  1081. add_fingerprint(user)
  1082. user.visible_message("<span class='warning'>[user] begins prying open [src].</span>",\
  1083. "<span class='noticealien'>You begin digging your claws into [src] with all your might!</span>",\
  1084. "<span class='warning'>You hear groaning metal...</span>")
  1085. var/time_to_open = 5 //half a second
  1086. if(hasPower())
  1087. time_to_open = 5 SECONDS //Powered airlocks take longer to open, and are loud.
  1088. playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
  1089. if(do_after(user, time_to_open, TRUE, src))
  1090. if(density && !open(2)) //The airlock is still closed, but something prevented it opening. (Another player noticed and bolted/welded the airlock in time!)
  1091. to_chat(user, "<span class='warning'>Despite your efforts, [src] managed to resist your attempts to open it!</span>")
  1092. /obj/machinery/door/airlock/hostile_lockdown(mob/origin)
  1093. // Must be powered and have working AI wire.
  1094. if(canAIControl(src) && !machine_stat)
  1095. locked = FALSE //For airlocks that were bolted open.
  1096. safe = FALSE //DOOR CRUSH
  1097. close()
  1098. bolt() //Bolt it!
  1099. set_electrified(MACHINE_ELECTRIFIED_PERMANENT) //Shock it!
  1100. if(origin)
  1101. LAZYADD(shockedby, "\[[time_stamp()]\] [key_name(origin)]")
  1102. /obj/machinery/door/airlock/disable_lockdown()
  1103. // Must be powered and have working AI wire.
  1104. if(canAIControl(src) && !machine_stat)
  1105. unbolt()
  1106. set_electrified(MACHINE_NOT_ELECTRIFIED)
  1107. open()
  1108. safe = TRUE
  1109. /obj/machinery/door/airlock/proc/on_break()
  1110. if(!panel_open)
  1111. panel_open = TRUE
  1112. wires.cut_all()
  1113. /obj/machinery/door/airlock/proc/set_electrified(seconds, mob/user)
  1114. secondsElectrified = seconds
  1115. diag_hud_set_electrified()
  1116. if(secondsElectrified > MACHINE_NOT_ELECTRIFIED)
  1117. INVOKE_ASYNC(src, .proc/electrified_loop)
  1118. if(user)
  1119. var/message
  1120. switch(secondsElectrified)
  1121. if(MACHINE_ELECTRIFIED_PERMANENT)
  1122. message = "permanently shocked"
  1123. if(MACHINE_NOT_ELECTRIFIED)
  1124. message = "unshocked"
  1125. else
  1126. message = "temp shocked for [secondsElectrified] seconds"
  1127. LAZYADD(shockedby, text("\[[time_stamp()]\] [key_name(user)] - ([uppertext(message)])"))
  1128. log_combat(user, src, message)
  1129. add_hiddenprint(user)
  1130. /obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
  1131. . = ..()
  1132. if(obj_integrity < (0.75 * max_integrity))
  1133. update_icon()
  1134. /obj/machinery/door/airlock/deconstruct(disassembled = TRUE, mob/user)
  1135. if(!(flags_1 & NODECONSTRUCT_1))
  1136. var/obj/structure/door_assembly/A
  1137. if(assemblytype)
  1138. A = new assemblytype(loc)
  1139. else
  1140. A = new /obj/structure/door_assembly(loc)
  1141. //If you come across a null assemblytype, it will produce the default assembly instead of disintegrating.
  1142. A.heat_proof_finished = heat_proof //tracks whether there's rglass in
  1143. A.setAnchored(TRUE)
  1144. A.glass = glass
  1145. A.state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
  1146. A.created_name = name
  1147. A.previous_assembly = previous_airlock
  1148. A.update_name()
  1149. A.update_icon()
  1150. if(!disassembled)
  1151. if(A)
  1152. A.obj_integrity = A.max_integrity * 0.5
  1153. else if(obj_flags & EMAGGED)
  1154. if(user)
  1155. to_chat(user, "<span class='warning'>You discard the damaged electronics.</span>")
  1156. else
  1157. if(user)
  1158. to_chat(user, "<span class='notice'>You remove the airlock electronics.</span>")
  1159. var/obj/item/electronics/airlock/ae
  1160. if(!electronics)
  1161. ae = new/obj/item/electronics/airlock(loc)
  1162. gen_access()
  1163. if(req_one_access.len)
  1164. ae.one_access = 1
  1165. ae.accesses = req_one_access
  1166. else
  1167. ae.accesses = req_access
  1168. else
  1169. ae = electronics
  1170. electronics = null
  1171. ae.forceMove(drop_location())
  1172. qdel(src)
  1173. /obj/machinery/door/airlock/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
  1174. switch(the_rcd.mode)
  1175. if(RCD_DECONSTRUCT)
  1176. if(security_level != AIRLOCK_SECURITY_NONE)
  1177. to_chat(user, "<span class='notice'>[src]'s reinforcement needs to be removed first.</span>")
  1178. return FALSE
  1179. return list("mode" = RCD_DECONSTRUCT, "delay" = 50, "cost" = 32)
  1180. return FALSE
  1181. /obj/machinery/door/airlock/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
  1182. switch(passed_mode)
  1183. if(RCD_DECONSTRUCT)
  1184. to_chat(user, "<span class='notice'>You deconstruct the airlock.</span>")
  1185. qdel(src)
  1186. return TRUE
  1187. return FALSE
  1188. /obj/machinery/door/airlock/proc/note_type() //Returns a string representing the type of note pinned to this airlock
  1189. if(!note)
  1190. return
  1191. else if(istype(note, /obj/item/paper))
  1192. return "note"
  1193. else if(istype(note, /obj/item/photo))
  1194. return "photo"
  1195. /obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
  1196. datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
  1197. ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
  1198. if(!ui)
  1199. ui = new(user, src, ui_key, "ai_airlock", name, 500, 390, master_ui, state)
  1200. ui.open()
  1201. return TRUE
  1202. /obj/machinery/door/airlock/ui_data()
  1203. var/list/data = list()
  1204. var/list/power = list()
  1205. power["main"] = secondsMainPowerLost ? 0 : 2 // boolean
  1206. power["main_timeleft"] = secondsMainPowerLost
  1207. power["backup"] = secondsBackupPowerLost ? 0 : 2 // boolean
  1208. power["backup_timeleft"] = secondsBackupPowerLost
  1209. data["power"] = power
  1210. data["shock"] = secondsElectrified == MACHINE_NOT_ELECTRIFIED ? 2 : 0
  1211. data["shock_timeleft"] = secondsElectrified
  1212. data["id_scanner"] = !aiDisabledIdScanner
  1213. data["emergency"] = emergency // access
  1214. data["locked"] = locked // bolted
  1215. data["lights"] = lights // bolt lights
  1216. data["safe"] = safe // safeties
  1217. data["speed"] = normalspeed // safe speed
  1218. data["welded"] = welded // welded
  1219. data["opened"] = !density // opened
  1220. var/list/wire = list()
  1221. wire["main_1"] = !wires.is_cut(WIRE_POWER1)
  1222. wire["main_2"] = !wires.is_cut(WIRE_POWER2)
  1223. wire["backup_1"] = !wires.is_cut(WIRE_BACKUP1)
  1224. wire["backup_2"] = !wires.is_cut(WIRE_BACKUP2)
  1225. wire["shock"] = !wires.is_cut(WIRE_SHOCK)
  1226. wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN)
  1227. wire["bolts"] = !wires.is_cut(WIRE_BOLTS)
  1228. wire["lights"] = !wires.is_cut(WIRE_LIGHT)
  1229. wire["safe"] = !wires.is_cut(WIRE_SAFETY)
  1230. wire["timing"] = !wires.is_cut(WIRE_TIMING)
  1231. data["wires"] = wire
  1232. return data
  1233. /obj/machinery/door/airlock/ui_act(action, params)
  1234. if(..())
  1235. return
  1236. if(!user_allowed(usr))
  1237. return
  1238. switch(action)
  1239. if("disrupt-main")
  1240. if(!secondsMainPowerLost)
  1241. loseMainPower()
  1242. update_icon()
  1243. else
  1244. to_chat(usr, "<span class='warning'>Main power is already offline.</span>")
  1245. . = TRUE
  1246. if("disrupt-backup")
  1247. if(!secondsBackupPowerLost)
  1248. loseBackupPower()
  1249. update_icon()
  1250. else
  1251. to_chat(usr, "<span class='warning'>Backup power is already offline.</span>")
  1252. . = TRUE
  1253. if("shock-restore")
  1254. shock_restore(usr)
  1255. . = TRUE
  1256. if("shock-temp")
  1257. shock_temp(usr)
  1258. . = TRUE
  1259. if("shock-perm")
  1260. shock_perm(usr)
  1261. . = TRUE
  1262. if("idscan-toggle")
  1263. aiDisabledIdScanner = !aiDisabledIdScanner
  1264. . = TRUE
  1265. if("emergency-toggle")
  1266. toggle_emergency(usr)
  1267. . = TRUE
  1268. if("bolt-toggle")
  1269. toggle_bolt(usr)
  1270. . = TRUE
  1271. if("light-toggle")
  1272. lights = !lights
  1273. update_icon()
  1274. . = TRUE
  1275. if("safe-toggle")
  1276. safe = !safe
  1277. . = TRUE
  1278. if("speed-toggle")
  1279. normalspeed = !normalspeed
  1280. . = TRUE
  1281. if("open-close")
  1282. user_toggle_open(usr)
  1283. . = TRUE
  1284. /obj/machinery/door/airlock/proc/user_allowed(mob/user)
  1285. return (issilicon(user) && canAIControl(user)) || IsAdminGhost(user)
  1286. /obj/machinery/door/airlock/proc/shock_restore(mob/user)
  1287. if(!user_allowed(user))
  1288. return
  1289. if(wires.is_cut(WIRE_SHOCK))
  1290. to_chat(user, "<span class='warning'>Can't un-electrify the airlock - The electrification wire is cut.</span>")
  1291. else if(isElectrified())
  1292. set_electrified(MACHINE_NOT_ELECTRIFIED, user)
  1293. /obj/machinery/door/airlock/proc/shock_temp(mob/user)
  1294. if(!user_allowed(user))
  1295. return
  1296. if(wires.is_cut(WIRE_SHOCK))
  1297. to_chat(user, "<span class='warning'>The electrification wire has been cut.</span>")
  1298. else
  1299. set_electrified(MACHINE_DEFAULT_ELECTRIFY_TIME, user)
  1300. /obj/machinery/door/airlock/proc/shock_perm(mob/user)
  1301. if(!user_allowed(user))
  1302. return
  1303. if(wires.is_cut(WIRE_SHOCK))
  1304. to_chat(user, "<span class='warning'>The electrification wire has been cut.</span>")
  1305. else
  1306. set_electrified(MACHINE_ELECTRIFIED_PERMANENT, user)
  1307. /obj/machinery/door/airlock/proc/toggle_bolt(mob/user)
  1308. if(!user_allowed(user))
  1309. return
  1310. if(wires.is_cut(WIRE_BOLTS))
  1311. to_chat(user, "<span class='warning'>The door bolt drop wire is cut - you can't toggle the door bolts.</span>")
  1312. return
  1313. if(locked)
  1314. if(!hasPower())
  1315. to_chat(user, "<span class='warning'>The door has no power - you can't raise the door bolts.</span>")
  1316. else
  1317. unbolt()
  1318. log_combat(user, src, "unbolted")
  1319. else
  1320. bolt()
  1321. log_combat(user, src, "bolted")
  1322. /obj/machinery/door/airlock/proc/toggle_emergency(mob/user)
  1323. if(!user_allowed(user))
  1324. return
  1325. emergency = !emergency
  1326. update_icon()
  1327. /obj/machinery/door/airlock/proc/user_toggle_open(mob/user)
  1328. if(!user_allowed(user))
  1329. return
  1330. if(welded)
  1331. to_chat(user, text("<span class='warning'>The airlock has been welded shut!</span>"))
  1332. else if(locked)
  1333. to_chat(user, text("<span class='warning'>The door bolts are down!</span>"))
  1334. else if(!density)
  1335. close()
  1336. else
  1337. open()
  1338. #undef AIRLOCK_CLOSED
  1339. #undef AIRLOCK_CLOSING
  1340. #undef AIRLOCK_OPEN
  1341. #undef AIRLOCK_OPENING
  1342. #undef AIRLOCK_DENY
  1343. #undef AIRLOCK_EMAG
  1344. #undef AIRLOCK_SECURITY_NONE
  1345. #undef AIRLOCK_SECURITY_METAL
  1346. #undef AIRLOCK_SECURITY_PLASTEEL_I_S
  1347. #undef AIRLOCK_SECURITY_PLASTEEL_I
  1348. #undef AIRLOCK_SECURITY_PLASTEEL_O_S
  1349. #undef AIRLOCK_SECURITY_PLASTEEL_O
  1350. #undef AIRLOCK_SECURITY_PLASTEEL
  1351. #undef AIRLOCK_INTEGRITY_N
  1352. #undef AIRLOCK_INTEGRITY_MULTIPLIER
  1353. #undef AIRLOCK_DAMAGE_DEFLECTION_N
  1354. #undef AIRLOCK_DAMAGE_DEFLECTION_R