diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 3a8662dedee..6e4d49aad9f 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -126,3 +126,8 @@ #define AI_DISPLAY_MODE_BLANK 0 #define AI_DISPLAY_MODE_EMOTE 1 #define AI_DISPLAY_MODE_BSOD 2 + +// Door operations +#define DOOR_OPENING 1 +#define DOOR_CLOSING 2 +#define DOOR_MALF 3 diff --git a/code/__HELPERS/lighting.dm b/code/__HELPERS/lighting.dm index fcba21227ab..55a0e2e53ec 100644 --- a/code/__HELPERS/lighting.dm +++ b/code/__HELPERS/lighting.dm @@ -1,4 +1,4 @@ -/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR]. +/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR]. Order of application matters: Default generated blockers are overlays, and will block its own emissive underlays. If you want an object to be both a blocker, and have their own emissive, it has to be an overlay instead. Grayscale lightmasks are visible in the BYOND right-click and Examine pane, unless they're covered up by another overlay. /proc/emissive_appearance(icon, icon_state = "", layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE) var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS) appearance.color = EMISSIVE_COLOR diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 2519224062e..94e81f2d785 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -146,29 +146,34 @@ return cameras /area/proc/air_doors_close() - if(!air_doors_activated) - air_doors_activated = TRUE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.activate_alarm() - if(D.operating) - D.nextstate = FD_CLOSED - else if(!D.density) - spawn(0) - D.close() + if(air_doors_activated) + return + air_doors_activated = TRUE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.is_operational()) + continue + D.activate_alarm() + if(D.welded) + continue + if(D.operating && D.operating != DOOR_CLOSING) + D.nextstate = FD_CLOSED + else if(!D.density) + INVOKE_ASYNC(D, /obj/machinery/door/firedoor.proc/close) /area/proc/air_doors_open() - if(air_doors_activated) - air_doors_activated = FALSE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.deactivate_alarm() - if(D.operating) - D.nextstate = OPEN - else if(D.density) - spawn(0) - D.open() - + if(!air_doors_activated) + return + air_doors_activated = FALSE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.is_operational()) + continue + D.deactivate_alarm() + if(D.welded) + continue + if(D.operating && D.operating != DOOR_OPENING) + D.nextstate = FD_OPEN + else if(D.density) + INVOKE_ASYNC(D, /obj/machinery/door/firedoor.proc/open) /area/Destroy() STOP_PROCESSING(SSobj, src) @@ -229,23 +234,34 @@ * Try to close all the firedoors in the area */ /area/proc/ModifyFiredoors(opening) - if(firedoors) - firedoors_last_closed_on = world.time - for(var/FD in firedoors) - var/obj/machinery/door/firedoor/D = FD - var/cont = !D.welded - if(cont && opening) //don't open if adjacent area is on fire - for(var/I in D.affecting_areas) - var/area/A = I - if(A.fire) - cont = FALSE - break - if(cont && D.is_operational()) - if(D.operating) - D.nextstate = opening ? FD_OPEN : FD_CLOSED - else if(!(D.density ^ opening)) - INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/open : /obj/machinery/door/firedoor.proc/close)) - INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/deactivate_alarm : /obj/machinery/door/firedoor.proc/activate_alarm)) + if(!firedoors) + return + firedoors_last_closed_on = world.time + for(var/obj/machinery/door/firedoor/D in firedoors) + if(!D.is_operational()) + continue + var/valid = TRUE + if(opening) //don't open if adjacent area is on fire + for(var/I in D.affecting_areas) + var/area/A = I + if(A.fire) + valid = FALSE + break + if(!valid) + continue + + // At this point, the area is safe and the door is technically functional. + + INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/deactivate_alarm : /obj/machinery/door/firedoor.proc/activate_alarm)) + if(D.welded) + continue // Alarm is toggled, but door stuck + if(D.operating) + if((D.operating == DOOR_OPENING && opening) || (D.operating == DOOR_CLOSING && !opening)) + continue + else + D.nextstate = opening ? FD_OPEN : FD_CLOSED + else if(D.density == opening) + INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/open : /obj/machinery/door/firedoor.proc/close)) /** * Generate a firealarm alert for this area diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 644c6c9ae3b..137e19562c7 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1233,7 +1233,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) if(!density) return TRUE SEND_SIGNAL(src, COMSIG_AIRLOCK_OPEN) - operating = TRUE + operating = DOOR_OPENING update_icon(AIRLOCK_OPENING, 1) sleep(1) set_opacity(0) @@ -1244,7 +1244,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) sleep(1) layer = OPEN_DOOR_LAYER update_icon(AIRLOCK_OPEN, 1) - operating = FALSE + operating = NONE return TRUE /obj/machinery/door/airlock/close(forced=0, override = 0) @@ -1274,7 +1274,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) killthis.ex_act(EXPLODE_HEAVY)//Smashin windows SEND_SIGNAL(src, COMSIG_AIRLOCK_CLOSE) - operating = TRUE + operating = DOOR_CLOSING update_icon(AIRLOCK_CLOSING, 1) layer = CLOSED_DOOR_LAYER if(!override) @@ -1290,7 +1290,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) update_freelook_sight() sleep(1) update_icon(AIRLOCK_CLOSED, 1) - operating = FALSE + operating = NONE if(safe) CheckForMobs() return TRUE @@ -1326,13 +1326,13 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) /obj/machinery/door/airlock/emag_act(mob/user) if(!operating && density && arePowerSystemsOn() && !emagged) - operating = TRUE + operating = DOOR_MALF update_icon(AIRLOCK_EMAG, 1) sleep(6) if(QDELETED(src)) return electronics = new /obj/item/airlock_electronics/destroyed() - operating = FALSE + operating = NONE if(!open()) update_icon(AIRLOCK_CLOSED, 1) emagged = TRUE @@ -1341,12 +1341,12 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) /obj/machinery/door/airlock/cmag_act(mob/user) if(operating || HAS_TRAIT(src, TRAIT_CMAGGED) || !density || !arePowerSystemsOn()) return - operating = TRUE + operating = DOOR_MALF update_icon(AIRLOCK_EMAG, 1) sleep(6) if(QDELETED(src)) return - operating = FALSE + operating = NONE update_icon(AIRLOCK_CLOSED, 1) ADD_TRAIT(src, TRAIT_CMAGGED, "clown_emag") return TRUE diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index c6f82284212..0c442fd217e 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -14,8 +14,8 @@ damage_deflection = 10 var/closingLayer = CLOSED_DOOR_LAYER var/visible = TRUE - /// Is it currently in the process of opening or closing. - var/operating = FALSE + /// Is it currently in the process of opening, closing or being tampered + var/operating = NONE var/autoclose = FALSE /// Whether the door detects things and mobs in its way and reopen or crushes them. var/safe = TRUE @@ -383,7 +383,7 @@ if(operating) return SEND_SIGNAL(src, COMSIG_DOOR_OPEN) - operating = TRUE + operating = DOOR_OPENING do_animate("opening") set_opacity(0) sleep(5) @@ -392,7 +392,7 @@ layer = initial(layer) update_icon() set_opacity(0) - operating = FALSE + operating = NONE air_update_turf(1) update_freelook_sight() if(autoclose) @@ -413,7 +413,7 @@ return SEND_SIGNAL(src, COMSIG_DOOR_CLOSE) - operating = TRUE + operating = DOOR_CLOSING do_animate("closing") layer = closingLayer @@ -423,7 +423,7 @@ update_icon() if(!glass || polarized_on) set_opacity(TRUE) - operating = FALSE + operating = NONE air_update_turf(1) update_freelook_sight() if(safe) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index fc7a37b3b8c..a5ce9b0b5b8 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -85,8 +85,22 @@ latetoggle() else stat |= NOPOWER + adjust_light() update_icon() +/obj/machinery/door/firedoor/proc/adjust_light() + if(stat & (NOPOWER|BROKEN)) + set_light(0) + return + if(active_alarm) + set_light(1, 0.5, COLOR_RED_LIGHT) + else + set_light(1, LIGHTING_MINIMUM_POWER) + +/obj/machinery/door/firedoor/extinguish_light() + set_light(0) + update_icon(UPDATE_OVERLAYS) + /obj/machinery/door/firedoor/attack_hand(mob/user) if(operating || !density) return @@ -168,7 +182,7 @@ else WELDER_WELD_SUCCESS_MESSAGE welded = !welded - update_icon() + update_icon(UPDATE_OVERLAYS) /obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user) if(welded || operating) @@ -209,20 +223,23 @@ /obj/machinery/door/firedoor/update_overlays() . = ..() - if(active_alarm && hasPower()) - . += image('icons/obj/doors/doorfire.dmi', "alarmlights") - if(density && welded) - . += "welded" - return if(welded) - . += "welded_open" + . += "welded[density ? "" : "_open"]" + if(active_alarm && hasPower()) + if(light) + . += emissive_appearance('icons/obj/doors/doorfire.dmi', "alarmlights_lightmask") + . += image('icons/obj/doors/doorfire.dmi', "alarmlights") /obj/machinery/door/firedoor/proc/activate_alarm() active_alarm = TRUE + adjust_light() update_icon() /obj/machinery/door/firedoor/proc/deactivate_alarm() active_alarm = FALSE + if(!density) + layer = initial(layer) + adjust_light() update_icon() /obj/machinery/door/firedoor/open(auto_close = TRUE) @@ -230,7 +247,8 @@ return . = ..() latetoggle(auto_close) - + if(active_alarm) + layer = closingLayer // Active firedoors take precedence and remain visible over closed airlocks. if(auto_close) autoclose = TRUE @@ -245,13 +263,11 @@ /obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE) if(operating || !hasPower() || !nextstate) return - switch(nextstate) - if(FD_OPEN) - nextstate = null - open(auto_close) - if(FD_CLOSED) - nextstate = null - close() + if(nextstate == FD_OPEN) + INVOKE_ASYNC(src, .proc/open, auto_close) + if(nextstate == FD_CLOSED) + INVOKE_ASYNC(src, .proc/close) + nextstate = null /obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE) if(!magic && (operating || !hasPower())) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 2c0e51f0c22..dc00f7ca1cb 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -178,7 +178,7 @@ if(emagged) return 0 if(!operating) //in case of emag - operating = TRUE + operating = DOOR_OPENING do_animate("opening") set_opacity(FALSE) playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) @@ -191,7 +191,7 @@ update_freelook_sight() if(operating) //emag again - operating = FALSE + operating = NONE return 1 /obj/machinery/door/window/close(forced=0) @@ -203,7 +203,7 @@ if(forced < 2) if(emagged) return 0 - operating = TRUE + operating = DOOR_CLOSING do_animate("closing") playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) icon_state = base_state @@ -215,7 +215,7 @@ update_freelook_sight() sleep(10) - operating = FALSE + operating = NONE return 1 /obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) @@ -261,12 +261,12 @@ /obj/machinery/door/window/emag_act(mob/user, obj/weapon) if(!operating && density && !emagged) emagged = TRUE - operating = TRUE + operating = DOOR_MALF electronics = new /obj/item/airlock_electronics/destroyed() flick("[base_state]spark", src) playsound(src, "sparks", 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) sleep(6) - operating = FALSE + operating = NONE open(2) return 1 @@ -274,11 +274,11 @@ if(operating || !density || HAS_TRAIT(src, TRAIT_CMAGGED)) return ADD_TRAIT(src, TRAIT_CMAGGED, "clown_emag") - operating = TRUE + operating = DOOR_MALF flick("[base_state]spark", src) playsound(src, "sparks", 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) sleep(6) - operating = FALSE + operating = NONE return TRUE /obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params) diff --git a/icons/obj/doors/doorfire.dmi b/icons/obj/doors/doorfire.dmi index d154a8436a0..a20b20a1d90 100644 Binary files a/icons/obj/doors/doorfire.dmi and b/icons/obj/doors/doorfire.dmi differ