From ff39ceff9ca10eaefd330788ee10b6a96dd0b661 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Tue, 9 Sep 2014 14:41:07 +0200 Subject: [PATCH] Emergency shutters down have pressure/temperature difference overlays. Based on the emergency shutter from Paradise. --- code/__HELPERS/game.dm | 63 +++++++++- code/game/machinery/doors/door.dm | 16 ++- code/game/machinery/doors/firedoor.dm | 171 ++++++++++++++++++++++---- icons/obj/doors/DoorHazard.dmi | Bin 2669 -> 4743 bytes 4 files changed, 216 insertions(+), 34 deletions(-) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 77ed7f308e..8c6befa38f 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -398,4 +398,65 @@ datum/projectile_data var/r = mixOneColor(weights, reds) var/g = mixOneColor(weights, greens) var/b = mixOneColor(weights, blues) - return rgb(r,g,b) \ No newline at end of file + return rgb(r,g,b) + +/** +* Gets the highest and lowest pressures from the tiles in cardinal directions +* around us, then checks the difference. +*/ +/proc/getOPressureDifferential(var/turf/loc) + var/minp=16777216; + var/maxp=0; + for(var/dir in cardinal) + var/turf/simulated/T=get_turf(get_step(loc,dir)) + var/cp=0 + if(T && istype(T) && T.zone) + var/datum/gas_mixture/environment = T.return_air() + cp = environment.return_pressure() + else + if(istype(T,/turf/simulated)) + continue + if(cpmaxp)maxp=cp + return abs(minp-maxp) + +/proc/convert_k2c(var/temp) + return ((temp - T0C)) + +/proc/convert_c2k(var/temp) + return ((temp + T0C)) + +/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature")) + var/list/temps = new/list(4) + for(var/dir in cardinal) + var/direction + switch(dir) + if(NORTH) + direction = 1 + if(SOUTH) + direction = 2 + if(EAST) + direction = 3 + if(WEST) + direction = 4 + var/turf/simulated/T=get_turf(get_step(loc,dir)) + var/list/rstats = new /list(stats.len) + if(T && istype(T) && T.zone) + var/datum/gas_mixture/environment = T.return_air() + for(var/i=1;i<=stats.len;i++) + if(stats[i] == "pressure") + rstats[i] = environment.return_pressure() + else + rstats[i] = environment.vars[stats[i]] + else if(istype(T, /turf/simulated)) + rstats = null // Exclude zone (wall, door, etc). + else if(istype(T, /turf)) + // Should still work. (/turf/return_air()) + var/datum/gas_mixture/environment = T.return_air() + for(var/i=1;i<=stats.len;i++) + if(stats[i] == "pressure") + rstats[i] = environment.return_pressure() + else + rstats[i] = environment.vars[stats[i]] + temps[direction] = rstats + return temps diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 12f224ac52..c49da985df 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -1,4 +1,6 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +#define DOOR_OPEN_LAYER 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6 +#define DOOR_CLOSED_LAYER 3.1 //Above most items if closed /obj/machinery/door name = "Door" @@ -8,7 +10,9 @@ anchored = 1 opacity = 1 density = 1 - layer = 2.7 + layer = DOOR_OPEN_LAYER + var/open_layer = DOOR_OPEN_LAYER + var/closed_layer = DOOR_CLOSED_LAYER var/secondsElectrified = 0 var/visible = 1 @@ -27,11 +31,11 @@ /obj/machinery/door/New() . = ..() if(density) - layer = 3.1 //Above most items if closed + layer = closed_layer explosion_resistance = initial(explosion_resistance) update_heat_protection(get_turf(src)) else - layer = 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6 + layer = open_layer explosion_resistance = 0 @@ -226,7 +230,7 @@ icon_state = "door0" src.SetOpacity(0) sleep(10) - src.layer = 2.7 + src.layer = open_layer src.density = 0 explosion_resistance = 0 update_icon() @@ -250,10 +254,10 @@ if(operating > 0) return operating = 1 - do_animate("closing") src.density = 1 explosion_resistance = initial(explosion_resistance) - src.layer = 3.1 + src.layer = closed_layer + do_animate("closing") sleep(10) update_icon() if(visible && !glass) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 40bc8d7e51..299238e42e 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,5 +1,15 @@ /var/const/OPEN = 1 /var/const/CLOSED = 2 + +#define FIREDOOR_MAX_PRESSURE_DIFF 25 // kPa +#define FIREDOOR_MAX_TEMP 50 // °C +#define FIREDOOR_MIN_TEMP 0 + +// Bitflags +#define FIREDOOR_ALERT_HOT 1 +#define FIREDOOR_ALERT_COLD 2 +// Not used #define FIREDOOR_ALERT_LOWPRESS 4 + /obj/machinery/door/firedoor name = "\improper Emergency Shutter" desc = "Emergency air-tight shutter, capable of sealing off breached areas." @@ -8,17 +18,33 @@ req_one_access = list(access_atmospherics, access_engine_equip) opacity = 0 density = 0 + layer = DOOR_OPEN_LAYER - 0.1 + open_layer = DOOR_OPEN_LAYER - 0.1 // Just below doors when open + closed_layer = DOOR_CLOSED_LAYER + 0.1 // Just above doors when closed var/blocked = 0 + var/lockdown = 0 // When the door has detected a problem, it locks. + var/pdiff_alert = 0 + var/pdiff = 0 var/nextstate = null var/net_id var/list/areas_added - var/list/users_to_open - + var/list/users_to_open = new + var/next_process_time = 0 + power_channel = ENVIRON use_power = 1 idle_power_usage = 5 + var/list/tile_info[4] + var/list/dir_alerts[4] // 4 dirs, bitflags + + // MUST be in same order as FIREDOOR_ALERT_* + var/list/ALERT_STATES=list( + "hot", + "cold" + ) + /obj/machinery/door/firedoor/New() . = ..() for(var/obj/machinery/door/firedoor/F in loc) @@ -38,7 +64,6 @@ A.all_doors.Add(src) areas_added += A - /obj/machinery/door/firedoor/Del() for(var/area/A in areas_added) A.all_doors.Remove(src) @@ -48,14 +73,47 @@ /obj/machinery/door/firedoor/examine() set src in view() . = ..() - if( islist(users_to_open) && users_to_open.len) + + if(get_dist(src, usr) > 1 && !isAI(usr)) + return + + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + usr << "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!" + + usr << "Sensor readings:" + for(var/index = 1; index <= tile_info.len; index++) + var/o = "  " + switch(index) + if(1) + o += "NORTH: " + if(2) + o += "SOUTH: " + if(3) + o += "EAST: " + if(4) + o += "WEST: " + if(tile_info[index] == null) + o += "DATA UNAVAILABLE" + usr << o + continue + var/celsius = convert_k2c(tile_info[index][1]) + var/pressure = tile_info[index][2] + if(dir_alerts[index] & (FIREDOOR_ALERT_HOT|FIREDOOR_ALERT_COLD)) + o += "" + else + o += "" + o += "[celsius]°C " + o += "" + o += "[pressure]kPa" + usr << o + + if(islist(users_to_open) && users_to_open.len) var/users_to_open_string = users_to_open[1] if(users_to_open.len >= 2) for(var/i = 2 to users_to_open.len) users_to_open_string += ", [users_to_open[i]]" usr << "These people have opened \the [src] during an alert: [users_to_open_string]." - /obj/machinery/door/firedoor/Bumped(atom/AM) if(p_open || operating) return @@ -63,7 +121,7 @@ return ..() if(istype(AM, /obj/mecha)) var/obj/mecha/mecha = AM - if (mecha.occupant) + if(mecha.occupant) var/mob/M = mecha.occupant if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent popup message spam. M.last_bumped = world.time @@ -79,12 +137,7 @@ user << "\The [src] is welded solid!" return - if(!allowed(user)) - user << "Access denied." - return - - var/alarmed = 0 - + var/alarmed = lockdown for(var/area/A in areas_added) //Checks if there are fire alarms in any areas associated with that firedoor if(A.fire || A.air_doors_activated) alarmed = 1 @@ -100,9 +153,21 @@ user << "\The [src] is not functioning, you'll have to force it open manually." return + if(alarmed && density && lockdown && !allowed(user)) + user << "Access denied. Please wait for authorities to arrive, or for the alert to clear." + return + else + user.visible_message("\The [src] [density ? "open" : "close"]s for \the [user].",\ + "\The [src] [density ? "open" : "close"]s.",\ + "You hear a beep, and a door opening.") + var/needs_to_close = 0 if(density) if(alarmed) + // Accountability! + users_to_open |= user.name + log_admin("[user]([user.ckey]) has opened an alarming emergency shutter.") + message_admins("[user]([user.ckey]) has opened an alarming emergency shutter.") needs_to_close = 1 spawn() open() @@ -128,38 +193,37 @@ var/obj/item/weapon/weldingtool/W = C if(W.remove_fuel(0, user)) blocked = !blocked - user.visible_message("\red \The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ + user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ "You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\ "You hear something being welded.") update_icon() return if(blocked) - user << "\red \The [src] is welded solid!" + user << "\The [src] is welded solid!" return - - if( istype(C, /obj/item/weapon/crowbar) || ( istype(C,/obj/item/weapon/twohanded/fireaxe) && C:wielded == 1 ) ) + if(istype(C, /obj/item/weapon/crowbar) || ( istype(C,/obj/item/weapon/twohanded/fireaxe) && C:wielded == 1)) if(operating) return - if( blocked && istype(C, /obj/item/weapon/crowbar) ) - user.visible_message("\red \The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ + if(blocked && istype(C, /obj/item/weapon/crowbar)) + user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ "You try to pry \the [src] [density ? "open" : "closed"], but it is welded in place!",\ "You hear someone struggle and metal straining.") return - user.visible_message("\red \The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ + user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ "You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\ "You hear metal strain.") if(do_after(user,30)) - if( istype(C, /obj/item/weapon/crowbar) ) - if( stat & (BROKEN|NOPOWER) || !density) - user.visible_message("\red \The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ + if(istype(C, /obj/item/weapon/crowbar)) + if(stat & (BROKEN|NOPOWER) || !density) + user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ "You force \the [src] [density ? "open" : "closed"] with \the [C]!",\ "You hear metal strain, and a door [density ? "open" : "close"].") else - user.visible_message("\red \The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\ + user.visible_message("\The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\ "You force \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \the [C]!",\ "You hear metal strain and groan, and a door [density ? "opening" : "closing"].") if(density) @@ -170,7 +234,50 @@ close() return +// CHECK PRESSURE +/obj/machinery/door/firedoor/process() + ..() + if(density && next_process_time <= world.time) + next_process_time = world.time + 100 // 10 second delays between process updates + var/changed = 0 + lockdown=0 + // Pressure alerts + pdiff = getOPressureDifferential(src.loc) + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + lockdown = 1 + if(!pdiff_alert) + pdiff_alert = 1 + changed = 1 // update_icon() + else + if(pdiff_alert) + pdiff_alert = 0 + changed = 1 // update_icon() + + tile_info = getCardinalAirInfo(src.loc,list("temperature","pressure")) + var/old_alerts = dir_alerts + for(var/index = 1; index <= 4; index++) + var/list/tileinfo=tile_info[index] + if(tileinfo==null) + continue // Bad data. + var/celsius = convert_k2c(tileinfo[1]) + + var/alerts=0 + + // Temperatures + if(celsius >= FIREDOOR_MAX_TEMP) + alerts |= FIREDOOR_ALERT_HOT + lockdown = 1 + else if(celsius <= FIREDOOR_MIN_TEMP) + alerts |= FIREDOOR_ALERT_COLD + lockdown = 1 + + dir_alerts[index]=alerts + + if(dir_alerts != old_alerts) + changed = 1 + if(changed) + update_icon() /obj/machinery/door/firedoor/proc/latetoggle() if(operating || !nextstate) @@ -178,6 +285,7 @@ switch(nextstate) if(OPEN) nextstate = null + open() if(CLOSED) nextstate = null @@ -189,15 +297,17 @@ return ..() /obj/machinery/door/firedoor/open(var/forced = 0) - if (!forced) - if (stat & (BROKEN|NOPOWER)) + if(!forced) + if(stat & (BROKEN|NOPOWER)) return //needs power to open unless it was forced else use_power(360) + else + log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.") + message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.") latetoggle() return ..() - /obj/machinery/door/firedoor/do_animate(animation) switch(animation) if("opening") @@ -213,6 +323,14 @@ icon_state = "door_closed" if(blocked) overlays += "welded" + if(pdiff_alert) + overlays += "palert" + if(dir_alerts) + for(var/d=1;d<=4;d++) + var/cdir = cardinal[d] + for(var/i=1;i<=ALERT_STATES.len;i++) + if(dir_alerts[d] & (1<<(i-1))) + overlays += new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir) else icon_state = "door_open" if(blocked) @@ -220,7 +338,6 @@ return - /obj/machinery/door/firedoor/border_only //These are playing merry hell on ZAS. Sorry fellas :( /* diff --git a/icons/obj/doors/DoorHazard.dmi b/icons/obj/doors/DoorHazard.dmi index 757c94f32704ddaf541ce82e8f819dc782498c36..594c65b44f94ba2191468c30fed864f0118161c0 100644 GIT binary patch literal 4743 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 literal 2669 zcma)8X1ZmMQ)xD2 zIWv{}R2HT}WiB&LnxmmNO3E!V7e*07fs5UlulIL9ob$ftU7q)O&iQh_4+_|)t7W1E z0Dvydhq_;F`Ra8I0afq+TDcIfHcLTYg?g!NXlN)7hbuY+y%GycqCrOLHDBI`P4Qvs zl;|Ps6&J>5Yt{y8{>sp_wzg(c5f}`{#>R$Dr^l)*2*CF3+nEeTrP_}LDk>`a)lMbA zIg898A^x3*K%>#Ry1KGK=2B?Lj7kAn4-kOdt?aQ&auKS#fHT zkd*xaApm)LbVEj8M(Dv5>bJ>9la42(9FM01Kw5Rfeh;mDJj&I&36b%G;l1S-e!)cr za+XDGUCnU|+RfFqZmoN)gLV}i{q}^17tGDSnC!W3HFk@Cdho0*Z6)Smjj^iN?as!j+j>@)N;vJwA*c~i zv|)#qWliYJm{Z2Opz*n2Nc*Cr&ZJ0CyCfkdIcaC2PI-N8G*?%LO)3!|GEND|&p3jl zMa2#nf>yG#e$IUHodO$T>Jd)zJ?$-vW=^WguKtq3G*Lwzy>BY5A7sZ`DDK((n=itaNdUiv!ejVzuQFn%J2@+`zEj#Xp>s zRMa#m?u$wK3)O=)S=Z?qFfBb>_(X!#QVX=qVqq#Fn2FV+`J9PW?o$a0NsQGH8ZG(_ zrJpn+FP;VRtS?_HWozN~T_?Vfa#EVv8nrZ$b{8#*lj2Ct{D)}{A z%^;=ey>A{A$kWF40)!1JHkF_zSkajIq^{Y zGHL4g;>`k$|27KIRoc!F9(CSjWq}RmIuhq=w7WggwFMfvg{k(}o*L+ab*6)$3o`E6pF%En2J|b0vN2q%hK=IMs&mH7YRQqIw&rPxxF4^#caZ z&FwMT{6@GMv(Yvx+*P@iz@;VS**z->jGMnKhm7KMj_E9cyQoAr_e#~wdpJ8L&!xIw zl`PFakK$9#HDQ76PeOKf8@)MJD3L-%e=S(@`{a;>k1_+%)t+*mr1_F87P5XVqmw-E19W_dtCbyi5ITYXU5}o2Y(;X zFOh{E3?kvhsc$v*cDOk{3qAHu{)mV`ePlG|40gcjTzulWo}QVL$EXV6l+x`SHq~6p zqMk{fAZ#&d%K_{eWe)~_x-UWgmO#&PPz{XeM>~4wdVJMrk%rO zhqtPAPpi0c$m)9LFv~y3y}t?lT; zb92{>$iMP9;tkk2QsG;((eVK(Tm&nZVnsy6iY!?HA?C7bRb&AV$WtgH7(jsNt0Bg< zSrB-n%;_WncamwU+(w2+c~sa6JV60(Q)(JOA{l_-pPFBPna3Jw|{oAN^BC zT3sCbHyHke`7`!nVxO#mfJ6aEAs?z}ea9sKu%{YJeFxQasH(l}Pl<`dr^$+3U4p&=%R z(QOx`n6QoJ^3i0~Yes~V>XKU7$+16Yxz>2CZ1edn8y=%T&JU>XWK+OnK?<|R|LJ&x zh3?=_*sg^rEH?)g1|Bqm*mcE7A-6l5G1=yhT~sNg&G(D%Hem3ZvdtUa$zh-OG-A6( z(0e_=!g}1PyjHDna8xICn_buZ_pSpEtkZbQEDI^ie-Ldkt35G RAKUmq0W_}wY9l2&?>~4a>E{3d