diff --git a/baystation12.dme b/baystation12.dme
index af6e369282f..68400f47455 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -573,6 +573,7 @@
#include "code\game\objects\items\devices\flashlight.dm"
#include "code\game\objects\items\devices\floor_painter.dm"
#include "code\game\objects\items\devices\lightreplacer.dm"
+#include "code\game\objects\items\devices\magnetic_lock.dm"
#include "code\game\objects\items\devices\megaphone.dm"
#include "code\game\objects\items\devices\modkit.dm"
#include "code\game\objects\items\devices\multitool.dm"
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 524708b64ed..eaa586fdee2 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -30,7 +30,7 @@
var/hasShocked = 0 //Prevents multiple shocks from happening
var/secured_wires = 0
var/datum/wires/airlock/wires = null
-
+ var/obj/item/device/magnetic_lock/bracer = null
var/open_sound_powered = 'sound/machines/airlock.ogg'
var/open_sound_unpowered = 'sound/machines/airlock_creaking.ogg'
@@ -481,7 +481,6 @@ About the new airlock wires panel:
else
return 0
-
/obj/machinery/door/airlock/update_icon()
if(overlays) overlays.Cut()
if(density)
@@ -728,10 +727,10 @@ About the new airlock wires panel:
else if (activate && !src.lights)
lights = 1
usr << "The door bolt lights have been enabled."
-
update_icon()
return 1
+
/obj/machinery/door/airlock/attackby(C as obj, mob/user as mob)
//world << text("airlock attackby src [] obj [] mob []", src, C, user)
if(!istype(usr, /mob/living/silicon))
@@ -740,8 +739,14 @@ About the new airlock wires panel:
return
if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll))
return
-
src.add_fingerprint(user)
+ if (istype(C, /obj/item/device/magnetic_lock))
+ if (bracer)
+ user << "There is already a [bracer] on [src]!"
+ return
+ var/obj/item/device/magnetic_lock/newbracer = C
+ newbracer.attachto(src, user)
+ return
if(!repairing && (istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density))
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0,user))
@@ -792,16 +797,13 @@ About the new airlock wires panel:
da.state = 1
da.created_name = src.name
da.update_state()
-
if(operating == -1 || (stat & BROKEN))
new /obj/item/weapon/circuitboard/broken(src.loc)
operating = 0
else
if (!electronics) create_electronics()
-
electronics.loc = src.loc
electronics = null
-
qdel(src)
return
else if(arePowerSystemsOn())
@@ -819,6 +821,7 @@ About the new airlock wires panel:
user << "The airlock's bolts prevent it from being forced."
else if( !welded && !operating )
if(density)
+
var/obj/item/weapon/material/twohanded/fireaxe/F = C
if(F.wielded)
spawn(0) open(1)
@@ -830,7 +833,6 @@ About the new airlock wires panel:
spawn(0) close(1)
else
user << "You need to be wielding \the [C] to do that."
-
else
..()
return
@@ -852,12 +854,12 @@ About the new airlock wires panel:
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, src)
s.start()
-
update_icon()
return
/obj/machinery/door/airlock/open(var/forced=0)
if(!can_open(forced))
+
return 0
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
@@ -875,7 +877,9 @@ About the new airlock wires panel:
if(!forced)
if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
return 0
-
+ if (bracer)
+ visible_message("[src]'s actuators whirr, but the door does not open.")
+ return 0
if(locked || welded)
return 0
return ..()
@@ -883,12 +887,10 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/can_close(var/forced=0)
if(locked || welded)
return 0
-
if(!forced)
//despite the name, this wire is for general door control.
if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
return 0
-
return ..()
/atom/movable/proc/blocks_airlock()
@@ -941,22 +943,20 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/close(var/forced=0)
if(!can_close(forced))
return 0
-
if(safe)
for(var/turf/turf in locs)
for(var/atom/movable/AM in turf)
if(AM.blocks_airlock())
if(world.time > next_beep_at)
+
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0)
next_beep_at = world.time + SecondsToTicks(10)
close_door_at = world.time + 6
return
-
for(var/turf/turf in locs)
for(var/atom/movable/AM in turf)
if(AM.airlock_crush(DOOR_CRUSH_DAMAGE))
take_damage(DOOR_CRUSH_DAMAGE)
-
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(arePowerSystemsOn())
playsound(src.loc, open_sound_powered, 100, 1)
@@ -972,9 +972,7 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/proc/lock(var/forced=0)
if(locked)
return 0
-
if (operating && !forced) return 0
-
src.locked = 1
for(var/mob/M in range(1,src))
M.show_message("You hear a click from the bottom of the door.", 2)
@@ -984,10 +982,8 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/proc/unlock(var/forced=0)
if(!src.locked)
return
-
if (!forced)
if(operating || !src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) return
-
src.locked = 0
for(var/mob/M in range(1,src))
M.show_message("You hear a click from the bottom of the door.", 2)
@@ -1081,6 +1077,8 @@ About the new airlock wires panel:
update_icon()
/obj/machinery/door/airlock/proc/prison_open()
+ if (bracer)
+ return
src.unlock()
src.open()
src.lock()
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 27ce595a779..edce2e34d11 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -26,7 +26,10 @@
var/timing = 1 // boolean, true/1 timer is on, false/0 means it's not timing
var/picture_state // icon_state of alert picture, if not displaying text/numbers
var/list/obj/machinery/targets = list()
+ var/list/obj/item/clothing/under/color/orange/uniforms = list()
var/timetoset = 0 // Used to set releasetime upon starting the timer
+ var/alerted = 0
+ var/spamcheck = 0
maptext_height = 26
maptext_width = 32
@@ -47,6 +50,10 @@
if(C.id == src.id)
targets += C
+ for(var/obj/item/clothing/under/color/orange/D in world)
+ if(D.id == id)
+ uniforms += D
+
if(targets.len==0)
stat |= BROKEN
update_icon()
@@ -68,6 +75,8 @@
if(timeleft > 1e5)
src.releasetime = 0
+ if(timeleft < 30 && !alerted)
+ alertuniforms()
if(world.timeofday > src.releasetime)
src.timer_end() // open doors, reset timer, clear status screen
@@ -152,6 +161,35 @@
/obj/machinery/door_timer/attack_ai(var/mob/user as mob)
return src.attack_hand(user)
+// Alert the uniform wearers that their timer is about to end up
+/obj/machinery/door_timer/proc/alertuniforms()
+ if(stat & (NOPOWER|BROKEN)) return
+ if(!uniforms.len)
+ alerted = 1
+ return
+
+ for(var/obj/item/clothing/under/color/orange/D in uniforms)
+ if(!ishuman(D.loc)) continue
+ D.loc << "A speaker in the collar of your suit pings:\n\blue Your cell timer will expire in 30 seconds or less. Please go to your cell to speed up processing."
+
+ alerted = 1
+
+ return
+
+// Alert the uniform wearers that they are needed at their cell
+/obj/machinery/door_timer/proc/summonuniforms()
+ if(stat & (NOPOWER|BROKEN)) return
+ if(spamcheck) return
+ if(!uniforms.len) return
+ for(var/obj/item/clothing/under/color/orange/D in uniforms)
+ if(!ishuman(D.loc)) continue
+ D.loc << "A speaker in the collar of your suit pings:\n\red You have been summoned to your cell!"
+
+ spamcheck = 1
+ spawn(30)
+ spamcheck = 0
+
+ return
//Allows humans to use door_timer
//Opens dialog window when someone clicks on door timer
@@ -249,7 +287,8 @@
if(href_list["change"])
src.timer_start()
-
+ if(href_list["summon"])
+ summonuniforms()
src.add_fingerprint(usr)
src.updateUsrDialog()
src.update_icon()
diff --git a/code/game/objects/items/devices/magnetic_lock.dm b/code/game/objects/items/devices/magnetic_lock.dm
new file mode 100644
index 00000000000..9b261401fe7
--- /dev/null
+++ b/code/game/objects/items/devices/magnetic_lock.dm
@@ -0,0 +1,323 @@
+#define STATUS_INACTIVE 0
+#define STATUS_ACTIVE 1
+#define STATUS_BROKEN 2
+
+#define LAYER_ATTACHED 3.2
+#define LAYER_NORMAL 3
+
+/obj/item/device/magnetic_lock
+ name = "magnetic door lock"
+ desc = "A large, ID locked device used for completely locking down airlocks."
+ icon = 'icons/obj/magnetic_locks/centcom.dmi'
+ icon_state = "inactive"
+ w_class = 3
+ req_access = list(103)
+ health = 90
+
+ var/department = "CENTCOM"
+ var/status = 0
+ var/constructionstate = 0
+ var/drainamount = 20
+ var/obj/machinery/door/airlock/target = null
+ var/obj/item/weapon/cell/powercell
+
+/obj/item/device/magnetic_lock/security
+ icon = 'icons/obj/magnetic_locks/security.dmi'
+ department = "Security"
+ req_access = list(1)
+
+/obj/item/device/magnetic_lock/engineering
+ icon = 'icons/obj/magnetic_locks/engineering.dmi'
+ department = "Engineering"
+ req_access = null
+ req_one_access = list(11, 24)
+
+/obj/item/device/magnetic_lock/New()
+ ..()
+
+ powercell = new /obj/item/weapon/cell/high()
+
+/obj/item/device/magnetic_lock/examine()
+ ..()
+
+ if (status == STATUS_BROKEN)
+ usr << "It looks broken!"
+ else
+ if (powercell)
+ var/power = round(powercell.charge / powercell.maxcharge * 100)
+ usr << "\blue The powercell is at [power]% charge."
+ else
+ usr << "\red It has no powercell to power it!"
+
+/obj/item/device/magnetic_lock/attack_hand(var/mob/user)
+ if (status == STATUS_ACTIVE)
+ ui_interact(user)
+ else
+ ..()
+
+/obj/item/device/magnetic_lock/bullet_act(var/obj/item/projectile/Proj)
+ takedamage(Proj.damage)
+ ..()
+
+/obj/item/device/magnetic_lock/attackby(var/obj/item/I, var/mob/user)
+ if (status == STATUS_BROKEN)
+ user << "[src] is broken beyond repair!"
+ return
+
+ if (istype(I, /obj/item/weapon) && user.a_intent == "hurt")
+ if (I.force >= 8)
+ user.visible_message("[user] bashes [src] with [I]!", "You strike [src] with [I], damaging it!")
+ takedamage(I.force)
+ return
+ else
+ user.visible_message("[user] hits [src] with [I] to no visible effect.", "You hit [src] with [I], but it appears to have no effect.")
+ return
+
+ switch (constructionstate)
+ if (0)
+ if (istype(I, /obj/item/weapon/card/emag))
+ var/obj/item/weapon/card/emag/emagcard = I
+ emagcard.uses--
+ visible_message("[src] sparks and falls off the door!", "You emag [src], frying its circuitry[status == STATUS_ACTIVE ? " and making it drop onto the floor" : ""]!")
+
+ setstatus(STATUS_BROKEN)
+ return
+
+ if (status == STATUS_ACTIVE && istype(I, /obj/item/weapon/card/id))
+ if (check_access(I) && !constructionstate)
+ user << "You swipe your [I] through [src], making it drop onto the floor with a thud."
+ setstatus(STATUS_INACTIVE)
+ return
+ else if (constructionstate)
+ user << "You cannot swipe your [I] through [src] with it partially dismantled!"
+ return
+ else
+ user << "A red light flashes on [src] as you swipe your [I] through it."
+ flick("deny",src)
+ return
+
+ if (istype(I, /obj/item/weapon/screwdriver))
+ user << "You unfasten and remove the plastic cover from [src], revealing a thick metal shell."
+ playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ setconstructionstate(1)
+ return
+
+ if (1)
+ if (istype(I, /obj/item/weapon/screwdriver))
+ user << "You put the metal cover of back onto [src], and screw it tight."
+ playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ setconstructionstate(0)
+ return
+ if (istype(I, /obj/item/weapon/cell))
+ user.drop_item()
+ I.loc = src
+ powercell = I
+ return
+ if (istype(I, /obj/item/weapon/crowbar))
+ user << "You remove \the [powercell] from \the [src]."
+ powercell.loc = loc
+ powercell = null
+ return
+ if (istype(I, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = I
+ if (WT.remove_fuel(1, user))
+ user.visible_message("[user] starts welding through the metal shell of [src].", "You start welding through the metal covering of [src]")
+ playsound(loc, 'sound/items/Welder.ogg', 50, 1)
+ if (do_after(user, 25))
+ user << "You are able to weld through the metal shell of [src]."
+ setconstructionstate(2)
+ return
+ if (2)
+ if (istype(I, /obj/item/weapon/crowbar))
+ user << "You pry off the metal covering from [src]."
+ playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
+ setconstructionstate(3)
+ return
+ if (3)
+ if (istype(I, /obj/item/weapon/wirecutters))
+ user << "You cut the wires connecting the [src]'s magnets to their powersupply, [target ? "making the device fall off [target] and rendering it unusable." : "rendering the device unusable."]"
+ playsound(loc, 'sound/items/Wirecutter.ogg', 50, 1)
+ setconstructionstate(4)
+ return
+
+/obj/item/device/magnetic_lock/process()
+ if (powercell && powercell.charge > drainamount)
+ powercell.charge -= drainamount
+ else
+ if (powercell)
+ powercell.charge = 0
+ visible_message("[src] beeps loudly and falls off \the [target]; its powercell having run out of power.")
+ setstatus(STATUS_INACTIVE)
+
+/obj/item/device/magnetic_lock/proc/attachto(var/obj/machinery/door/airlock/newtarget, var/mob/user as mob)
+ if (status == STATUS_BROKEN)
+ user << "[src] is damaged beyond repair! It cannot be used!"
+ return
+
+ if (!newtarget.density || newtarget.operating)
+ user << "[newtarget] must be closed before you can attach [src] to it!"
+ return
+
+ if (newtarget.p_open)
+ user << "You must close [newtarget]'s maintenance panel before attaching [src] to it!"
+ return
+
+ user.visible_message("[user] starts mounting [src] onto [newtarget].", "You begin mounting [src] onto [newtarget].")
+ if (do_after(user, 35, 1))
+ if (status == STATUS_BROKEN)
+ user << "[src] is damaged beyond repair! It cannot be used!"
+ return
+
+ if (!newtarget.density)
+ user << "[newtarget] must be closed before you can attach [src] to it!"
+ return
+
+ if (newtarget.p_open)
+ user << "You must close [newtarget]'s maintenance panel before attaching [src] to it!"
+ return
+
+ user.visible_message("[user] attached [src] onto [newtarget] and flicks it on. The magnetic lock now seals [newtarget].", "You attached [src] onto [newtarget] and switched on the magnetic lock.")
+ user.drop_item()
+
+ setstatus(STATUS_ACTIVE, newtarget)
+ return
+
+/obj/item/device/magnetic_lock/proc/setstatus(var/newstatus, var/obj/machinery/door/airlock/newtarget as obj)
+ switch (newstatus)
+ if (STATUS_INACTIVE)
+ if (status != STATUS_ACTIVE)
+ return
+ if (!target)
+ return
+
+ detach()
+ icon_state = "inactive"
+ status = newstatus
+
+ if (STATUS_ACTIVE)
+ if (status != STATUS_INACTIVE)
+ return
+ if (!newtarget)
+ return
+
+ attach(newtarget)
+ icon_state = "active"
+ status = newstatus
+
+ if (STATUS_BROKEN)
+ spark()
+
+ if (target)
+ var/playflick = 1
+ if (constructionstate)
+ playflick = 0
+
+ detach(playflick)
+
+ icon_state = "broken"
+ status = newstatus
+
+/obj/item/device/magnetic_lock/proc/setconstructionstate(var/newstate)
+ constructionstate = newstate
+ if (newstate == 0)
+ if (status == STATUS_ACTIVE)
+ icon_state = "active"
+ else
+ icon_state = "inactive"
+ else if (newstate == 2)
+ flick("deconstruct_2_anim", src)
+ else if (newstate == 4)
+ setstatus(STATUS_BROKEN)
+ else
+ icon_state = "deconstruct_[constructionstate]"
+
+/obj/item/device/magnetic_lock/proc/detach(var/playflick = 1)
+ if (target)
+
+ if (playflick)
+ spawn(-15) flick("release", src)
+
+ adjustsprite(null)
+ layer = LAYER_NORMAL
+
+ target.bracer = null
+
+ processing_objects.Remove(src)
+
+ anchored = 0
+
+/obj/item/device/magnetic_lock/proc/attach(var/obj/machinery/door/airlock/newtarget as obj)
+ adjustsprite(newtarget)
+ layer = LAYER_ATTACHED
+ flick("deploy", src)
+
+ newtarget.bracer = src
+ target = newtarget
+
+ processing_objects.Add(src)
+
+ anchored = 1
+
+/obj/item/device/magnetic_lock/proc/adjustsprite(var/obj/target as obj)
+ if (target)
+ switch (get_dir(src, target))
+ if (NORTH)
+ pixel_x = 0
+ pixel_y = 32
+ if (NORTHEAST)
+ pixel_x = 32
+ pixel_y = 32
+ if (EAST)
+ pixel_x = 32
+ pixel_y = 0
+ if (SOUTHEAST)
+ pixel_x = 32
+ pixel_y = -32
+ if (SOUTH)
+ pixel_x = 0
+ pixel_y = -32
+ if (SOUTHWEST)
+ pixel_x = -32
+ pixel_y = -32
+ if (WEST)
+ pixel_x = -32
+ pixel_y = 0
+ if (NORTHWEST)
+ pixel_x = -32
+ pixel_y = 32
+ else
+ pixel_x = 0
+ pixel_y = 0
+
+/obj/item/device/magnetic_lock/proc/takedamage(var/damage)
+ health -= damage
+
+ if (damage >= 40 && prob(50))
+ health = 0
+
+ if (health <= 0)
+ visible_message("[src] sparks[target ? " and falls off of \the [target]!" : "!"] It is now completely unusable!")
+ setstatus(STATUS_BROKEN)
+ return
+
+ if (prob(50))
+ spark()
+
+/obj/item/device/magnetic_lock/proc/spark()
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+
+ if (target)
+ s.set_up(5, 1, target)
+ else
+ s.set_up(5, 1, src)
+
+ s.start()
+ spawn(5)
+ del(s)
+
+#undef STATUS_INACTIVE
+#undef STATUS_ACTIVE
+#undef STATUS_BROKEN
+
+#undef LAYER_ATTACHED
+#undef LAYER_NORMAL
diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm
index 725f51b1ef4..acc7f557087 100644
--- a/code/modules/clothing/under/color.dm
+++ b/code/modules/clothing/under/color.dm
@@ -37,6 +37,23 @@
worn_state = "orange"
has_sensor = 2
sensor_mode = 3
+ var/id
+
+/obj/item/clothing/under/color/orange/cell1
+ desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position.\nThis one has \"Cell 1\" marked on it."
+ id = "Cell 1"
+
+/obj/item/clothing/under/color/orange/cell2
+ desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position.\nThis one has \"Cell 2\" marked on it."
+ id = "Cell 2"
+
+/obj/item/clothing/under/color/orange/cell3
+ desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position.\nThis one has \"Cell 3\" marked on it."
+ id = "Cell 3"
+
+/obj/item/clothing/under/color/orange/cell4
+ desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position.\nThis one has \"Cell 4\" marked on it."
+ id = "Cell 4"
/obj/item/clothing/under/color/pink
name = "pink jumpsuit"
diff --git a/icons/obj/magnetic_locks/centcom.dmi b/icons/obj/magnetic_locks/centcom.dmi
new file mode 100644
index 00000000000..97a977941ac
Binary files /dev/null and b/icons/obj/magnetic_locks/centcom.dmi differ
diff --git a/icons/obj/magnetic_locks/engineering.dmi b/icons/obj/magnetic_locks/engineering.dmi
new file mode 100644
index 00000000000..49323f4a377
Binary files /dev/null and b/icons/obj/magnetic_locks/engineering.dmi differ
diff --git a/icons/obj/magnetic_locks/security.dmi b/icons/obj/magnetic_locks/security.dmi
new file mode 100644
index 00000000000..00a820fbb87
Binary files /dev/null and b/icons/obj/magnetic_locks/security.dmi differ