mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 15:17:01 +01:00
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:
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 |
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user