Moves atmos checking from Fire Alarms to Fire Locks, Fire Locks now close on their own rather than area-based, a bunch of related stuff. (#62055)

Moves hot/cold checks to the fire locks
Makes detection due to above only close the affected Fire Lock and its neighbors (via merge code stolen from tanks)
Fire Alarms still can lock an area down if clicked by a user, right clicking is now the reset function. Resetting also resets all Fire Locks closed by their own detection.
Gives Fire Locks an overlay of lights that display the reason it closed (Orange for Hot, Cyan for Cold, White for unknown/Fire Alarm Pulled)
Emagging a Fire Alarm now emags all Fire Locks in the area, which accomplishes the same effect as before (disables atmos monitoring). Pulling a Fire Alarm will make emagged Fire Locks in the area display a corrupted warning light pattern, making finding and replacing emagged Fire Locks somewhat easier.
Silicons can disable atmos sensors in an area by CTRL-Clicking a Fire Alarm, much like before. but now the Fire Alarms in the area will change their sprite to hint at this (the normally-green light will turn off).

Makes a breach or fire not cause Fire Lock Hell anymore. Only the Fire Locks facing an issue will tend to close, leaving the rest of the area mostly unaffected.

This needs some work done to make it detect based on edges, but it's a decent enough start to live with if we're all killed by meteors (Lemon)
This commit is contained in:
zxaber
2022-01-22 01:36:03 -08:00
committed by GitHub
parent 7c7362836e
commit 4383df09ae
12 changed files with 450 additions and 247 deletions
+1 -1
View File
@@ -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"
+13
View File
@@ -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"
@@ -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
+15 -94
View File
@@ -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
+303 -68
View File
@@ -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 <i>unscrewed</i>, but the bolts themselves are still <b>wrenched</b> 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
+91 -78
View File
@@ -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."
. += "<b>Left-Click</b> to activate all firelocks in this area."
. += "<b>Right-Click</b> or <b>Alt-Click</b> to reset firelocks in this area."
else
. += "The local area thermal detection light is [my_area.fire_detect ? "lit" : "unlit"]."
. += "<b>Left-Click</b> 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)
+2 -2
View File
@@ -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 --
@@ -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)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 20 KiB

+1
View File
@@ -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"