diff --git a/code/__DEFINES/alarm.dm b/code/__DEFINES/alarm.dm
index f5142f5a167..2c38e3f3f59 100644
--- a/code/__DEFINES/alarm.dm
+++ b/code/__DEFINES/alarm.dm
@@ -1,7 +1,7 @@
//A set of defines to be used by the alarm datums
///Sent by air alarms, indecates something wrong with thier attached atmosphere
#define ALARM_ATMOS "Atmosphere"
-///Sent by fire alarms when they are toggled
+///Sent by firelocks when they detect fire, and by fire alarms when a user pulls them
#define ALARM_FIRE "Fire"
///Sent by apcs when their power starts to fail
#define ALARM_POWER "Power"
diff --git a/code/__DEFINES/firealarm.dm b/code/__DEFINES/firealarm.dm
new file mode 100644
index 00000000000..f2a561d12d2
--- /dev/null
+++ b/code/__DEFINES/firealarm.dm
@@ -0,0 +1,13 @@
+//A set of defines to be sent by fire alarms. The signals are sent to the alarm's area, but are read and used by the firelocks of the area, and are used for mass changes in fire detection.
+///Sent when a fire alarm is emagged
+#define FIRE_DETECT_EMAG "fire_detect_emag"
+///Sent when the fire detection of an area is disabled
+#define FIRE_DETECT_STOP "fire_detect_stop"
+///Sent when the fire detection of an area is enabled
+#define FIRE_DETECT_START "fire_detect_start"
+///Designates a fire lock should be closed due to HEAT
+#define FIRELOCK_ALARM_TYPE_HOT "firelock_alarm_type_hot"
+///Designates a fire lock should be closed due to COLD
+#define FIRELOCK_ALARM_TYPE_COLD "firelock_alarm_type_cold"
+///Designates a fire lock should be closed due unknown reasons (IE fire alarm was pulled)
+#define FIRELOCK_ALARM_TYPE_GENERIC "firelock_alarm_type_generic"
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
index 7a7a53bcec1..1142c23d2d9 100644
--- a/code/datums/looping_sounds/machinery_sounds.dm
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -64,6 +64,8 @@
mid_length = 18
volume = 50
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
/datum/looping_sound/oven
start_sound = 'sound/machines/oven/oven_loop_start.ogg' //my immersions
start_length = 12
@@ -98,6 +100,7 @@
volume = 85
vary = TRUE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/server
mid_sounds = list('sound/machines/tcomms/tcomms_mid1.ogg'=1,'sound/machines/tcomms/tcomms_mid2.ogg'=1,'sound/machines/tcomms/tcomms_mid3.ogg'=1,'sound/machines/tcomms/tcomms_mid4.ogg'=1,\
@@ -108,6 +111,8 @@
falloff_exponent = 5
volume = 50
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
/datum/looping_sound/computer
start_sound = 'sound/machines/computer/computer_start.ogg'
start_length = 7.2 SECONDS
@@ -121,6 +126,8 @@
extra_range = -12
falloff_distance = 1 //Instant falloff after initial tile
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
/datum/looping_sound/gravgen
mid_sounds = list('sound/machines/gravgen/gravgen_mid1.ogg'=1,'sound/machines/gravgen/gravgen_mid2.ogg'=1,'sound/machines/gravgen/gravgen_mid3.ogg'=1,'sound/machines/gravgen/gravgen_mid4.ogg'=1,)
mid_length = 1.8 SECONDS
@@ -129,6 +136,15 @@
falloff_distance = 5
falloff_exponent = 20
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/datum/looping_sound/firealarm
+ mid_sounds = list('sound/machines/FireAlarm.ogg'=1)
+ mid_length = 9.7 SECONDS
+ volume = 75
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
/datum/looping_sound/gravgen/kinesis
volume = 20
falloff_distance = 2
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 147f7aa93d1..b252b2739a4 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -17,11 +17,17 @@
///Do we have an active fire alarm?
var/fire = FALSE
- ///How many fire alarm sources do we have?
- var/triggered_firealarms = 0
+ ///A var for whether the area allows for detecting fires/etc. Disabled or enabled at a fire alarm, checked by fire locks.
+ var/fire_detect = TRUE
+ ///A list of all fire locks in this area. Used by fire alarm panels when resetting fire locks or activating all in an area
+ var/list/firedoors
+ ///A list of firelocks currently active. Used by fire alarms when setting their icons.
+ var/list/active_firelocks
+ ///A list of all fire alarms in this area. Used by fire locks and burglar alarms to tell the fire alarm to change its icon.
+ var/list/firealarms
///Alarm type to count of sources. Not usable for ^ because we handle fires differently
var/list/active_alarms = list()
- ///We use this just for fire alarms, because they're area based right now so one alarm going poof shouldn't prevent you from clearing your alarms listing
+ ///We use this just for fire alarms, because they're area based right now so one alarm going poof shouldn't prevent you from clearing your alarms listing. Fire alarms and fire locks will set and clear alarms.
var/datum/alarm_handler/alarm_manager
var/lightswitch = TRUE
@@ -65,10 +71,7 @@
var/list/ambientsounds
flags_1 = CAN_BE_DIRTY_1
- var/list/firedoors
var/list/cameras
- var/list/firealarms
- var/firedoors_last_closed_on = 0
///Typepath to limit the areas (subtypes included) that atoms in this area can smooth with. Used for shuttles.
var/area/area_limited_icon_smoothing
@@ -241,84 +244,6 @@ GLOBAL_LIST_EMPTY(teleportlocs)
QDEL_NULL(alarm_manager)
return ..()
-/**
- * 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
- if (D.being_held_open)
- continue
- 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 ? FIREDOOR_OPEN : FIREDOOR_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
- *
- * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
- *
- * Also starts the area processing on SSobj
- */
-/area/proc/firealert(obj/source)
- if (!fire)
- set_fire_alarm_effect()
- ModifyFiredoors(FALSE)
- for(var/item in firealarms)
- var/obj/machinery/firealarm/F = item
- F.update_appearance()
- alarm_manager.send_alarm(ALARM_FIRE, source)
- START_PROCESSING(SSobj, src)
-
-/**
- * Reset the firealarm alert for this area
- *
- * resets the alert sent to all ai players, alert consoles, drones and alarm monitor programs
- * in the world
- *
- * Also cycles the icons of all firealarms and deregisters the area from processing on SSOBJ
- */
-/area/proc/firereset(obj/source)
- var/should_reset_alarms = fire
- if(source)
- if(istype(source, /obj/machinery/firealarm))
- var/obj/machinery/firealarm/alarm = source
- if(alarm.triggered)
- alarm.triggered = FALSE
- triggered_firealarms -= 1
- if(triggered_firealarms > 0)
- should_reset_alarms = FALSE
- should_reset_alarms = should_reset_alarms & power_environ //No resetting if there's no power
-
- if (should_reset_alarms) // if there's a source, make sure there's no fire alarms left
- unset_fire_alarm_effects()
- ModifyFiredoors(TRUE)
- for(var/item in firealarms)
- var/obj/machinery/firealarm/F = item
- F.update_appearance()
- alarm_manager.clear_alarm(ALARM_FIRE, source)
- STOP_PROCESSING(SSobj, src)
-
-/**
- * If 100 ticks has elapsed, toggle all the firedoors closed again
- */
-/area/process()
- if(!triggered_firealarms)
- firereset() //If there are no breaches or fires, and this alert was caused by a breach or fire, die
- if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds
- ModifyFiredoors(FALSE)
-
/**
* Close and lock a door passed into this proc
*
@@ -352,15 +277,14 @@ GLOBAL_LIST_EMPTY(teleportlocs)
* Updates the fire light on fire alarms in the area and sets all lights to emergency mode
*/
/area/proc/set_fire_alarm_effect()
+ if(fire)
+ return
fire = TRUE
- if(!triggered_firealarms) //If there aren't any fires/breaches
- triggered_firealarms = INFINITY //You're not allowed to naturally die
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- for(var/alarm in firealarms)
- var/obj/machinery/firealarm/F = alarm
- F.update_fire_light(fire)
for(var/obj/machinery/light/L in src)
L.update()
+ for(var/obj/machinery/firealarm/firepanel in firealarms)
+ firepanel.set_status()
/**
* unset the fire alarm visual affects in an area
@@ -369,14 +293,11 @@ GLOBAL_LIST_EMPTY(teleportlocs)
*/
/area/proc/unset_fire_alarm_effects()
fire = FALSE
- triggered_firealarms = 0
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- for(var/alarm in firealarms)
- var/obj/machinery/firealarm/F = alarm
- F.update_fire_light(fire)
- F.triggered = FALSE
for(var/obj/machinery/light/L in src)
L.update()
+ for(var/obj/machinery/firealarm/firepanel in firealarms)
+ firepanel.set_status()
/**
* Update the icon state of the area
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index af8d6d56b9a..36a22bc1559 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -1,6 +1,7 @@
#define CONSTRUCTION_PANEL_OPEN 1 //Maintenance panel is open, still functioning
#define CONSTRUCTION_NO_CIRCUIT 2 //Circuit board removed, can safely weld apart
#define DEFAULT_STEP_TIME 20 /// default time for each step
+#define DETECT_COOLDOWN_STEP_TIME 5 SECONDS ///Wait time before we can detect an issue again, after a recent clear.
/obj/machinery/door/firedoor
name = "firelock"
@@ -21,16 +22,76 @@
assemblytype = /obj/structure/firelock_frame
armor = list(MELEE = 10, BULLET = 30, LASER = 20, ENERGY = 20, BOMB = 30, BIO = 100, FIRE = 95, ACID = 70)
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN
- var/nextstate = null
+
+ COOLDOWN_DECLARE(detect_cooldown)
+
+ ///Trick to get the glowing overlay visible from a distance
+ luminosity = 1
+ ///X offset for the overlay lights, so that they line up with the thin border firelocks
+ var/light_xoffset = 0
+ ///Y offset for the overlay lights, so that they line up with the thin border firelocks
+ var/light_yoffset = 0
+
var/boltslocked = TRUE
+ ///List of areas we handle. See CalculateAffectingAreas()
var/list/affecting_areas
+ ///For the few times we affect only the area we're actually in. Set during Init. If we get moved, we don't update, but this is consistant with fire alarms and also kinda funny so call it intentional.
+ var/area/my_area
+ ///Tracks if the firelock is being held open by a crowbar. If so, we don't close until they walk away
var/being_held_open = FALSE
+
+ ///Type of alarm when active. See code/defines/firealarm.dm for the list. This var being null means there is no alarm.
+ var/alarm_type = null
+ ///The merger_id and merger_typecache variables are used to make rows of firelocks activate at the same time.
+ var/merger_id = "firelocks"
+ var/static/list/merger_typecache
+ ///Overlay object for the warning lights. This and some plane settings allows the lights to glow in the dark.
+ var/mutable_appearance/warn_lights
+
+ ///looping sound datum for our fire alarm siren.
+ var/datum/looping_sound/firealarm/soundloop
+ ///Keeps track of if we're playing the alarm sound loop (as only one firelock per group should be). Used during power changes.
+ var/is_playing_alarm = FALSE
+
var/knock_sound = 'sound/effects/glassknock.ogg'
var/bash_sound = 'sound/effects/glassbash.ogg'
+
/obj/machinery/door/firedoor/Initialize(mapload)
. = ..()
+ COOLDOWN_START(src, detect_cooldown, DETECT_COOLDOWN_STEP_TIME)
+ soundloop = new(src, FALSE)
+ AddElement(/datum/element/atmos_sensitive, mapload)
CalculateAffectingAreas()
+ my_area = get_area(src)
+ var/static/list/loc_connections = list(
+ COMSIG_TURF_EXPOSE = .proc/check_atmos,
+ )
+
+ AddElement(/datum/element/connect_loc, loc_connections)
+ if(!merger_typecache)
+ merger_typecache = typecacheof(/obj/machinery/door/firedoor)
+
+ check_atmos()
+
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/door/firedoor/LateInitialize()
+ . = ..()
+ GetMergeGroup(merger_id, allowed_types = merger_typecache)
+
+/**
+ * Sets the offset for the warning lights.
+ *
+ * Used for special firelocks with light overlays that don't line up to their sprite.
+ */
+/obj/machinery/door/firedoor/proc/adjust_lights_starting_offset()
+ return
+
+/obj/machinery/door/firedoor/Destroy()
+ remove_from_areas()
+ QDEL_NULL(soundloop)
+ return ..()
/obj/machinery/door/firedoor/examine(mob/user)
. = ..()
@@ -46,29 +107,145 @@
else
. += span_notice("The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor.")
+/**
+ * Calculates what areas we should worry about.
+ *
+ * This proc builds a list of areas we are in and areas we border
+ * and writes it to affecting_areas.
+ */
/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
+ var/list/new_affecting_areas = get_adjacent_open_areas(src) | get_area(src)
+ if(compare_list(new_affecting_areas, affecting_areas))
+ return //No changes needed
+
remove_from_areas()
- affecting_areas = get_adjacent_open_areas(src) | get_area(src)
- for(var/I in affecting_areas)
- var/area/A = I
- LAZYADD(A.firedoors, src)
-
-/obj/machinery/door/firedoor/closed
- icon_state = "door_closed"
- density = TRUE
-
-//see also turf/AfterChange for adjacency shennanigans
+ affecting_areas = new_affecting_areas
+ for(var/area/place in affecting_areas)
+ LAZYADD(place.firedoors, src)
+ if(alarm_type)
+ if(place == get_area(src))
+ LAZYADD(place.active_firelocks, src) //We only add ourselves to our own area's active firelocks...
+ for(var/obj/machinery/firealarm/fire_panel in place.firealarms)
+ fire_panel.set_status() //...but all adjacent fire alarms are notified
+/**
+ * Removes us from any lists of areas in the affecting_areas list, then clears affecting_areas
+ *
+ * Undoes everything done in the CalculateAffectingAreas() proc, to clean up prior to deletion.
+ * Calls reset() first, in case any alarms need to be cleared first.
+ */
/obj/machinery/door/firedoor/proc/remove_from_areas()
- if(affecting_areas)
- for(var/I in affecting_areas)
- var/area/A = I
- LAZYREMOVE(A.firedoors, src)
+ if(!affecting_areas)
+ return
+ for(var/area/place in affecting_areas)
+ LAZYREMOVE(place.firedoors, src)
+ LAZYREMOVE(place.active_firelocks, src)
+ if(LAZYLEN(place.active_firelocks)) //if we were the last firelock still active in this particular area
+ continue
+ for(var/obj/machinery/firealarm/fire_panel in place.firealarms)
+ fire_panel.set_status()
-/obj/machinery/door/firedoor/Destroy()
- remove_from_areas()
- affecting_areas.Cut()
- return ..()
+/obj/machinery/door/firedoor/proc/check_atmos(datum/source)
+ if(!COOLDOWN_FINISHED(src, detect_cooldown))
+ return
+ if(alarm_type)
+ return
+ for(var/area/place in affecting_areas)
+ if(!place.fire_detect) //if any area is set to disable detection
+ return
+
+ var/turf/my_turf = source
+ if(!my_turf)
+ my_turf = get_turf(src)
+
+ var/datum/gas_mixture/environment = my_turf.return_air()
+ var/result
+
+ if(environment?.temperature >= FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
+ result = FIRELOCK_ALARM_TYPE_HOT
+ if(environment?.temperature <= BODYTEMP_COLD_DAMAGE_LIMIT)
+ result = FIRELOCK_ALARM_TYPE_COLD
+ if(!result)
+ return
+
+ start_activation_process(result)
+
+/**
+ * Begins activation process of us and our neighbors.
+ *
+ * This proc will call activate() on every fire lock (including us) listed
+ * in the merge group datum. Returns without doing anything if our alarm_type
+ * was already set, as that means that we're already active.
+ *
+ * Arguments:
+ * code should be one of three defined alarm types, or can be not supplied. Will dictate the color of the fire alarm lights, and defults to "firelock_alarm_type_generic"
+ */
+/obj/machinery/door/firedoor/proc/start_activation_process(code = FIRELOCK_ALARM_TYPE_GENERIC)
+ if(alarm_type)
+ return //We're already active
+ soundloop.start()
+ is_playing_alarm = TRUE
+ var/datum/merger/merge_group = GetMergeGroup(merger_id, merger_typecache)
+ for(var/obj/machinery/door/firedoor/buddylock as anything in merge_group.members)
+ buddylock.activate(code)
+
+
+/**
+ * Proc that handles activation of the firelock and all this details
+ *
+ * Sets the alarm_type variable based on the single arg, which is in turn
+ * used by several procs to understand the intended state of the fire lock.
+ * Also calls set_status() on all fire alarms in all affected areas, tells
+ * the area the firelock sits in to report the event (AI, alarm consoles, etc)
+ * and finally calls correct_state(), which will handle opening or closing
+ * this fire lock.
+ */
+/obj/machinery/door/firedoor/proc/activate(code = FIRELOCK_ALARM_TYPE_GENERIC)
+ SIGNAL_HANDLER
+ if(alarm_type)
+ return //Already active
+ alarm_type = code
+ for(var/area/place in affecting_areas)
+ LAZYADD(place.active_firelocks, src)
+ if(LAZYLEN(place.active_firelocks) == 1) //if we're the first to activate in this particular area
+ for(var/obj/machinery/firealarm/fire_panel in place.firealarms)
+ fire_panel.set_status()
+ if(alarm_type != FIRELOCK_ALARM_TYPE_GENERIC) //Generic alarms tend to activate all firelocks in an area, which otherwise makes the red lighting spread like a virus. Anyway, fire alarms already do this for manual pulls.
+ place.set_fire_alarm_effect() //bathe in red
+ if(place == my_area)
+ place.alarm_manager.send_alarm(ALARM_FIRE, place) //We'll limit our reporting to just the area we're on. If the issue affects bordering areas, they can report it themselves
+ update_icon() //Sets the door lights even if the door doesn't move.
+ correct_state()
+
+/**
+ * Proc that handles reset steps
+ *
+ * Clears the alarm state and attempts to open the firelock.
+ */
+/obj/machinery/door/firedoor/proc/reset()
+ SIGNAL_HANDLER
+ alarm_type = null
+ for(var/area/place in affecting_areas)
+ LAZYREMOVE(place.active_firelocks, src)
+ if(!LAZYLEN(place.active_firelocks)) //if we were the last firelock still active in this particular area
+ for(var/obj/machinery/firealarm/fire_panel in place.firealarms)
+ fire_panel.set_status()
+ place.unset_fire_alarm_effects()
+ COOLDOWN_START(src, detect_cooldown, DETECT_COOLDOWN_STEP_TIME)
+ soundloop.stop()
+ is_playing_alarm = FALSE
+ update_icon() //Sets the door lights even if the door doesn't move.
+ correct_state()
+
+/obj/machinery/door/firedoor/emag_act(mob/user, obj/item/card/emag/doorjack/digital_crowbar)
+ if(obj_flags & EMAGGED)
+ return
+ if(!isAI(user)) //Skip doorjack-specific code
+ if(!user || digital_crowbar.charges < 1)
+ return
+ digital_crowbar.use_charge(user)
+ obj_flags |= EMAGGED
+ INVOKE_ASYNC(src, .proc/open)
/obj/machinery/door/firedoor/Bumped(atom/movable/AM)
if(panel_open || operating)
@@ -82,7 +259,13 @@
/obj/machinery/door/firedoor/power_change()
. = ..()
- INVOKE_ASYNC(src, .proc/latetoggle)
+ update_icon()
+ correct_state()
+ if(machine_stat & NOPOWER)
+ soundloop.stop()
+ else if(is_playing_alarm)
+ soundloop.start()
+
/obj/machinery/door/firedoor/attack_hand(mob/living/user, list/modifiers)
. = ..()
@@ -140,6 +323,7 @@
user.visible_message(span_danger("[user] [welded?"welds":"unwelds"] [src]."), span_notice("You [welded ? "weld" : "unweld"] [src]."))
log_game("[key_name(user)] [welded ? "welded":"unwelded"] firedoor [src] with [W] at [AREACOORD(src)]")
update_appearance()
+ correct_state()
/// We check for adjacency when using the primary attack.
/obj/machinery/door/firedoor/try_to_crowbar(obj/item/acting_object, mob/user)
@@ -167,6 +351,8 @@
if(density)
open()
+ if(alarm_type)
+ addtimer(CALLBACK(src, .proc/correct_state), 2 SECONDS, TIMER_UNIQUE)
else
close()
@@ -177,7 +363,7 @@
if(!QDELETED(user) && Adjacent(user) && isliving(user) && (living_user.body_position == STANDING_UP))
return
being_held_open = FALSE
- INVOKE_ASYNC(src, .proc/close)
+ correct_state()
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
UnregisterSignal(user, COMSIG_LIVING_SET_BODY_POSITION)
UnregisterSignal(user, COMSIG_PARENT_QDELETING)
@@ -190,6 +376,8 @@
return TRUE
if(density)
open()
+ if(alarm_type)
+ addtimer(CALLBACK(src, .proc/correct_state), 2 SECONDS, TIMER_UNIQUE)
else
close()
return TRUE
@@ -203,6 +391,8 @@
to_chat(user, span_warning("[src] refuses to budge!"))
return
open()
+ if(alarm_type)
+ addtimer(CALLBACK(src, .proc/correct_state), 2 SECONDS, TIMER_UNIQUE)
/obj/machinery/door/firedoor/do_animate(animation)
switch(animation)
@@ -217,46 +407,71 @@
/obj/machinery/door/firedoor/update_overlays()
. = ..()
- if(!welded)
+ if(welded)
+ . += density ? "welded" : "welded_open"
+ if(alarm_type && powered())
+ var/mutable_appearance/hazards
+ hazards = mutable_appearance(icon, "[(obj_flags & EMAGGED) ? "firelock_alarm_type_emag" : alarm_type]")
+ hazards.pixel_x = light_xoffset
+ hazards.pixel_y = light_yoffset
+ . += hazards
+ hazards = emissive_appearance(icon, "[(obj_flags & EMAGGED) ? "firelock_alarm_type_emag" : alarm_type]", alpha = src.alpha)
+ hazards.pixel_x = light_xoffset
+ hazards.pixel_y = light_yoffset
+ . += hazards
+
+/**
+ * Corrects the current state of the door, based on if alarm_type is set.
+ *
+ * This proc is called after weld and power restore events. Gives the
+ * illusion that the door is constantly attempting to move without actually
+ * having to process it. Timers also call this, so that if alarm_type
+ * changes during the timer, the door doesn't close or open incorrectly.
+ */
+/obj/machinery/door/firedoor/proc/correct_state()
+ if(obj_flags & EMAGGED || being_held_open)
+ return //Unmotivated, indifferent, we have no real care what state we're in anymore.
+ if(alarm_type && !density) //We should be closed but we're not
+ INVOKE_ASYNC(src, .proc/close)
+ return
+ if(!alarm_type && density) //We should be open but we're not
+ INVOKE_ASYNC(src, .proc/open)
return
- . += density ? "welded" : "welded_open"
/obj/machinery/door/firedoor/open()
+ if(welded)
+ return
+ var/alarm = alarm_type
. = ..()
- latetoggle()
+ if(alarm != alarm_type) //Something changed while we were sleeping
+ correct_state() //So we should re-evaluate our state
/obj/machinery/door/firedoor/close()
if(HAS_TRAIT(loc, TRAIT_FIREDOOR_STOP))
return
+ var/alarm = alarm_type
. = ..()
- latetoggle()
+ if(alarm != alarm_type) //Something changed while we were sleeping
+ correct_state() //So we should re-evaluate our state
/obj/machinery/door/firedoor/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
- var/turf/T = get_turf(src)
+ var/turf/targetloc = get_turf(src)
if(disassembled || prob(40))
- var/obj/structure/firelock_frame/F = new assemblytype(T)
+ var/obj/structure/firelock_frame/unbuilt_lock = new assemblytype(targetloc)
if(disassembled)
- F.constructionStep = CONSTRUCTION_PANEL_OPEN
+ unbuilt_lock.constructionStep = CONSTRUCTION_PANEL_OPEN
else
- F.constructionStep = CONSTRUCTION_NO_CIRCUIT
- F.update_integrity(F.max_integrity * 0.5)
- F.update_appearance()
+ unbuilt_lock.constructionStep = CONSTRUCTION_NO_CIRCUIT
+ unbuilt_lock.update_integrity(unbuilt_lock.max_integrity * 0.5)
+ unbuilt_lock.update_appearance()
else
- new /obj/item/electronics/firelock (T)
+ new /obj/item/electronics/firelock (targetloc)
qdel(src)
-
-/obj/machinery/door/firedoor/proc/latetoggle()
- if(operating || machine_stat & NOPOWER || !nextstate)
- return
- switch(nextstate)
- if(FIREDOOR_OPEN)
- nextstate = null
- open()
- if(FIREDOOR_CLOSED)
- nextstate = null
- close()
+/obj/machinery/door/firedoor/closed
+ icon_state = "door_closed"
+ density = TRUE
/obj/machinery/door/firedoor/border_only
icon = 'icons/obj/doors/edge_Doorfire.dmi'
@@ -276,6 +491,25 @@
)
AddElement(/datum/element/connect_loc, loc_connections)
+ adjust_lights_starting_offset()
+
+/obj/machinery/door/firedoor/border_only/adjust_lights_starting_offset()
+ light_xoffset = 0
+ light_yoffset = 0
+ switch(dir)
+ if(NORTH)
+ light_yoffset = 2
+ if(SOUTH)
+ light_yoffset = -2
+ if(EAST)
+ light_xoffset = 2
+ if(WEST)
+ light_xoffset = -2
+ update_icon()
+
+/obj/machinery/door/firedoor/border_only/Moved()
+ . = ..()
+ adjust_lights_starting_offset()
/obj/machinery/door/firedoor/border_only/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
@@ -338,14 +572,14 @@
icon_state = "[base_icon_state][constructionStep]"
return ..()
-/obj/structure/firelock_frame/attackby(obj/item/C, mob/user)
+/obj/structure/firelock_frame/attackby(obj/item/attacking_object, mob/user)
switch(constructionStep)
if(CONSTRUCTION_PANEL_OPEN)
- if(C.tool_behaviour == TOOL_CROWBAR)
- C.play_tool_sound(src)
+ if(attacking_object.tool_behaviour == TOOL_CROWBAR)
+ attacking_object.play_tool_sound(src)
user.visible_message(span_notice("[user] begins removing the circuit board from [src]..."), \
span_notice("You begin prying out the circuit board from [src]..."))
- if(!C.use_tool(src, user, DEFAULT_STEP_TIME))
+ if(!attacking_object.use_tool(src, user, DEFAULT_STEP_TIME))
return
if(constructionStep != CONSTRUCTION_PANEL_OPEN)
return
@@ -356,14 +590,14 @@
constructionStep = CONSTRUCTION_NO_CIRCUIT
update_appearance()
return
- if(C.tool_behaviour == TOOL_WRENCH)
+ if(attacking_object.tool_behaviour == TOOL_WRENCH)
if(locate(/obj/machinery/door/firedoor) in get_turf(src))
to_chat(user, span_warning("There's already a firelock there."))
return
- C.play_tool_sound(src)
+ attacking_object.play_tool_sound(src)
user.visible_message(span_notice("[user] starts bolting down [src]..."), \
span_notice("You begin bolting [src]..."))
- if(!C.use_tool(src, user, DEFAULT_STEP_TIME))
+ if(!attacking_object.use_tool(src, user, DEFAULT_STEP_TIME))
return
if(locate(/obj/machinery/door/firedoor) in get_turf(src))
return
@@ -376,61 +610,61 @@
new /obj/machinery/door/firedoor(get_turf(src))
qdel(src)
return
- if(istype(C, /obj/item/stack/sheet/plasteel))
- var/obj/item/stack/sheet/plasteel/P = C
+ if(istype(attacking_object, /obj/item/stack/sheet/plasteel))
+ var/obj/item/stack/sheet/plasteel/plasteel_sheet = attacking_object
if(reinforced)
to_chat(user, span_warning("[src] is already reinforced."))
return
- if(P.get_amount() < 2)
+ if(plasteel_sheet.get_amount() < 2)
to_chat(user, span_warning("You need more plasteel to reinforce [src]."))
return
user.visible_message(span_notice("[user] begins reinforcing [src]..."), \
span_notice("You begin reinforcing [src]..."))
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, TRUE)
if(do_after(user, DEFAULT_STEP_TIME, target = src))
- if(constructionStep != CONSTRUCTION_PANEL_OPEN || reinforced || P.get_amount() < 2 || !P)
+ if(constructionStep != CONSTRUCTION_PANEL_OPEN || reinforced || plasteel_sheet.get_amount() < 2 || !plasteel_sheet)
return
user.visible_message(span_notice("[user] reinforces [src]."), \
span_notice("You reinforce [src]."))
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, TRUE)
- P.use(2)
+ plasteel_sheet.use(2)
reinforced = 1
return
if(CONSTRUCTION_NO_CIRCUIT)
- if(istype(C, /obj/item/electronics/firelock))
- user.visible_message(span_notice("[user] starts adding [C] to [src]..."), \
+ if(istype(attacking_object, /obj/item/electronics/firelock))
+ user.visible_message(span_notice("[user] starts adding [attacking_object] to [src]..."), \
span_notice("You begin adding a circuit board to [src]..."))
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, TRUE)
if(!do_after(user, DEFAULT_STEP_TIME, target = src))
return
if(constructionStep != CONSTRUCTION_NO_CIRCUIT)
return
- qdel(C)
+ qdel(attacking_object)
user.visible_message(span_notice("[user] adds a circuit to [src]."), \
- span_notice("You insert and secure [C]."))
+ span_notice("You insert and secure [attacking_object]."))
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, TRUE)
constructionStep = CONSTRUCTION_PANEL_OPEN
return
- if(C.tool_behaviour == TOOL_WELDER)
- if(!C.tool_start_check(user, amount=1))
+ if(attacking_object.tool_behaviour == TOOL_WELDER)
+ if(!attacking_object.tool_start_check(user, amount=1))
return
user.visible_message(span_notice("[user] begins cutting apart [src]'s frame..."), \
span_notice("You begin slicing [src] apart..."))
- if(C.use_tool(src, user, DEFAULT_STEP_TIME, volume=50, amount=1))
+ if(attacking_object.use_tool(src, user, DEFAULT_STEP_TIME, volume=50, amount=1))
if(constructionStep != CONSTRUCTION_NO_CIRCUIT)
return
user.visible_message(span_notice("[user] cuts apart [src]!"), \
span_notice("You cut [src] into metal."))
- var/turf/T = get_turf(src)
- new /obj/item/stack/sheet/iron(T, 3)
+ var/turf/tagetloc = get_turf(src)
+ new /obj/item/stack/sheet/iron(tagetloc, 3)
if(reinforced)
- new /obj/item/stack/sheet/plasteel(T, 2)
+ new /obj/item/stack/sheet/plasteel(tagetloc, 2)
qdel(src)
return
- if(istype(C, /obj/item/electroadaptive_pseudocircuit))
- var/obj/item/electroadaptive_pseudocircuit/P = C
- if(!P.adapt_circuit(user, DEFAULT_STEP_TIME * 0.5))
+ if(istype(attacking_object, /obj/item/electroadaptive_pseudocircuit))
+ var/obj/item/electroadaptive_pseudocircuit/raspberrypi = attacking_object
+ if(!raspberrypi.adapt_circuit(user, DEFAULT_STEP_TIME * 0.5))
return
user.visible_message(span_notice("[user] fabricates a circuit and places it into [src]."), \
span_notice("You adapt a firelock circuit and slot it into the assembly."))
@@ -466,3 +700,4 @@
#undef CONSTRUCTION_PANEL_OPEN
#undef CONSTRUCTION_NO_CIRCUIT
+#undef DETECT_COOLDOWN_STEP_TIME
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 0027c08338d..69f0af98c1f 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -1,5 +1,3 @@
-#define FIREALARM_COOLDOWN 96 // This define is based on the current FireAlarm.ogg, and is placed to match the running time at 9 seconds 600 milliseconds (9.6 seconds)
-
/obj/item/electronics/firealarm
name = "fire alarm electronics"
desc = "A fire alarm circuit. Can handle heat levels up to 40 degrees celsius."
@@ -32,13 +30,12 @@
//Trick to get the glowing overlay visible from a distance
luminosity = 1
-
- var/detecting = 1
- var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
- COOLDOWN_DECLARE(last_alarm)
- var/area/myarea = null
- //Has this firealarm been triggered by its enviroment?
- var/triggered = FALSE
+ ///Buildstate for contruction steps. 2 = complete, 1 = no wires, 0 = circuit gone
+ var/buildstage = 2
+ ///Our home area, set in Init. Due to loading step order, this seems to be null very early in the server setup process, which is why some procs use `my_area?` for var or list checks.
+ var/area/my_area = null
+ ///looping sound datum for our fire alarm siren.
+ var/datum/looping_sound/firealarm/soundloop
/obj/machinery/firealarm/Initialize(mapload, dir, building)
. = ..()
@@ -48,17 +45,35 @@
if(name == initial(name))
name = "[get_area_name(src)] [initial(name)]"
update_appearance()
- myarea = get_area(src)
- LAZYADD(myarea.firealarms, src)
+ my_area = get_area(src)
+ LAZYADD(my_area.firealarms, src)
AddElement(/datum/element/atmos_sensitive, mapload)
RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, .proc/check_security_level)
+ soundloop = new(src, FALSE)
/obj/machinery/firealarm/Destroy()
- myarea.firereset(src)
- LAZYREMOVE(myarea.firealarms, src)
+ if(my_area)
+ LAZYREMOVE(my_area.firealarms, src)
+ my_area = null
+ QDEL_NULL(soundloop)
return ..()
+/**
+ * Sets the sound state, and then calls update_icon()
+ *
+ * This proc exists to be called by areas and firelocks
+ * so that it may update its icon and start or stop playing
+ * the alarm sound based on the state of an area variable.
+ */
+/obj/machinery/firealarm/proc/set_status()
+ if( (my_area.fire || LAZYLEN(my_area.active_firelocks)) && !(obj_flags & EMAGGED) )
+ set_light(l_power = 0.8)
+ else
+ soundloop.stop()
+ set_light(l_power = 0)
+ update_icon()
+
/obj/machinery/firealarm/update_icon_state()
if(panel_open)
icon_state = "fire_b[buildstage]"
@@ -84,12 +99,11 @@
. += mutable_appearance(icon, "fire_[SEC_LEVEL_GREEN]")
. += emissive_appearance(icon, "fire_[SEC_LEVEL_GREEN]", alpha = src.alpha)
- var/area/area = get_area(src)
-
- if(!detecting || !area.fire)
- . += "fire_off"
- . += mutable_appearance(icon, "fire_off")
- . += emissive_appearance(icon, "fire_off", alpha = src.alpha)
+ if(!(my_area?.fire || LAZYLEN(my_area?.active_firelocks)))
+ if(my_area?.fire_detect) //If this is false, leave the green light missing. A good hint to anyone paying attention.
+ . += "fire_off"
+ . += mutable_appearance(icon, "fire_off")
+ . += emissive_appearance(icon, "fire_off", alpha = src.alpha)
else if(obj_flags & EMAGGED)
. += "fire_emagged"
. += mutable_appearance(icon, "fire_emagged")
@@ -99,7 +113,7 @@
. += mutable_appearance(icon, "fire_on")
. += emissive_appearance(icon, "fire_on", alpha = src.alpha)
- if(!panel_open && detecting && triggered) //It just looks horrible with the panel open
+ if(!panel_open && my_area?.fire_detect && my_area?.fire) //It just looks horrible with the panel open
. += "fire_detected"
. += mutable_appearance(icon, "fire_detected")
. += emissive_appearance(icon, "fire_detected", alpha = src.alpha) //Pain
@@ -120,28 +134,9 @@
update_appearance()
if(user)
user.visible_message(span_warning("Sparks fly out of [src]!"),
- span_notice("You emag [src], disabling its thermal sensors."))
+ span_notice("You override [src], disabling the speaker."))
playsound(src, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
-
-/obj/machinery/firealarm/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
- return (exposed_temperature > T0C + 200 || exposed_temperature < BODYTEMP_COLD_DAMAGE_LIMIT) && !(obj_flags & EMAGGED) && !machine_stat
-
-/obj/machinery/firealarm/atmos_expose(datum/gas_mixture/air, exposed_temperature)
- if(!detecting)
- return
- if(!triggered)
- triggered = TRUE
- myarea.triggered_firealarms += 1
- update_appearance()
- alarm()
-
-/obj/machinery/firealarm/atmos_end()
- if(!detecting)
- return
- if(triggered)
- triggered = FALSE
- myarea.triggered_firealarms -= 1
- update_appearance()
+ set_status()
/**
* Signal handler for checking if we should update fire alarm appearance accordingly to a newly set security level
@@ -156,41 +151,69 @@
if(is_station_level(z))
update_appearance()
+/**
+ * Sounds the fire alarm and closes all firelocks in the area. Also tells the area to color the lights red.
+ *
+ * Arguments:
+ * * mob/user is the user that pulled the alarm.
+ */
/obj/machinery/firealarm/proc/alarm(mob/user)
- if(!is_operational || !COOLDOWN_FINISHED(src, last_alarm))
+ if(!is_operational)
return
- COOLDOWN_START(src, last_alarm, FIREALARM_COOLDOWN)
- var/area/area = get_area(src)
- area.firealert(src)
- playsound(loc, 'sound/machines/FireAlarm.ogg', 96)
+
+ if(my_area.fire)
+ return //area alarm already active
+ my_area.alarm_manager.send_alarm(ALARM_FIRE, my_area)
+ my_area.set_fire_alarm_effect()
+ for(var/obj/machinery/door/firedoor/firelock in my_area.firedoors)
+ firelock.activate(FIRELOCK_ALARM_TYPE_GENERIC)
if(user)
log_game("[user] triggered a fire alarm at [COORD(src)]")
+ soundloop.start() //Manually pulled fire alarms will make the sound, rather than the doors.
+/**
+ * Resets all firelocks in the area. Also tells the area to disable alarm lighting, if it was enabled.
+ *
+ * Arguments:
+ * * mob/user is the user that reset the alarm.
+ */
/obj/machinery/firealarm/proc/reset(mob/user)
if(!is_operational)
return
- var/area/area = get_area(src)
- area.firereset()
+ my_area.unset_fire_alarm_effects()
+ for(var/obj/machinery/door/firedoor/firelock in my_area.firedoors)
+ firelock.reset()
+ my_area.alarm_manager.clear_alarm(ALARM_FIRE, my_area)
if(user)
log_game("[user] reset a fire alarm at [COORD(src)]")
+ soundloop.stop()
/obj/machinery/firealarm/attack_hand(mob/user, list/modifiers)
if(buildstage != 2)
return
. = ..()
add_fingerprint(user)
- var/area/area = get_area(src)
- if(area.fire)
- reset(user)
- else
- alarm(user)
+ alarm(user)
+
+/obj/machinery/firealarm/attack_hand_secondary(mob/user, list/modifiers)
+ if(buildstage != 2)
+ return ..()
+ add_fingerprint(user)
+ reset(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/machinery/firealarm/attack_ai(mob/user)
return attack_hand(user)
+/obj/machinery/firealarm/attack_ai_secondary(mob/user)
+ return attack_hand_secondary(user)
+
/obj/machinery/firealarm/attack_robot(mob/user)
return attack_hand(user)
+/obj/machinery/firealarm/attack_robot_secondary(mob/user)
+ return attack_hand_secondary(user)
+
/obj/machinery/firealarm/attackby(obj/item/tool, mob/living/user, params)
add_fingerprint(user)
@@ -218,15 +241,7 @@
switch(buildstage)
if(2)
- if(tool.tool_behaviour == TOOL_MULTITOOL)
- detecting = !detecting
- if (src.detecting)
- user.visible_message(span_notice("[user] reconnects [src]'s detecting unit!"), span_notice("You reconnect [src]'s detecting unit."))
- else
- user.visible_message(span_notice("[user] disconnects [src]'s detecting unit!"), span_notice("You disconnect [src]'s detecting unit."))
- return
-
- else if(tool.tool_behaviour == TOOL_WIRECUTTER)
+ if(tool.tool_behaviour == TOOL_WIRECUTTER)
buildstage = 1
tool.play_tool_sound(src)
new /obj/item/stack/cable_coil(user.loc, 5)
@@ -293,7 +308,6 @@
tool.play_tool_sound(src)
qdel(src)
return
-
return ..()
/obj/machinery/firealarm/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
@@ -321,14 +335,12 @@
/obj/machinery/firealarm/singularity_pull(S, current_size)
if (current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects, the fire alarm experiences integrity failure
deconstruct()
- ..()
+ return ..()
/obj/machinery/firealarm/atom_break(damage_flag)
if(buildstage == 0) //can't break the electronics if there isn't any inside.
return
- . = ..()
- if(.)
- LAZYREMOVE(myarea.firealarms, src)
+ return ..()
/obj/machinery/firealarm/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
@@ -340,18 +352,16 @@
new /obj/item/stack/cable_coil(loc, 3)
qdel(src)
-/obj/machinery/firealarm/proc/update_fire_light(fire)
- if(fire == !!light_power)
- return // do nothing if we're already active
- if(fire)
- set_light(l_power = 0.8)
- else
- set_light(l_power = 0)
-
// Allows users to examine the state of the thermal sensor
/obj/machinery/firealarm/examine(mob/user)
. = ..()
- . += "A light on the side indicates the thermal sensor is [detecting ? "enabled" : "disabled"]."
+ if((my_area?.fire || LAZYLEN(my_area?.active_firelocks)))
+ . += "The local area hazard light is flashing."
+ . += "Left-Click to activate all firelocks in this area."
+ . += "Right-Click or Alt-Click to reset firelocks in this area."
+ else
+ . += "The local area thermal detection light is [my_area.fire_detect ? "lit" : "unlit"]."
+ . += "Left-Click to activate all firelocks in this area."
// Allows Silicons to disable thermal sensor
/obj/machinery/firealarm/BorgCtrlClick(mob/living/silicon/robot/user)
@@ -364,8 +374,11 @@
if(obj_flags & EMAGGED)
to_chat(user, span_warning("The control circuitry of [src] appears to be malfunctioning."))
return
- detecting = !detecting
- to_chat(user, span_notice("You [ detecting ? "enable" : "disable" ] [src]'s detecting unit!"))
+ my_area.fire_detect = !my_area.fire_detect
+ for(var/obj/machinery/firealarm/fire_panel in my_area.firealarms)
+ fire_panel.update_icon()
+ to_chat(user, span_notice("You [ my_area.fire_detect ? "enable" : "disable" ] the local firelock thermal sensors!"))
+ log_game("[user] has [ my_area.fire_detect ? "enabled" : "disabled" ] firelock sensors using [src] at [COORD(src)]")
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/firealarm, 26)
diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm
index f38a5d59660..502a03b13fe 100644
--- a/code/game/objects/items/emags.dm
+++ b/code/game/objects/items/emags.dm
@@ -57,7 +57,7 @@
/obj/item/card/emag/Initialize(mapload)
. = ..()
- type_blacklist = list(typesof(/obj/machinery/door/airlock), typesof(/obj/machinery/door/window/)) //list of all typepaths that require a specialized emag to hack.
+ type_blacklist = list(typesof(/obj/machinery/door/airlock), typesof(/obj/machinery/door/window/), typesof(/obj/machinery/door/firedoor)) //list of all typepaths that require a specialized emag to hack.
/obj/item/card/emag/attack()
return
@@ -95,7 +95,7 @@
/obj/item/card/emag/doorjack/Initialize(mapload)
. = ..()
- type_whitelist = list(typesof(/obj/machinery/door/airlock), typesof(/obj/machinery/door/window/)) //list of all acceptable typepaths that this device can affect
+ type_whitelist = list(typesof(/obj/machinery/door/airlock), typesof(/obj/machinery/door/window/), typesof(/obj/machinery/door/firedoor)) //list of all acceptable typepaths that this device can affect
/obj/item/card/emag/doorjack/proc/use_charge(mob/user)
charges --
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index 88fbc34a969..e577684864a 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -697,11 +697,15 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
uses = 1
/datum/action/innate/ai/break_fire_alarms/Activate()
- for(var/obj/machinery/firealarm/F in GLOB.machines)
- if(!is_station_level(F.z))
+ for(var/obj/machinery/firealarm/bellman in GLOB.machines)
+ if(!is_station_level(bellman.z))
continue
- F.obj_flags |= EMAGGED
- F.update_appearance()
+ bellman.obj_flags |= EMAGGED
+ bellman.update_appearance()
+ for(var/obj/machinery/door/firedoor/firelock in GLOB.machines)
+ if(!is_station_level(firelock.z))
+ continue
+ firelock.emag_act(owner_AI, src)
to_chat(owner, span_notice("All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized."))
owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0)
diff --git a/icons/obj/doors/doorfire.dmi b/icons/obj/doors/doorfire.dmi
index 472415ec798..d0f7fb60ec5 100644
Binary files a/icons/obj/doors/doorfire.dmi and b/icons/obj/doors/doorfire.dmi differ
diff --git a/icons/obj/doors/doorfireglass.dmi b/icons/obj/doors/doorfireglass.dmi
index f00e6c2857e..243bb091f46 100644
Binary files a/icons/obj/doors/doorfireglass.dmi and b/icons/obj/doors/doorfireglass.dmi differ
diff --git a/icons/obj/doors/edge_Doorfire.dmi b/icons/obj/doors/edge_Doorfire.dmi
index 3aad0114d4b..bb6bee3fc87 100644
Binary files a/icons/obj/doors/edge_Doorfire.dmi and b/icons/obj/doors/edge_Doorfire.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 22952675d5e..4ae350dfaac 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -71,6 +71,7 @@
#include "code\__DEFINES\explosions.dm"
#include "code\__DEFINES\exports.dm"
#include "code\__DEFINES\fantasy_affixes.dm"
+#include "code\__DEFINES\firealarm.dm"
#include "code\__DEFINES\flags.dm"
#include "code\__DEFINES\food.dm"
#include "code\__DEFINES\footsteps.dm"