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/__DEFINES/~skyrat_defines/signals.dm b/code/__DEFINES/~skyrat_defines/signals.dm index 720efee1aad..144f46b3356 100644 --- a/code/__DEFINES/~skyrat_defines/signals.dm +++ b/code/__DEFINES/~skyrat_defines/signals.dm @@ -59,15 +59,6 @@ ///from base of /obj/effect/abstract/liquid_turf/Initialize() (/obj/effect/abstract/liquid_turf/liquids) #define COMSIG_TURF_LIQUIDS_CREATION "turf_liquids_creation" -// Firealarm signals -/// /obj/machinery/firealarm/proc/trigger_effects(manual = FALSE) -#define COMSIG_FIREALARM_TRIGGERED_ON "firealarm_triggered_on" -#define COMSIG_FIREALARM_TRIGGER_DOORS "firealarm_trigger_doors" -/// /obj/machinery/firealarm/proc/untrigger_effects() -#define COMSIG_FIREALARM_TRIGGERED_OFF "firealarm_triggered_off" -/// /obj/machinery/door/firedoor/proc/trigger_hot() -#define COMSIG_FIREDOOR_CLOSED_FIRE "firedoor_closed_fire" - //when someone casts their fishing rod #define COMSIG_START_FISHING "start_fishing" //when someone pulls back their fishing rod 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 f9e68cf6e56..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 SKYRAT EDIT REMOVAL - 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 @@ -240,84 +243,6 @@ GLOBAL_LIST_EMPTY(teleportlocs) STOP_PROCESSING(SSobj, src) QDEL_NULL(alarm_manager) return ..() -/* SKYRAT EDIT REMOVAL -/** - * 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,15 +293,12 @@ 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 4827f5a3a35..e641fbf588e 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,11 +1,12 @@ #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" desc = "Apply crowbar." - icon = 'icons/obj/doors/Doorfireglass.dmi' //ICON OVERRIDEN IN SKYRAT AESTHETICS - SEE MODULE + icon = 'icons/obj/doors/Doorfireglass.dmi' icon_state = "door_open" opacity = FALSE density = FALSE @@ -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) . = ..() @@ -39,39 +100,152 @@ else if(!welded) . += span_notice("It is closed, but could be pried open.") . += span_notice("Hold the firelock temporarily open by prying it with left-click and standing next to it.") - //. += span_notice("Prying by right-clicking the firelock will open it permanently.") SKYRAT EDIT REMOVAL + . += span_notice("Prying by right-clicking the firelock will open it permanently.") . += span_notice("Deconstruction would require it to be welded shut.") else if(boltslocked) . += span_notice("It is welded shut. The floor bolts have been locked by screws.") else . += span_notice("The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor.") -/* //SKYRAT EDIT REMOVAL BEGIN +/** + * 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) -*/ //SKYRAT EDIT END + 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 -/obj/machinery/door/firedoor/closed - icon_state = "door_closed" - density = TRUE - -/* //SKYRAT EDIT REMOVAL BEGIN -//see also turf/AfterChange for adjacency shennanigans +/** + * 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 ..() -*/ //SKYRAT EDIT REMOVAL END +/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 >= BODYTEMP_HEAT_DAMAGE_LIMIT || environment?.volume > WARNING_HIGH_PRESSURE) //SKYRAT EDIT CHANGE - BETTER LOCKS + result = FIRELOCK_ALARM_TYPE_HOT + if(environment?.temperature <= BODYTEMP_COLD_DAMAGE_LIMIT || environment?.volume < WARNING_LOW_PRESSURE) //SKYRAT EDIT CHANGE - BETTER LOCKS + 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) @@ -85,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) . = ..() @@ -143,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) @@ -170,6 +351,8 @@ if(density) open() + if(alarm_type) + addtimer(CALLBACK(src, .proc/correct_state), 2 SECONDS, TIMER_UNIQUE) else close() @@ -180,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) @@ -193,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 @@ -206,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) @@ -214,54 +401,77 @@ if("closing") flick("door_closing", src) -/*SKYRAT EDIT REMOVAL /obj/machinery/door/firedoor/update_icon_state() . = ..() icon_state = "[base_icon_state]_[density ? "closed" : "open"]" -*/ /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' @@ -281,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) . = ..() @@ -306,7 +535,7 @@ /obj/machinery/door/firedoor/heavy name = "heavy firelock" - icon = 'icons/obj/doors/Doorfire.dmi' //ICON OVERRIDEN IN SKYRAT AESTHETICS - SEE MODULE + icon = 'icons/obj/doors/Doorfire.dmi' //SKYRAT EDIT - ICON OVERRIDEN IN AESTHETICS MODULE glass = FALSE explosion_block = 2 assemblytype = /obj/structure/firelock_frame/heavy @@ -321,7 +550,7 @@ /obj/structure/firelock_frame name = "firelock frame" desc = "A partially completed firelock." - icon = 'icons/obj/doors/Doorfire.dmi' //ICON OVERRIDEN IN SKYRAT AESTHETICS - SEE MODULE + icon = 'icons/obj/doors/Doorfire.dmi' //SKYRAT EDIT - ICON OVERRIDEN IN AESTHETICS MODULE icon_state = "frame1" base_icon_state = "frame" anchored = FALSE @@ -343,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 @@ -361,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 @@ -381,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.")) @@ -471,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 0bf94df8f17..69f0af98c1f 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -1,5 +1,3 @@ -#define FIREALARM_COOLDOWN 80 //SKYRAT EDIT CHANGE - ORIGINAL: 96 - AESTHETIC // 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." @@ -7,7 +5,7 @@ /obj/item/wallframe/firealarm name = "fire alarm frame" desc = "Used for building fire alarms." - icon = 'icons/obj/monitors.dmi' //ICON OVERRIDEN IN SKYRAT AESTHETICS - SEE MODULE + icon = 'icons/obj/monitors.dmi' icon_state = "fire_bitem" result_path = /obj/machinery/firealarm pixel_shift = 26 @@ -15,7 +13,7 @@ /obj/machinery/firealarm name = "fire alarm" desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever." - icon = 'icons/obj/monitors.dmi' //ICON OVERRIDEN IN SKYRAT AESTHETICS - SEE MODULE + icon = 'icons/obj/monitors.dmi' icon_state = "fire0" max_integrity = 250 integrity_failure = 0.4 @@ -32,15 +30,13 @@ //Trick to get the glowing overlay visible from a distance luminosity = 1 + ///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 - 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 - -/* SKYRAT EDIT REMOVAL /obj/machinery/firealarm/Initialize(mapload, dir, building) . = ..() if(building) @@ -49,18 +45,34 @@ 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) @@ -87,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") @@ -102,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 @@ -123,30 +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) - -/* SKYRAT EDIT REMOVAL -/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 @@ -161,45 +151,69 @@ if(is_station_level(z)) update_appearance() -/*SKYRAT EDIT REMOVAL +/** + * 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) ORIGINAL - playsound(loc, alarm_sound, 75) //SKYRAT EDIT CHANGE - AESTHETICS + + 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) -/*SKYRAT EDIT REMOVAL +/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) @@ -227,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) @@ -302,9 +308,7 @@ tool.play_tool_sound(src) qdel(src) return - return ..() -*/ /obj/machinery/firealarm/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) if((buildstage == 0) && (the_rcd.upgrade & RCD_UPGRADE_SIMPLE_CIRCUITS)) @@ -331,16 +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 ..() -/* SKYRAT EDIT REMOVAL /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)) @@ -352,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) @@ -376,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/effects/spawners/structure.dm b/code/game/objects/effects/spawners/structure.dm index 2b307cf0689..09368b8a170 100644 --- a/code/game/objects/effects/spawners/structure.dm +++ b/code/game/objects/effects/spawners/structure.dm @@ -91,7 +91,7 @@ again. /obj/effect/spawner/structure/window/reinforced name = "reinforced window spawner" icon_state = "rwindow_spawner" - spawn_list = list(/obj/machinery/door/firedoor/onetile, /obj/structure/grille, /obj/structure/window/reinforced/fulltile) //SKYRAT EDIT CHANGE + spawn_list = list(/obj/machinery/door/firedoor, /obj/structure/grille, /obj/structure/window/reinforced/fulltile) //SKYRAT EDIT CHANGE /obj/effect/spawner/structure/window/hollow/reinforced name = "hollow reinforced window spawner" diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm index 011f2ca655d..5af3ce53824 100644 --- a/code/game/objects/items/emags.dm +++ b/code/game/objects/items/emags.dm @@ -61,7 +61,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 @@ -101,7 +101,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 c2a91e71b42..78f825fb7fe 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/modular_skyrat/modules/aesthetics/firealarm/code/firealarm.dm b/modular_skyrat/modules/aesthetics/firealarm/code/firealarm.dm index ad81f36275d..8ccf169d457 100644 --- a/modular_skyrat/modules/aesthetics/firealarm/code/firealarm.dm +++ b/modular_skyrat/modules/aesthetics/firealarm/code/firealarm.dm @@ -1,199 +1,2 @@ /obj/machinery/firealarm icon = 'modular_skyrat/modules/aesthetics/firealarm/icons/firealarm.dmi' - var/alarm_sound = 'modular_skyrat/modules/aesthetics/firealarm/sound/alarm_fire.ogg' - -/obj/machinery/firealarm/Initialize(mapload, dir, building) - . = ..() - if(dir) - src.setDir(dir) - if(building) - buildstage = 0 - panel_open = TRUE - pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) - pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 - if(name == initial(name)) - name = "[get_area_name(src)] [initial(name)]" - - RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, .proc/check_security_level) - - gather_objects() - - update_appearance() - -/obj/machinery/firealarm/proc/gather_objects() - var/area/my_area = get_area(src) - - for(var/obj/machinery/light/iterating_light in my_area) - iterating_light.RegisterSignal(src, COMSIG_FIREALARM_TRIGGERED_ON, /obj/machinery/light.proc/firealarm_on) - iterating_light.RegisterSignal(src, COMSIG_FIREALARM_TRIGGERED_OFF, /obj/machinery/light.proc/firealarm_off) - - for(var/obj/machinery/door/firedoor/iterating_firedoor in my_area) - iterating_firedoor.RegisterSignal(src, COMSIG_FIREALARM_TRIGGER_DOORS, /obj/machinery/door/firedoor.proc/firealarm_on) - iterating_firedoor.RegisterSignal(src, COMSIG_FIREALARM_TRIGGERED_OFF, /obj/machinery/door/firedoor.proc/firealarm_off) - - for(var/obj/machinery/firealarm/iterating_firealarm in my_area) - if(iterating_firealarm == src) //Bad infinite loops. - continue - iterating_firealarm.RegisterSignal(src, COMSIG_FIREALARM_TRIGGERED_ON, .proc/area_alarm_on) - iterating_firealarm.RegisterSignal(src, COMSIG_FIREALARM_TRIGGERED_OFF, .proc/area_alarm_off) - -/obj/machinery/firealarm/proc/area_alarm_on() - SIGNAL_HANDLER - - triggered = TRUE - update_fire_light(TRUE) - update_appearance() - -/obj/machinery/firealarm/proc/area_alarm_off() - SIGNAL_HANDLER - - triggered = FALSE - update_fire_light(FALSE) - update_appearance() - -/obj/machinery/firealarm/proc/alarm(mob/user) - if(triggered) - return - triggered = TRUE - if(!is_operational || !COOLDOWN_FINISHED(src, last_alarm)) - return - COOLDOWN_START(src, last_alarm, FIREALARM_COOLDOWN) - playsound(loc, alarm_sound, 75) - if(user) - log_game("[user] triggered a fire alarm at [COORD(src)]") - trigger_effects() - trigger_doors() - update_appearance() - -/obj/machinery/firealarm/proc/trigger_doors() - SEND_SIGNAL(src, COMSIG_FIREALARM_TRIGGER_DOORS) - -/obj/machinery/firealarm/proc/trigger_effects() - SIGNAL_HANDLER - SEND_SIGNAL(src, COMSIG_FIREALARM_TRIGGERED_ON) - -/obj/machinery/firealarm/proc/untrigger_effects() - SEND_SIGNAL(src, COMSIG_FIREALARM_TRIGGERED_OFF) - -/obj/machinery/firealarm/proc/reset(mob/user) - if(!is_operational) - return - if(user) - log_game("[user] reset a fire alarm at [COORD(src)]") - triggered = FALSE - untrigger_effects() - update_appearance() - -/obj/machinery/firealarm/attack_hand(mob/user, list/modifiers) - if(buildstage != 2) - return ..() - add_fingerprint(user) - if(triggered) - reset(user) - else - alarm(user, TRUE) - -/obj/machinery/firealarm/attackby(obj/item/tool, mob/living/user, params) - add_fingerprint(user) - - if(tool.tool_behaviour == TOOL_SCREWDRIVER && buildstage == 2) - tool.play_tool_sound(src) - panel_open = !panel_open - to_chat(user, span_notice("The wires have been [panel_open ? "exposed" : "unexposed"].")) - update_appearance() - return - - if(panel_open) - - if(tool.tool_behaviour == TOOL_WELDER && !user.combat_mode) - if(atom_integrity < max_integrity) - if(!tool.tool_start_check(user, amount=0)) - return - - to_chat(user, span_notice("You begin repairing [src]...")) - if(tool.use_tool(src, user, 40, volume=50)) - atom_integrity = max_integrity - to_chat(user, span_notice("You repair [src].")) - else - to_chat(user, span_warning("[src] is already in good condition!")) - return - - 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) - buildstage = 1 - tool.play_tool_sound(src) - new /obj/item/stack/cable_coil(user.loc, 5) - to_chat(user, span_notice("You cut the wires from \the [src].")) - update_appearance() - return - - else if(tool.force) //hit and turn it on - ..() - if(!triggered) - alarm() - return - - if(1) - if(istype(tool, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = tool - if(coil.get_amount() < 5) - to_chat(user, span_warning("You need more cable for this!")) - else - coil.use(5) - buildstage = 2 - to_chat(user, span_notice("You wire \the [src].")) - update_appearance() - return - - else if(tool.tool_behaviour == TOOL_CROWBAR) - user.visible_message(span_notice("[user.name] removes the electronics from [src.name]."), \ - span_notice("You start prying out the circuit...")) - if(tool.use_tool(src, user, 20, volume=50)) - if(buildstage == 1) - if(machine_stat & BROKEN) - to_chat(user, span_notice("You remove the destroyed circuit.")) - set_machine_stat(machine_stat & ~BROKEN) - else - to_chat(user, span_notice("You pry out the circuit.")) - new /obj/item/electronics/firealarm(user.loc) - buildstage = 0 - update_appearance() - return - if(0) - if(istype(tool, /obj/item/electronics/firealarm)) - to_chat(user, span_notice("You insert the circuit.")) - qdel(tool) - buildstage = 1 - update_appearance() - return - - else if(istype(tool, /obj/item/electroadaptive_pseudocircuit)) - var/obj/item/electroadaptive_pseudocircuit/pseudoc = tool - if(!pseudoc.adapt_circuit(user, 15)) - return - user.visible_message(span_notice("[user] fabricates a circuit and places it into [src]."), \ - span_notice("You adapt a fire alarm circuit and slot it into the assembly.")) - buildstage = 1 - update_appearance() - return - - else if(tool.tool_behaviour == TOOL_WRENCH) - user.visible_message(span_notice("[user] removes the fire alarm assembly from the wall."), \ - span_notice("You remove the fire alarm assembly from the wall.")) - var/obj/item/wallframe/firealarm/frame = new /obj/item/wallframe/firealarm() - frame.forceMove(user.drop_location()) - tool.play_tool_sound(src) - qdel(src) - return - - return ..() - diff --git a/modular_skyrat/modules/aesthetics/firedoor/code/firedoor.dm b/modular_skyrat/modules/aesthetics/firedoor/code/firedoor.dm index 82eadaf9ea7..67a6dcabf11 100644 --- a/modular_skyrat/modules/aesthetics/firedoor/code/firedoor.dm +++ b/modular_skyrat/modules/aesthetics/firedoor/code/firedoor.dm @@ -1,116 +1,9 @@ -#define FIREDOOR_CLOSE_OVERRIDE_RESET_TIME 3 MINUTES - /obj/machinery/door/firedoor name = "emergency shutter" desc = "Emergency air-tight shutter, capable of sealing off breached areas. This one has a glass panel. It has a mechanism to open it with just your hands." icon = 'modular_skyrat/modules/aesthetics/firedoor/icons/firedoor_glass.dmi' - req_access = list(ACCESS_ATMOSPHERICS) - - var/door_open_sound = 'modular_skyrat/modules/aesthetics/firedoor/sound/firedoor_open.ogg' - var/door_close_sound = 'modular_skyrat/modules/aesthetics/firedoor/sound/firedoor_open.ogg' - var/hot_or_cold = FALSE //True for hot, false for cold - - var/stay_open = FALSE // Do we stay open? Triggered via atmos techs using their ID on it. - var/has_hotcold_sprite = TRUE //does it actually have the sprites needed - /obj/machinery/door/firedoor/heavy name = "heavy emergency shutter" desc = "Emergency air-tight shutter, capable of sealing off breached areas. It has a mechanism to open it with just your hands." icon = 'modular_skyrat/modules/aesthetics/firedoor/icons/firedoor.dmi' - -/obj/structure/firelock_frame - icon = 'modular_skyrat/modules/aesthetics/firedoor/icons/firedoor.dmi' - -/obj/machinery/door/firedoor/border_only - has_hotcold_sprite = FALSE - -/obj/machinery/door/firedoor/open() - playsound(loc, door_open_sound, 90, TRUE) - . = ..() - -/obj/machinery/door/firedoor/close() - if(stay_open) - return - playsound(loc, door_close_sound, 90, TRUE) - return ..() - -/obj/machinery/door/firedoor/proc/atmos_changed(datum/source, datum/gas_mixture/air, exposed_temperature) - if(density) - return - var/pressure = air.return_pressure() - if(exposed_temperature < BODYTEMP_COLD_DAMAGE_LIMIT || pressure < WARNING_LOW_PRESSURE) - hot_or_cold = FALSE - close() - else if(exposed_temperature > BODYTEMP_HEAT_DAMAGE_LIMIT || pressure > WARNING_HIGH_PRESSURE) - hot_or_cold = TRUE - trigger_hot() - close() - -/obj/machinery/door/firedoor/proc/firealarm_on() - SIGNAL_HANDLER - INVOKE_ASYNC(src, .proc/close) - -/obj/machinery/door/firedoor/proc/firealarm_off() - SIGNAL_HANDLER - INVOKE_ASYNC(src, .proc/open) - -/obj/machinery/door/firedoor/proc/trigger_hot() - SEND_SIGNAL(src, COMSIG_FIREDOOR_CLOSED_FIRE) - -/obj/machinery/door/firedoor/proc/CalculateAffectingAreas() - for(var/turf/adjacent_turf in range(1, src)) - RegisterSignal(adjacent_turf, COMSIG_TURF_EXPOSE, .proc/atmos_changed, override = TRUE) - - var/my_area = get_area(src) - - for(var/obj/machinery/firealarm/iterating_alarm in my_area) - iterating_alarm.RegisterSignal(src, COMSIG_FIREDOOR_CLOSED_FIRE, /obj/machinery/firealarm.proc/trigger_effects, override = TRUE) //We trigger the alarms automatically. - -/obj/machinery/door/firedoor/update_icon_state() - . = ..() - if(density) - icon_state = "[base_icon_state]_closed[has_hotcold_sprite ? (hot_or_cold ? "_hot" : "_cold") : ""]" - else - icon_state = "[base_icon_state]_open" - -/obj/machinery/door/firedoor/onetile/CalculateAffectingAreas() - RegisterSignal(loc, COMSIG_TURF_EXPOSE, .proc/atmos_changed, override = TRUE) - - var/my_area = get_area(src) - - for(var/obj/machinery/firealarm/iterating_alarm in my_area) - iterating_alarm.RegisterSignal(src, COMSIG_FIREDOOR_CLOSED_FIRE, /obj/machinery/firealarm.proc/trigger_effects, override = TRUE) //We trigger the alarms automatically. - -/obj/machinery/door/firedoor/attackby(obj/item/C, mob/user, params) - if(C.GetID()) - if(allowed(user)) - toggle_close_override(user) - return ..() - - -/obj/machinery/door/firedoor/CtrlClick(mob/user) - if(!user.canUseTopic(src, !issilicon(user))) - return - toggle_close_override(user) - -/obj/machinery/door/firedoor/proc/toggle_close_override(mob/user) - stay_open = !stay_open - balloon_alert(user, "close override [stay_open ? "engaged" : "disengaged"]") - addtimer(CALLBACK(src, .proc/close_override_reset), FIREDOOR_CLOSE_OVERRIDE_RESET_TIME) - -/obj/machinery/door/firedoor/proc/close_override_reset() - stay_open = FALSE - -/obj/machinery/door/firedoor/examine(mob/user) - . = ..() - if(issilicon(user)) - . += span_notice("You can override the closing mechanism by Ctrl+Clicking on the firelock.") - else - . += span_notice("You can override the closing mechanism by swiping your ID on it, providing it has Atmospherics Access.") - . += span_notice("It's close override mechanism is [stay_open ? "engaged" : "disengaged"].") - -/obj/effect/spawner/structure/window/reinforced/no_firelock - spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/fulltile) - -#undef FIREDOOR_CLOSE_OVERRIDE_RESET_TIME diff --git a/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor.dmi b/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor.dmi index ab738947228..ff3df706eb3 100644 Binary files a/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor.dmi and b/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor.dmi differ diff --git a/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor_glass.dmi b/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor_glass.dmi index b94ec448eac..3aadeda915c 100644 Binary files a/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor_glass.dmi and b/modular_skyrat/modules/aesthetics/firedoor/icons/firedoor_glass.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 46541af0cb3..b877dc929e4 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"