diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm
index 02f36f5ea40..28907e9e05e 100644
--- a/_maps/map_files/Delta/delta.dmm
+++ b/_maps/map_files/Delta/delta.dmm
@@ -47144,7 +47144,7 @@
pixel_y = 3
},
/obj/item/circuitboard/powermonitor,
-/obj/item/circuitboard/stationalert_all{
+/obj/item/circuitboard/stationalert{
pixel_x = 3;
pixel_y = -3
},
diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
index bcbbe8b2351..4152ad16382 100644
--- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
@@ -31415,7 +31415,7 @@
pixel_x = -2;
pixel_y = 2
},
-/obj/item/circuitboard/stationalert_all{
+/obj/item/circuitboard/stationalert{
pixel_x = 1;
pixel_y = -1
},
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm
new file mode 100644
index 00000000000..af01a5bc274
--- /dev/null
+++ b/_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm
@@ -0,0 +1,614 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/template_noop,
+/area/template_noop)
+"b" = (
+/obj/structure/disposalpipe/segment,
+/turf/space,
+/area/space)
+"c" = (
+/obj/structure/lattice,
+/obj/structure/disposalpipe/broken{
+ dir = 1
+ },
+/turf/space,
+/area/space)
+"d" = (
+/obj/structure/lattice,
+/turf/space,
+/area/template_noop)
+"e" = (
+/obj/structure/grille/broken,
+/turf/space,
+/area/space)
+"g" = (
+/obj/structure/disposalpipe/broken{
+ dir = 4
+ },
+/turf/space,
+/area/space)
+"h" = (
+/obj/structure/disposalpipe/broken{
+ dir = 8
+ },
+/obj/item/stack/cable_coil/cut,
+/turf/space,
+/area/space)
+"l" = (
+/obj/item/stack/cable_coil/cut,
+/turf/space,
+/area/space)
+"m" = (
+/obj/effect/spawner/random_spawners/grille_often,
+/turf/space,
+/area/space)
+"n" = (
+/obj/effect/spawner/random_spawners/grille_maybe,
+/turf/space,
+/area/space)
+"o" = (
+/obj/random/tool,
+/turf/space,
+/area/space)
+"p" = (
+/obj/structure/girder/displaced,
+/turf/space,
+/area/space)
+"r" = (
+/turf/space,
+/area/template_noop)
+"t" = (
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8"
+ },
+/turf/space,
+/area/space)
+"u" = (
+/obj/structure/door_assembly/door_assembly_sec,
+/obj/item/airlock_electronics,
+/turf/space,
+/area/space)
+"v" = (
+/obj/structure/lattice,
+/turf/space,
+/area/space)
+"x" = (
+/obj/structure/disposalpipe/junction,
+/turf/space,
+/area/space)
+"y" = (
+/turf/simulated/floor/plating/burnt,
+/area/space)
+"z" = (
+/obj/item/stack/sheet/metal,
+/turf/simulated/floor/plating,
+/area/space)
+"B" = (
+/obj/structure/lattice,
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/space)
+"C" = (
+/obj/structure/disposalpipe/broken{
+ dir = 1
+ },
+/turf/space,
+/area/space)
+"D" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ pixel_y = 0;
+ tag = ""
+ },
+/turf/simulated/floor/plating/burnt,
+/area/space)
+"E" = (
+/obj/item/stack/rods,
+/turf/space,
+/area/space)
+"F" = (
+/turf/simulated/floor/plating,
+/area/space)
+"G" = (
+/obj/random/toolbox,
+/turf/simulated/floor/plating/burnt,
+/area/space)
+"I" = (
+/obj/structure/grille/broken,
+/obj/structure/lattice,
+/turf/space,
+/area/space)
+"J" = (
+/obj/structure/disposalpipe/broken,
+/turf/space,
+/area/space)
+"K" = (
+/obj/item/shard,
+/turf/space,
+/area/space)
+"N" = (
+/obj/structure/lattice,
+/obj/structure/lattice,
+/turf/space,
+/area/space)
+"O" = (
+/obj/machinery/power/apc/worn_out{
+ pixel_y = -24
+ },
+/obj/structure/cable,
+/turf/simulated/floor/plating,
+/area/space)
+"Q" = (
+/obj/effect/decal/cleanable/shreds{
+ pixel_x = 4;
+ pixel_y = 3
+ },
+/turf/simulated/floor/plating,
+/area/space)
+"T" = (
+/obj/structure/girder,
+/turf/simulated/floor/plating/burnt,
+/area/space)
+"U" = (
+/obj/effect/spawner/random_barrier/wall_probably,
+/turf/space,
+/area/space)
+"V" = (
+/turf/simulated/wall,
+/area/space)
+"X" = (
+/turf/space,
+/area/space)
+"Y" = (
+/obj/item/shard,
+/turf/simulated/floor/plating,
+/area/space)
+"Z" = (
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/space)
+
+(1,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
+(2,1,1) = {"
+a
+a
+r
+r
+r
+r
+r
+r
+r
+r
+r
+d
+d
+d
+r
+r
+d
+r
+a
+a
+"}
+(3,1,1) = {"
+a
+d
+v
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+r
+a
+"}
+(4,1,1) = {"
+a
+d
+X
+m
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+v
+Z
+X
+X
+d
+a
+"}
+(5,1,1) = {"
+a
+r
+X
+g
+F
+X
+E
+I
+X
+X
+v
+X
+F
+v
+v
+X
+X
+X
+r
+a
+"}
+(6,1,1) = {"
+a
+r
+J
+x
+C
+E
+v
+F
+X
+E
+X
+v
+g
+Z
+X
+X
+F
+X
+r
+a
+"}
+(7,1,1) = {"
+a
+r
+X
+X
+y
+E
+X
+E
+Y
+X
+n
+X
+h
+E
+F
+X
+v
+X
+r
+a
+"}
+(8,1,1) = {"
+a
+r
+X
+X
+X
+B
+l
+p
+X
+X
+y
+p
+X
+X
+X
+E
+X
+X
+r
+a
+"}
+(9,1,1) = {"
+a
+r
+X
+v
+X
+o
+X
+X
+Z
+l
+X
+E
+a
+X
+X
+V
+X
+X
+r
+a
+"}
+(10,1,1) = {"
+a
+r
+X
+Z
+Q
+v
+X
+y
+v
+D
+y
+U
+X
+X
+E
+X
+X
+n
+r
+a
+"}
+(11,1,1) = {"
+a
+r
+X
+X
+I
+v
+v
+X
+v
+t
+O
+V
+X
+X
+F
+E
+Z
+X
+d
+a
+"}
+(12,1,1) = {"
+a
+r
+X
+X
+X
+X
+J
+b
+c
+v
+G
+V
+X
+E
+e
+X
+X
+X
+r
+a
+"}
+(13,1,1) = {"
+a
+r
+X
+X
+X
+V
+X
+Z
+X
+X
+X
+u
+X
+X
+X
+X
+e
+Z
+r
+a
+"}
+(14,1,1) = {"
+a
+r
+X
+X
+X
+X
+v
+X
+v
+N
+E
+E
+K
+X
+v
+o
+X
+X
+r
+a
+"}
+(15,1,1) = {"
+a
+r
+X
+E
+X
+X
+X
+X
+E
+J
+C
+X
+X
+X
+X
+X
+X
+X
+d
+a
+"}
+(16,1,1) = {"
+a
+r
+X
+X
+E
+X
+T
+X
+Z
+X
+n
+v
+X
+z
+n
+X
+Z
+X
+d
+a
+"}
+(17,1,1) = {"
+a
+r
+X
+X
+X
+X
+X
+X
+X
+X
+F
+X
+X
+X
+p
+X
+X
+X
+r
+a
+"}
+(18,1,1) = {"
+a
+r
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+X
+v
+X
+X
+X
+X
+r
+a
+"}
+(19,1,1) = {"
+a
+a
+r
+r
+r
+r
+d
+r
+d
+r
+r
+r
+r
+r
+r
+r
+r
+r
+a
+a
+"}
+(20,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm
new file mode 100644
index 00000000000..6a6cd81f32a
--- /dev/null
+++ b/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm
@@ -0,0 +1,631 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/template_noop,
+/area/template_noop)
+"b" = (
+/turf/simulated/floor/plating/airless,
+/area/space)
+"c" = (
+/turf/simulated/floor/plasteel/airless,
+/area/space)
+"d" = (
+/turf/simulated/wall/r_wall,
+/area/space)
+"e" = (
+/obj/item/stack/cable_coil/cut,
+/turf/space,
+/area/template_noop)
+"h" = (
+/obj/structure/girder,
+/turf/space,
+/area/template_noop)
+"j" = (
+/obj/structure/grille/broken,
+/turf/space,
+/area/space)
+"l" = (
+/obj/structure/girder,
+/turf/simulated/floor/plating,
+/area/space)
+"m" = (
+/obj/structure/grille/broken,
+/turf/template_noop,
+/area/space)
+"n" = (
+/obj/item/stack/cable_coil/cut,
+/turf/template_noop,
+/area/space)
+"o" = (
+/obj/machinery/door/airlock/command{
+ max_integrity = 40
+ },
+/turf/simulated/floor/plating/burnt,
+/area/space)
+"p" = (
+/mob/living/simple_animal/hostile/carp,
+/turf/space,
+/area/template_noop)
+"q" = (
+/obj/structure/lattice,
+/turf/space,
+/area/space)
+"s" = (
+/obj/item/stack/rods,
+/turf/space,
+/area/template_noop)
+"t" = (
+/obj/structure/door_assembly/door_assembly_com,
+/turf/space,
+/area/template_noop)
+"u" = (
+/obj/item/clothing/head/bio_hood/virology,
+/turf/space,
+/area/space)
+"w" = (
+/obj/structure/lattice,
+/turf/template_noop,
+/area/space)
+"x" = (
+/turf/template_noop,
+/area/space)
+"y" = (
+/obj/structure/lattice,
+/obj/structure/door_assembly/door_assembly_vault{
+ anchored = 1
+ },
+/turf/simulated/floor/plating,
+/area/space)
+"z" = (
+/obj/item/stack/rods,
+/turf/template_noop,
+/area/space)
+"A" = (
+/obj/structure/closet/crate,
+/obj/item/reagent_containers/glass/beaker/bluespace,
+/turf/simulated/floor/plasteel/airless,
+/area/space)
+"B" = (
+/obj/item/shard{
+ icon_state = "small"
+ },
+/turf/space,
+/area/space)
+"C" = (
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/template_noop)
+"D" = (
+/obj/effect/landmark/burnturf,
+/turf/simulated/floor/plating/airless,
+/area/space)
+"E" = (
+/obj/item/reagent_containers/glass/beaker/large,
+/turf/space,
+/area/template_noop)
+"F" = (
+/obj/effect/landmark/burnturf,
+/turf/simulated/floor,
+/area/space)
+"G" = (
+/obj/structure/safe,
+/obj/item/stack/sheet/mineral/bananium{
+ amount = 5
+ },
+/obj/item/stack/ore/bluespace_crystal/refined{
+ amount = 3
+ },
+/obj/item/dnainjector/comic,
+/turf/simulated/floor/plasteel/airless,
+/area/space)
+"H" = (
+/obj/structure/table,
+/obj/item/shard{
+ icon_state = "medium"
+ },
+/turf/simulated/floor/plasteel/airless,
+/area/space)
+"I" = (
+/turf/space,
+/area/template_noop)
+"J" = (
+/mob/living/simple_animal/hostile/carp,
+/turf/space,
+/area/space)
+"L" = (
+/obj/item/shard,
+/turf/space,
+/area/space)
+"M" = (
+/turf/simulated/floor,
+/area/space)
+"N" = (
+/obj/item/clothing/suit/bio_suit/virology,
+/turf/space,
+/area/space)
+"O" = (
+/obj/item/shard{
+ icon_state = "small"
+ },
+/turf/space,
+/area/template_noop)
+"P" = (
+/obj/item/reagent_containers/spray/cleaner,
+/turf/space,
+/area/space)
+"R" = (
+/obj/item/stack/sheet/metal,
+/turf/template_noop,
+/area/space)
+"S" = (
+/obj/item/reagent_containers/spray/cleaner,
+/turf/space,
+/area/template_noop)
+"T" = (
+/turf/simulated/wall,
+/area/space)
+"U" = (
+/obj/effect/landmark/burnturf,
+/turf/simulated/floor/plasteel/airless,
+/area/space)
+"V" = (
+/obj/structure/lattice,
+/turf/space,
+/area/template_noop)
+"W" = (
+/obj/effect/landmark/burnturf,
+/turf/simulated/floor/plating/burnt,
+/area/space)
+"X" = (
+/turf/space,
+/area/space)
+"Y" = (
+/obj/structure/girder/reinforced,
+/turf/simulated/floor/plating/damaged,
+/area/space)
+"Z" = (
+/obj/machinery/computer/pandemic{
+ circuit = /obj/effect/decal/cleanable/shreds
+ },
+/turf/simulated/floor/plasteel/airless,
+/area/space)
+
+(1,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
+(2,1,1) = {"
+a
+d
+d
+d
+d
+d
+Y
+x
+S
+I
+I
+I
+I
+I
+I
+C
+I
+I
+I
+a
+"}
+(3,1,1) = {"
+a
+d
+G
+A
+U
+F
+d
+z
+T
+T
+T
+T
+o
+T
+I
+p
+O
+I
+I
+a
+"}
+(4,1,1) = {"
+a
+Y
+c
+U
+c
+U
+d
+X
+w
+M
+c
+U
+M
+T
+V
+I
+I
+I
+I
+a
+"}
+(5,1,1) = {"
+a
+Y
+M
+F
+F
+q
+d
+z
+m
+F
+M
+F
+M
+T
+I
+I
+I
+I
+I
+a
+"}
+(6,1,1) = {"
+a
+d
+d
+y
+d
+d
+Y
+X
+w
+X
+n
+X
+w
+T
+I
+I
+I
+C
+I
+a
+"}
+(7,1,1) = {"
+a
+X
+X
+X
+X
+X
+P
+J
+X
+T
+T
+T
+x
+T
+I
+I
+V
+I
+I
+a
+"}
+(8,1,1) = {"
+a
+X
+X
+X
+X
+X
+X
+X
+R
+X
+w
+X
+b
+x
+x
+I
+I
+I
+I
+a
+"}
+(9,1,1) = {"
+a
+p
+s
+T
+m
+w
+T
+R
+X
+B
+X
+w
+X
+x
+z
+I
+s
+I
+I
+a
+"}
+(10,1,1) = {"
+a
+s
+I
+T
+M
+F
+T
+X
+X
+X
+X
+X
+X
+x
+x
+I
+I
+I
+I
+a
+"}
+(11,1,1) = {"
+a
+I
+I
+T
+c
+F
+T
+X
+X
+c
+F
+b
+j
+D
+T
+I
+I
+I
+t
+a
+"}
+(12,1,1) = {"
+a
+C
+I
+T
+X
+M
+N
+s
+I
+I
+L
+q
+q
+b
+T
+I
+s
+I
+I
+a
+"}
+(13,1,1) = {"
+a
+I
+I
+l
+T
+T
+T
+I
+I
+u
+j
+W
+c
+U
+T
+I
+I
+I
+I
+a
+"}
+(14,1,1) = {"
+a
+I
+s
+p
+I
+I
+I
+I
+p
+I
+I
+c
+H
+Z
+T
+I
+I
+I
+I
+a
+"}
+(15,1,1) = {"
+a
+V
+I
+I
+s
+V
+C
+I
+I
+s
+h
+T
+T
+T
+T
+I
+I
+I
+I
+a
+"}
+(16,1,1) = {"
+a
+I
+I
+E
+I
+I
+V
+I
+O
+I
+V
+I
+I
+I
+I
+I
+C
+I
+I
+a
+"}
+(17,1,1) = {"
+a
+I
+I
+C
+I
+e
+I
+I
+I
+I
+I
+e
+I
+V
+I
+I
+I
+I
+I
+a
+"}
+(18,1,1) = {"
+a
+I
+I
+I
+I
+I
+I
+h
+I
+I
+C
+I
+I
+I
+I
+I
+I
+I
+I
+a
+"}
+(19,1,1) = {"
+a
+I
+I
+I
+I
+I
+I
+I
+I
+I
+I
+I
+I
+I
+C
+I
+I
+I
+I
+a
+"}
+(20,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm
new file mode 100644
index 00000000000..54d8725b3c4
--- /dev/null
+++ b/_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm
@@ -0,0 +1,824 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/template_noop,
+/area/template_noop)
+"b" = (
+/obj/item/stack/rods,
+/turf/simulated/floor/plating/damaged,
+/area/template_noop)
+"c" = (
+/obj/item/trash/popcorn,
+/turf/space,
+/area/template_noop)
+"d" = (
+/obj/effect/spawner/random_spawners/wall_rusted_maybe,
+/turf/space,
+/area/template_noop)
+"e" = (
+/obj/item/poster/random_contraband,
+/turf/space,
+/area/template_noop)
+"f" = (
+/obj/structure/lattice,
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/template_noop)
+"g" = (
+/obj/item/paperplane,
+/turf/space,
+/area/template_noop)
+"h" = (
+/obj/item/trash/can,
+/turf/space,
+/area/template_noop)
+"i" = (
+/obj/item/stack/rods,
+/turf/template_noop,
+/area/template_noop)
+"k" = (
+/obj/item/shard,
+/turf/space,
+/area/template_noop)
+"l" = (
+/obj/item/stack/cable_coil{
+ amount = 10
+ },
+/turf/space,
+/area/template_noop)
+"m" = (
+/turf/simulated/wall,
+/area/template_noop)
+"n" = (
+/obj/item/stack/sheet/glass,
+/turf/space,
+/area/template_noop)
+"p" = (
+/obj/random/plushie,
+/turf/space,
+/area/template_noop)
+"q" = (
+/obj/item/stack/rods,
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/template_noop)
+"t" = (
+/obj/machinery/computer/arcade/battle,
+/turf/simulated/floor/plasteel/airless,
+/area/template_noop)
+"u" = (
+/obj/random/tool,
+/turf/space,
+/area/template_noop)
+"v" = (
+/obj/item/trash/syndi_cakes,
+/turf/space,
+/area/template_noop)
+"w" = (
+/obj/item/stack/tickets,
+/turf/space,
+/area/template_noop)
+"x" = (
+/obj/structure/bookcase/random/fiction,
+/turf/simulated/floor/plating/damaged,
+/area/template_noop)
+"y" = (
+/obj/item/storage/pill_bottle/random_meds/labelled,
+/turf/space,
+/area/template_noop)
+"z" = (
+/turf/simulated/floor/plating/damaged,
+/area/template_noop)
+"A" = (
+/turf/space,
+/area/template_noop)
+"B" = (
+/obj/item/stack/rods,
+/turf/space,
+/area/template_noop)
+"D" = (
+/obj/item/paper,
+/obj/item/paper,
+/turf/space,
+/area/template_noop)
+"F" = (
+/obj/random/tool,
+/turf/simulated/floor/plating/damaged,
+/area/template_noop)
+"G" = (
+/obj/item/poster/random_official,
+/turf/space,
+/area/template_noop)
+"H" = (
+/obj/item/stack/sheet/wood,
+/turf/space,
+/area/template_noop)
+"I" = (
+/obj/item/stack/rods,
+/obj/item/stack/rods,
+/turf/space,
+/area/template_noop)
+"J" = (
+/obj/item/trash/pistachios,
+/turf/space,
+/area/template_noop)
+"K" = (
+/obj/structure/lattice,
+/obj/structure/girder,
+/turf/space,
+/area/template_noop)
+"L" = (
+/obj/structure/girder,
+/turf/space,
+/area/template_noop)
+"M" = (
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/template_noop)
+"O" = (
+/obj/structure/girder/displaced,
+/turf/space,
+/area/template_noop)
+"S" = (
+/obj/structure/lattice,
+/turf/space,
+/area/template_noop)
+"U" = (
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/turf/space,
+/area/template_noop)
+"V" = (
+/obj/item/circuitboard/arcade,
+/turf/space,
+/area/template_noop)
+"W" = (
+/obj/item/stack/tickets,
+/obj/item/stack/tickets,
+/obj/item/stack/tickets,
+/turf/space,
+/area/template_noop)
+"Z" = (
+/obj/item/paper,
+/turf/space,
+/area/template_noop)
+
+(1,1,1) = {"
+a
+a
+a
+i
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
+(2,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+i
+a
+a
+a
+a
+a
+i
+a
+a
+a
+"}
+(3,1,1) = {"
+a
+a
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+A
+a
+a
+"}
+(4,1,1) = {"
+a
+a
+A
+B
+A
+B
+A
+A
+A
+A
+A
+A
+A
+A
+B
+B
+A
+A
+a
+a
+"}
+(5,1,1) = {"
+i
+a
+A
+A
+A
+A
+A
+w
+A
+M
+A
+A
+A
+A
+A
+A
+A
+A
+a
+i
+"}
+(6,1,1) = {"
+a
+a
+A
+A
+I
+A
+B
+A
+A
+B
+B
+M
+A
+A
+M
+A
+A
+A
+a
+a
+"}
+(7,1,1) = {"
+a
+a
+A
+A
+k
+A
+A
+O
+A
+A
+A
+A
+m
+A
+w
+A
+M
+A
+a
+a
+"}
+(8,1,1) = {"
+a
+a
+A
+B
+A
+M
+A
+A
+H
+A
+G
+n
+m
+A
+A
+B
+A
+A
+a
+a
+"}
+(9,1,1) = {"
+a
+a
+A
+A
+m
+A
+w
+A
+M
+S
+A
+A
+d
+B
+M
+A
+O
+A
+a
+a
+"}
+(10,1,1) = {"
+a
+a
+A
+w
+A
+A
+Z
+A
+W
+B
+S
+h
+M
+L
+A
+c
+A
+A
+a
+a
+"}
+(11,1,1) = {"
+a
+i
+B
+w
+A
+S
+A
+F
+A
+A
+S
+A
+Z
+A
+A
+A
+M
+A
+a
+a
+"}
+(12,1,1) = {"
+a
+a
+A
+A
+A
+A
+H
+L
+S
+m
+m
+b
+A
+A
+Z
+w
+B
+A
+i
+a
+"}
+(13,1,1) = {"
+a
+a
+A
+A
+B
+O
+A
+B
+A
+M
+A
+D
+K
+z
+S
+H
+B
+A
+a
+a
+"}
+(14,1,1) = {"
+a
+a
+A
+n
+A
+y
+f
+B
+A
+z
+B
+A
+m
+t
+A
+A
+A
+A
+a
+a
+"}
+(15,1,1) = {"
+a
+a
+A
+A
+z
+H
+A
+A
+A
+S
+A
+q
+z
+z
+A
+A
+O
+A
+a
+a
+"}
+(16,1,1) = {"
+a
+a
+A
+A
+S
+A
+S
+U
+B
+B
+A
+A
+S
+A
+M
+B
+u
+A
+a
+i
+"}
+(17,1,1) = {"
+a
+a
+A
+M
+H
+w
+Z
+B
+x
+S
+B
+S
+A
+k
+J
+A
+A
+A
+a
+a
+"}
+(18,1,1) = {"
+a
+a
+B
+A
+A
+A
+A
+A
+A
+w
+B
+z
+A
+O
+A
+M
+A
+A
+a
+a
+"}
+(19,1,1) = {"
+i
+a
+A
+A
+M
+z
+S
+w
+S
+M
+A
+m
+p
+A
+A
+B
+A
+A
+a
+a
+"}
+(20,1,1) = {"
+a
+a
+A
+B
+A
+O
+H
+v
+A
+V
+H
+A
+B
+L
+A
+M
+A
+w
+a
+a
+"}
+(21,1,1) = {"
+a
+a
+A
+A
+A
+A
+e
+M
+A
+w
+Z
+w
+B
+A
+w
+A
+A
+A
+a
+a
+"}
+(22,1,1) = {"
+a
+a
+A
+A
+M
+O
+m
+m
+L
+A
+B
+A
+A
+q
+A
+A
+A
+A
+a
+a
+"}
+(23,1,1) = {"
+a
+a
+A
+B
+k
+A
+B
+A
+B
+g
+n
+A
+O
+A
+A
+B
+B
+A
+a
+a
+"}
+(24,1,1) = {"
+a
+a
+A
+A
+A
+A
+A
+M
+A
+A
+l
+A
+w
+A
+A
+A
+A
+A
+a
+a
+"}
+(25,1,1) = {"
+a
+a
+A
+w
+B
+A
+A
+A
+A
+A
+A
+B
+A
+A
+A
+w
+A
+A
+a
+a
+"}
+(26,1,1) = {"
+a
+a
+A
+A
+A
+A
+A
+A
+B
+A
+B
+A
+A
+B
+A
+B
+A
+A
+a
+a
+"}
+(27,1,1) = {"
+a
+a
+B
+A
+w
+A
+B
+A
+w
+A
+B
+A
+A
+A
+A
+A
+A
+w
+a
+a
+"}
+(28,1,1) = {"
+a
+a
+A
+A
+A
+A
+A
+A
+A
+w
+A
+A
+A
+A
+A
+A
+A
+A
+a
+i
+"}
+(29,1,1) = {"
+i
+a
+a
+a
+a
+a
+a
+i
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
+(30,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+i
+a
+a
+"}
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm
new file mode 100644
index 00000000000..ec11a787510
--- /dev/null
+++ b/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm
@@ -0,0 +1,2284 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"aM" = (
+/obj/item/stack/rods,
+/obj/item/stack/sheet/metal,
+/turf/template_noop,
+/area/space)
+"bb" = (
+/obj/structure/table/reinforced,
+/obj/item/stack/sheet/mineral/plasma,
+/obj/item/stack/sheet/mineral/plasma,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"bk" = (
+/obj/structure/grille/broken,
+/turf/template_noop,
+/area/space)
+"cc" = (
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/turf/simulated/floor/plating,
+/area/space)
+"dy" = (
+/obj/structure/lattice,
+/turf/unsimulated,
+/area/template_noop)
+"es" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"eY" = (
+/turf/simulated/floor/plating,
+/area/ruin)
+"fq" = (
+/obj/machinery/autolathe,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"fU" = (
+/obj/machinery/door/airlock/mining/glass{
+ locked = 1;
+ name = "Egg processing"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"fY" = (
+/obj/item/stack/sheet/metal,
+/turf/template_noop,
+/area/template_noop)
+"hm" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"ho" = (
+/turf/space,
+/area/ruin/space)
+"hs" = (
+/turf/unsimulated/wall/fakeglass{
+ dir = 4
+ },
+/area/ruin/unpowered)
+"iK" = (
+/obj/item/stack/sheet/metal,
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/ruin/space)
+"jf" = (
+/obj/item/trash/syndi_cakes,
+/turf/template_noop,
+/area/template_noop)
+"jv" = (
+/obj/item/stack/cable_coil/cut,
+/turf/template_noop,
+/area/space)
+"jR" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/fancy/cigarettes/cigpack_random,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"ke" = (
+/obj/structure/lattice,
+/turf/space,
+/area/ruin)
+"kh" = (
+/obj/structure/lattice,
+/obj/item/stack/rods,
+/turf/template_noop,
+/area/space)
+"ky" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/turf/simulated/wall,
+/area/ruin/unpowered)
+"ls" = (
+/obj/structure/lattice,
+/turf/template_noop,
+/area/space)
+"lK" = (
+/obj/machinery/power/apc/noalarm{
+ area = null;
+ dir = 8;
+ keep_preset_name = 1;
+ light_power = 0;
+ locked = 0;
+ max_integrity = 0;
+ name = "Manufacturing control room APC";
+ pixel_y = -24;
+ shorted = 1;
+ start_charge = -1
+ },
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"mc" = (
+/obj/structure/grille/broken,
+/turf/space,
+/area/ruin/space)
+"mx" = (
+/obj/item/trash/syndi_cakes,
+/turf/template_noop,
+/area/space)
+"nV" = (
+/obj/machinery/conveyor/east{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/space)
+"ob" = (
+/obj/structure/lattice,
+/turf/template_noop,
+/area/ruin/unpowered)
+"om" = (
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/turf/simulated/floor/plasteel,
+/area/space)
+"oJ" = (
+/obj/item/stack/rods,
+/turf/template_noop,
+/area/space)
+"oO" = (
+/obj/machinery/door/airlock/mining/glass{
+ name = "Nexus"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"pb" = (
+/obj/structure/lattice/catwalk,
+/turf/template_noop,
+/area/space)
+"pd" = (
+/turf/template_noop,
+/area/space)
+"pp" = (
+/turf/unsimulated,
+/area/space)
+"pR" = (
+/obj/machinery/conveyor/northeast{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"qS" = (
+/obj/machinery/door/airlock/mining/glass{
+ name = "EVA Storage"
+ },
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ pixel_y = 0;
+ tag = ""
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"rJ" = (
+/obj/structure/lattice,
+/turf/space,
+/area/ruin/space)
+"rL" = (
+/obj/item/reagent_containers/food/snacks/syndicake,
+/turf/simulated/floor/plasteel/airless,
+/area/ruin)
+"rU" = (
+/obj/machinery/space_heater,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"sd" = (
+/turf/simulated/floor/plating,
+/area/space)
+"sN" = (
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"ub" = (
+/obj/item/stack/rods,
+/turf/space,
+/area/space)
+"uA" = (
+/obj/structure/table/reinforced,
+/obj/item/reagent_containers/food/snacks/syndicake,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"uF" = (
+/obj/machinery/conveyor/east{
+ id = null
+ },
+/obj/machinery/gibber/autogibber,
+/obj/structure/plasticflaps{
+ canmove = 0
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"wB" = (
+/obj/item/trash/syndi_cakes,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"xT" = (
+/turf/unsimulated/floor/plating/airless,
+/area/space)
+"yU" = (
+/obj/item/stack/sheet/metal,
+/obj/item/stack/sheet/metal,
+/turf/space,
+/area/space)
+"zd" = (
+/obj/machinery/power/apc/noalarm{
+ area = null;
+ dir = 8;
+ keep_preset_name = 1;
+ locked = 0;
+ name = "EVA room APC";
+ pixel_x = -24;
+ start_charge = -1
+ },
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"zo" = (
+/turf/simulated/wall,
+/area/space)
+"zt" = (
+/obj/item/stack/sheet/metal,
+/turf/template_noop,
+/area/space)
+"zF" = (
+/turf/unsimulated/wall/fakeglass{
+ dir = 8
+ },
+/area/ruin/unpowered)
+"zP" = (
+/obj/structure/rack{
+ dir = 4
+ },
+/obj/random/tool,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"zY" = (
+/obj/structure/table/reinforced,
+/obj/machinery/light,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"AH" = (
+/obj/structure/lattice,
+/obj/item/stack/sheet/metal,
+/turf/template_noop,
+/area/template_noop)
+"AJ" = (
+/obj/effect/spawner/random_spawners/wall_rusted_probably,
+/turf/simulated/wall,
+/area/space)
+"BG" = (
+/obj/machinery/door/airlock/mining/glass{
+ locked = 1;
+ name = "Manufacturing Control Room"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"BI" = (
+/obj/structure/reagent_dispensers/fueltank,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Cp" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"CY" = (
+/obj/item/stack/rods,
+/turf/template_noop,
+/area/template_noop)
+"Da" = (
+/turf/space,
+/area/space)
+"Dz" = (
+/mob/living/simple_animal/pet/dog/corgi,
+/obj/machinery/conveyor/east{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Ej" = (
+/obj/structure/closet/emcloset,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"EJ" = (
+/turf/simulated/wall,
+/area/ruin/unpowered)
+"Fw" = (
+/obj/item/stack/cable_coil/cut,
+/turf/template_noop,
+/area/template_noop)
+"FO" = (
+/obj/machinery/suit_storage_unit/syndicate,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Gm" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ pixel_y = 0;
+ tag = ""
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"GB" = (
+/mob/living/simple_animal/pet/dog/corgi,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Hx" = (
+/mob/living/simple_animal/pet/dog/corgi,
+/mob/living/simple_animal/pet/dog/corgi,
+/obj/machinery/conveyor/east{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Hy" = (
+/turf/template_noop,
+/area/template_noop)
+"HQ" = (
+/obj/structure/table/reinforced,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Ic" = (
+/turf/simulated/floor/plating/burnt,
+/area/ruin)
+"Id" = (
+/obj/effect/spawner/random_spawners/oil_maybe,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"IB" = (
+/obj/structure/lattice,
+/turf/space,
+/area/ruin/unpowered)
+"JI" = (
+/obj/effect/spawner/random_spawners/syndicate/loot,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Ki" = (
+/obj/structure/lattice,
+/turf/template_noop,
+/area/template_noop)
+"Kr" = (
+/obj/structure/girder,
+/turf/simulated/floor/plating,
+/area/ruin/unpowered)
+"KD" = (
+/turf/simulated/floor/plating,
+/area/ruin/unpowered)
+"KS" = (
+/turf/unsimulated/floor/plating/airless,
+/area/ruin)
+"Le" = (
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/machinery/power/port_gen/pacman,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Mi" = (
+/turf/unsimulated,
+/area/template_noop)
+"Mz" = (
+/turf/simulated/floor/plasteel,
+/area/ruin)
+"Nw" = (
+/obj/machinery/power/smes,
+/obj/structure/cable/blue{
+ d2 = 2;
+ icon_state = "0-2"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"NQ" = (
+/obj/structure/lattice,
+/turf/space,
+/area/space)
+"OA" = (
+/obj/structure/computerframe,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"OK" = (
+/obj/item/reagent_containers/food/snacks/syndicake,
+/turf/space,
+/area/space)
+"OU" = (
+/turf/simulated/floor/plasteel/airless,
+/area/ruin)
+"Qv" = (
+/obj/machinery/light{
+ dir = 8
+ },
+/obj/item/trash/syndi_cakes,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"QF" = (
+/obj/machinery/conveyor/east{
+ dir = 2;
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin)
+"QI" = (
+/obj/effect/spawner/random_spawners/wall_rusted_probably,
+/turf/simulated/wall,
+/area/ruin)
+"Ra" = (
+/obj/machinery/power/terminal{
+ dir = 4
+ },
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Rg" = (
+/obj/machinery/door/airlock/mining/glass{
+ locked = 1;
+ max_integrity = 300000;
+ name = "Corgi processing";
+ normal_integrity = 300000
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Ry" = (
+/obj/item/stack/cable_coil/random,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"RB" = (
+/mob/living/simple_animal/hostile/alien/sentinel{
+ animal_species = /mob/living/simple_animal/pet/dog;
+ attack_sound = 'sound/weapons/bite.ogg';
+ attacktext = "bites";
+ damage_coeff = list("brute" = 1, "fire" = 1, "tox" = 1, "clone" = 1, "stamina" = 1, "oxy" = 1);
+ death_sound = null;
+ deathmessage = "";
+ desc = "This is no longer a goodboy. Not anymore. He has seen too much.";
+ icon = 'icons/mob/pets.dmi';
+ icon_living = "corgi";
+ icon_state = "corgi";
+ name = "angry corgi";
+ retreat_distance = 2
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Tj" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/toolbox/syndicate,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Tt" = (
+/obj/machinery/door/airlock/external,
+/turf/simulated/floor/plating,
+/area/ruin/powered)
+"TM" = (
+/obj/structure/chair/office/dark{
+ dir = 4;
+ icon_state = "officechair_dark";
+ tag = "icon-officechair_dark (EAST)"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"TT" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8";
+ tag = ""
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4";
+ tag = "90Curve"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Uq" = (
+/obj/machinery/conveyor/east{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin)
+"UC" = (
+/obj/machinery/tcomms/relay/ruskie{
+ network_id = "SYNDICAKES-FACTORY"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"UR" = (
+/obj/item/trash/syndi_cakes,
+/turf/space,
+/area/space)
+"Vf" = (
+/obj/machinery/light{
+ dir = 8
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Vt" = (
+/obj/machinery/conveyor/north/ccw{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"VH" = (
+/obj/structure/lattice,
+/obj/random/tool,
+/turf/space,
+/area/space)
+"VR" = (
+/obj/structure/lattice,
+/turf/unsimulated,
+/area/space)
+"WL" = (
+/turf/space,
+/area/ruin/unpowered)
+"Xd" = (
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8"
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"XZ" = (
+/obj/machinery/light/small{
+ dir = 1
+ },
+/obj/structure/dispenser/oxygen,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"YR" = (
+/obj/machinery/light{
+ dir = 4
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"YU" = (
+/obj/structure/chair/office/dark,
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Zs" = (
+/obj/machinery/conveyor/east{
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/ruin/unpowered)
+"Zt" = (
+/obj/machinery/conveyor/east{
+ dir = 2;
+ id = null
+ },
+/turf/simulated/floor/plasteel,
+/area/space)
+
+(1,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(2,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(3,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+ob
+ob
+ob
+ob
+ob
+ob
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+Ki
+Ki
+Ki
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(4,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+Le
+Vf
+zd
+rU
+sN
+FO
+EJ
+EJ
+EJ
+pb
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(5,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+JI
+GB
+Vf
+sN
+sN
+EJ
+Ra
+sN
+Gm
+sN
+sN
+sN
+Tt
+KD
+Tt
+pb
+pb
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(6,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+sN
+Hx
+GB
+Dz
+sN
+EJ
+Nw
+hm
+TT
+Id
+sN
+Ej
+EJ
+EJ
+EJ
+pb
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(7,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+sN
+Dz
+sN
+Dz
+sN
+EJ
+UC
+sN
+Gm
+sN
+sN
+zY
+EJ
+pd
+pd
+ls
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(8,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+sN
+Zs
+sN
+Zs
+sN
+EJ
+fq
+sN
+Gm
+sN
+YU
+OA
+EJ
+pd
+pd
+ls
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(9,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+sN
+Zs
+sN
+Zs
+Id
+EJ
+sN
+GB
+Gm
+TM
+sN
+sN
+EJ
+pd
+pd
+ls
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(10,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+pR
+Vt
+Vt
+Vt
+sN
+EJ
+XZ
+BI
+Gm
+uA
+OA
+jR
+EJ
+pd
+pd
+ls
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(11,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+Zs
+EJ
+EJ
+EJ
+Rg
+EJ
+EJ
+EJ
+qS
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+EJ
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(12,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+Zs
+zF
+TM
+Qv
+lK
+EJ
+sN
+Vf
+Gm
+sN
+sN
+Ry
+Kr
+OU
+OU
+OU
+EJ
+pd
+pd
+pd
+OK
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(13,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+Zs
+hs
+Tj
+zP
+Xd
+ky
+Cp
+Cp
+es
+sN
+rJ
+rJ
+Kr
+rL
+eY
+OU
+EJ
+pd
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(14,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+Zs
+EJ
+sN
+wB
+sN
+BG
+sN
+sN
+sN
+sN
+sN
+sN
+oO
+OU
+eY
+eY
+EJ
+pd
+pd
+pd
+pd
+oJ
+Hy
+Hy
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+"}
+(15,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+Zs
+zF
+TM
+RB
+wB
+EJ
+IB
+sN
+sN
+sN
+sN
+sN
+EJ
+eY
+eY
+rJ
+EJ
+pd
+pd
+mx
+pd
+pd
+pd
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(16,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+Zs
+hs
+bb
+OA
+sN
+EJ
+WL
+IB
+sN
+YR
+HQ
+HQ
+EJ
+OU
+eY
+rJ
+EJ
+zt
+oJ
+pd
+pd
+pd
+jv
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(17,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+EJ
+uF
+EJ
+EJ
+EJ
+fU
+EJ
+EJ
+EJ
+oO
+EJ
+EJ
+EJ
+EJ
+OU
+eY
+OK
+Da
+pd
+pd
+NQ
+zt
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(18,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+VR
+Uq
+OU
+OU
+KS
+KS
+Mz
+ho
+OU
+OU
+OU
+OU
+ho
+iK
+eY
+Da
+OK
+OK
+pd
+pd
+NQ
+zo
+pd
+pd
+pd
+pd
+OK
+Hy
+Hy
+fY
+Hy
+Hy
+Hy
+"}
+(19,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+pd
+rJ
+rJ
+rJ
+KS
+rJ
+KS
+rJ
+Ic
+Ic
+ub
+NQ
+OU
+eY
+NQ
+NQ
+Da
+OK
+yU
+Da
+NQ
+AJ
+pd
+OK
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(20,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+pd
+nV
+xT
+mc
+KS
+mc
+KS
+KS
+Da
+NQ
+Da
+NQ
+Da
+Da
+UR
+ub
+Da
+Da
+pd
+pd
+NQ
+NQ
+pd
+pd
+pd
+zt
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(21,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Ki
+pp
+pd
+Zt
+rJ
+Zt
+Zt
+NQ
+Ic
+Da
+Da
+Da
+Da
+OK
+Da
+Da
+OK
+OK
+Da
+pd
+NQ
+Da
+Da
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+Hy
+Ki
+Hy
+Hy
+"}
+(22,1,1) = {"
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+Hy
+pp
+pd
+pd
+zo
+pd
+zo
+zo
+NQ
+ub
+Da
+Da
+Da
+Da
+Da
+Da
+Da
+Da
+Da
+ub
+zt
+pd
+pd
+pd
+mx
+pd
+pd
+pd
+pd
+OK
+Hy
+Hy
+Hy
+Hy
+"}
+(23,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Ki
+Mi
+dy
+VR
+pd
+pd
+pd
+pd
+pd
+zt
+pd
+pd
+Ic
+OU
+OU
+Da
+NQ
+pd
+pd
+pd
+pd
+pd
+pd
+oJ
+pd
+zt
+ls
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+"}
+(24,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Ki
+Hy
+Hy
+Hy
+Ki
+Hy
+pd
+oJ
+OK
+pd
+pd
+bk
+NQ
+QF
+QF
+QF
+QF
+Da
+sd
+oJ
+pd
+pd
+pd
+cc
+VH
+pd
+pd
+pd
+pd
+pd
+pd
+ls
+Hy
+Hy
+Hy
+Hy
+"}
+(25,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+Hy
+pd
+pd
+ke
+OK
+pd
+zo
+NQ
+NQ
+QI
+NQ
+NQ
+pd
+pd
+zt
+NQ
+NQ
+om
+zo
+pd
+OK
+pd
+oJ
+pd
+pd
+zt
+Hy
+Hy
+Hy
+Hy
+"}
+(26,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Ki
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+NQ
+ls
+pd
+pd
+pd
+pd
+pd
+zo
+zo
+Da
+Da
+pd
+oJ
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+"}
+(27,1,1) = {"
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+Hy
+AH
+CY
+CY
+Hy
+fY
+Hy
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+OK
+pd
+oJ
+pd
+mx
+pd
+pd
+OK
+pd
+pd
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+"}
+(28,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+pd
+NQ
+OK
+pd
+pd
+aM
+pd
+mx
+pd
+pd
+pd
+oJ
+pd
+pd
+pd
+pd
+pd
+pd
+aM
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+jf
+"}
+(29,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+Fw
+Hy
+pd
+oJ
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+OK
+oJ
+pd
+kh
+pd
+pd
+pd
+pd
+pd
+pd
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+"}
+(30,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+CY
+CY
+Hy
+pd
+pd
+pd
+pd
+ls
+pd
+OK
+pd
+pd
+pd
+pd
+pd
+pd
+pd
+oJ
+zt
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(31,1,1) = {"
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+Ki
+Hy
+Hy
+Hy
+Ki
+Hy
+CY
+Hy
+Hy
+Hy
+Hy
+pd
+pd
+pd
+pd
+pd
+ls
+pd
+pd
+oJ
+pd
+pd
+aM
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(32,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+kh
+pd
+pd
+zt
+pd
+pd
+ls
+pd
+pd
+pd
+pd
+pd
+pd
+Hy
+Hy
+Fw
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(33,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+CY
+Hy
+CY
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+Hy
+jf
+Hy
+Hy
+Hy
+Hy
+pd
+pd
+pd
+pd
+pd
+NQ
+zt
+pd
+pd
+oJ
+pd
+fY
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(34,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+CY
+Hy
+fY
+Hy
+CY
+Hy
+Hy
+pd
+oJ
+pd
+pd
+OK
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(35,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Ki
+Hy
+Hy
+Hy
+CY
+CY
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+pd
+pd
+ls
+pd
+pd
+pd
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(36,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+CY
+Hy
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(37,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Fw
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(38,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(39,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+jf
+Hy
+Hy
+Hy
+Hy
+fY
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
+(40,1,1) = {"
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+Hy
+"}
diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm
index 2c12ac11e44..8f6f8d77f7f 100644
--- a/_maps/map_files/cyberiad/cyberiad.dmm
+++ b/_maps/map_files/cyberiad/cyberiad.dmm
@@ -35244,7 +35244,7 @@
},
/area/bridge)
"bnW" = (
-/obj/machinery/computer/station_alert/all,
+/obj/machinery/computer/station_alert,
/turf/simulated/floor/plasteel{
dir = 0;
icon_state = "yellow"
@@ -72578,7 +72578,7 @@
pixel_x = -2;
pixel_y = 2
},
-/obj/item/circuitboard/stationalert_all{
+/obj/item/circuitboard/stationalert{
pixel_x = 1;
pixel_y = -1
},
@@ -75846,7 +75846,7 @@
pixel_x = 32;
pixel_y = 0
},
-/obj/machinery/computer/station_alert/all,
+/obj/machinery/computer/station_alert,
/obj/structure/cable/yellow{
d1 = 4;
d2 = 8;
@@ -77210,6 +77210,10 @@
pixel_x = -22
},
/obj/item/stack/tape_roll,
+/obj/machinery/camera/motion{
+ c_tag = "EVA Motion Sensor";
+ dir = 4
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
diff --git a/_maps/map_files/shuttles/admin_armory.dmm b/_maps/map_files/shuttles/admin_armory.dmm
new file mode 100644
index 00000000000..837f867a52b
--- /dev/null
+++ b/_maps/map_files/shuttles/admin_armory.dmm
@@ -0,0 +1,1691 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"ae" = (
+/obj/structure/sign/securearea,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall4";
+ tag = "icon-swall14"
+ },
+/area/shuttle/administration)
+"al" = (
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/table/reinforced,
+/obj/item/folder,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"aI" = (
+/obj/structure/table/reinforced,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/machinery/recharger,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"aP" = (
+/obj/machinery/vending/security,
+/obj/machinery/light/spot{
+ dir = 8;
+ icon_state = "tube1";
+ tag = "icon-tube1 (WEST)"
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"bi" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/firstaid,
+/obj/item/storage/firstaid/brute,
+/obj/item/storage/box/bodybags,
+/obj/item/storage/firstaid/surgery,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"bm" = (
+/obj/structure/grille,
+/obj/machinery/door/poddoor/shutters/preopen{
+ dir = 8;
+ id_tag = "adminarmoryshutters"
+ },
+/obj/structure/window/full/shuttle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"ce" = (
+/obj/machinery/autolathe/upgraded{
+ hacked = 1
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"cl" = (
+/obj/structure/chair/comfy/shuttle{
+ dir = 4
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"cV" = (
+/obj/structure/closet/emcloset,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"de" = (
+/obj/machinery/suit_storage_unit/security/secure,
+/obj/machinery/light/spot,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"df" = (
+/obj/structure/rack,
+/obj/item/storage/box/buck{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/storage/box/buck,
+/obj/structure/window/reinforced,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"dn" = (
+/obj/machinery/computer/camera_advanced/shuttle_docker/admin{
+ name = "NRV Sparta navigation computer"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"dH" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/gun/energy/gun,
+/obj/item/gun/energy/gun{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"dP" = (
+/obj/machinery/recharge_station/upgraded,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"eI" = (
+/obj/machinery/turretid/stun{
+ check_access = 0;
+ name = "Shuttle Turret Control";
+ pixel_y = 30
+ },
+/obj/machinery/computer/camera_advanced,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"eL" = (
+/obj/structure/chair{
+ dir = 8
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"fo" = (
+/obj/structure/chair{
+ dir = 1
+ },
+/obj/machinery/light/spot,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"fF" = (
+/obj/machinery/light/spot{
+ dir = 4;
+ icon_state = "tube1";
+ tag = "icon-tube1 (EAST)"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"fQ" = (
+/obj/structure/chair/comfy/shuttle{
+ dir = 1
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"gd" = (
+/obj/machinery/door/airlock/shuttle,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"gh" = (
+/obj/structure/chair/comfy/shuttle{
+ dir = 8
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"gj" = (
+/obj/machinery/computer/communications{
+ name = "NRV Sparta Communications Console"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"hN" = (
+/obj/structure/chair{
+ dir = 4
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"ir" = (
+/obj/structure/closet/emcloset,
+/obj/machinery/light/spot{
+ dir = 1;
+ icon_state = "tube1";
+ tag = "icon-tube1 (NORTH)"
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"iz" = (
+/obj/machinery/porta_turret{
+ installation = /obj/item/gun/energy/gun
+ },
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"jn" = (
+/obj/machinery/door/poddoor/shutters{
+ density = 0;
+ dir = 2;
+ icon_state = "open";
+ id_tag = "Asclshutters";
+ name = "Blast Shutters";
+ opacity = 0
+ },
+/obj/structure/grille,
+/obj/structure/window/full/shuttle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"jB" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/lockbox/mindshield,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"jU" = (
+/obj/machinery/suit_storage_unit/security/secure,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"kf" = (
+/obj/structure/sign/securearea,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall8";
+ tag = "icon-swall12"
+ },
+/area/shuttle/administration)
+"mq" = (
+/obj/structure/grille,
+/obj/machinery/door/poddoor/shutters{
+ density = 0;
+ dir = 2;
+ icon_state = "open";
+ id_tag = "Asclshutters";
+ name = "Blast Shutters";
+ opacity = 0
+ },
+/obj/structure/window/full/shuttle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"mw" = (
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"mM" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall13"
+ },
+/area/shuttle/administration)
+"nG" = (
+/obj/structure/window/full/shuttle,
+/obj/structure/grille,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"oj" = (
+/obj/structure/chair/comfy/shuttle{
+ dir = 1
+ },
+/obj/machinery/light/spot,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"oY" = (
+/obj/structure/chair,
+/obj/machinery/light/spot{
+ dir = 1;
+ icon_state = "tube1";
+ tag = "icon-tube1 (NORTH)"
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"pa" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall11"
+ },
+/area/shuttle/administration)
+"pK" = (
+/obj/structure/shuttle/engine/propulsion{
+ dir = 4;
+ icon_state = "propulsion"
+ },
+/turf/simulated/shuttle/plating,
+/area/shuttle/administration)
+"pQ" = (
+/obj/structure/chair{
+ dir = 8
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"qQ" = (
+/obj/machinery/recharger/wallcharger{
+ pixel_y = 25
+ },
+/obj/machinery/recharger/wallcharger{
+ pixel_y = 35
+ },
+/obj/machinery/light/spot{
+ dir = 1;
+ icon_state = "tube1";
+ tag = "icon-tube1 (NORTH)"
+ },
+/obj/machinery/door_control{
+ id = "adminshuttlebridge";
+ name = "Bridge Privacy Shutters";
+ pixel_x = 25;
+ req_access_txt = "19"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"rb" = (
+/obj/machinery/door/window/brigdoor/southleft{
+ req_one_access_txt = "2;19"
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"rW" = (
+/obj/structure/grille,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id_tag = "adminshuttlebridge"
+ },
+/obj/structure/window/full/shuttle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"sq" = (
+/obj/structure/table/reinforced,
+/obj/item/kitchen/knife/combat{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/kitchen/knife/combat,
+/obj/item/kitchen/knife/combat{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"tx" = (
+/obj/machinery/sleeper/upgraded{
+ dir = 4
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"ua" = (
+/obj/structure/chair{
+ dir = 8
+ },
+/obj/machinery/door_control{
+ dir = 4;
+ id = "Asclshutters";
+ name = "External Window Shutter Control";
+ pixel_x = -5;
+ pixel_y = 35;
+ req_access_txt = "19"
+ },
+/obj/machinery/keycard_auth{
+ pixel_y = 24
+ },
+/obj/machinery/door_control{
+ id = "asclblast";
+ name = "Airlock Blast Door Control";
+ pixel_x = 5;
+ pixel_y = 35;
+ req_access = null;
+ req_access_txt = "19"
+ },
+/obj/structure/chair/comfy/shuttle{
+ dir = 8
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"uz" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall4";
+ tag = "icon-swall14"
+ },
+/area/shuttle/administration)
+"uW" = (
+/obj/structure/shuttle/engine/heater{
+ dir = 4;
+ icon_state = "heater"
+ },
+/obj/structure/window/plasmareinforced{
+ dir = 8
+ },
+/turf/simulated/shuttle/plating,
+/area/shuttle/administration)
+"uY" = (
+/obj/structure/window/reinforced,
+/obj/structure/table/reinforced,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"vk" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall_s6"
+ },
+/area/shuttle/administration)
+"vy" = (
+/obj/structure/table/reinforced,
+/obj/item/stack/sheet/metal/fifty,
+/obj/item/stack/sheet/glass/fifty,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"vA" = (
+/obj/machinery/light/spot{
+ dir = 1;
+ icon_state = "tube1";
+ tag = "icon-tube1 (NORTH)"
+ },
+/obj/machinery/portable_atmospherics/canister/oxygen,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"vW" = (
+/obj/structure/table/reinforced,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/storage/box/handcuffs,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"wG" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/item/clothing/suit/armor/bulletproof{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/obj/item/clothing/head/helmet/alt,
+/obj/item/clothing/suit/armor/bulletproof{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/obj/item/clothing/head/helmet/alt,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"wH" = (
+/obj/machinery/light/spot{
+ dir = 8;
+ icon_state = "tube1";
+ tag = "icon-tube1 (WEST)"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"wQ" = (
+/obj/machinery/door/poddoor/shutters{
+ density = 0;
+ dir = 8;
+ icon_state = "open";
+ id_tag = "Asclshutters";
+ name = "Blast Shutters";
+ opacity = 0
+ },
+/obj/structure/grille,
+/obj/structure/window/full/shuttle,
+/turf/space,
+/area/shuttle/administration)
+"xm" = (
+/obj/structure/table/reinforced,
+/obj/item/rcd/preloaded,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/obj/item/rcd_ammo{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"xF" = (
+/obj/structure/rack,
+/obj/item/storage/box/buck{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/storage/box/buck,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"xH" = (
+/obj/machinery/door/airlock/shuttle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"xK" = (
+/obj/structure/sign/nosmoking_2,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall3";
+ tag = "icon-swall3"
+ },
+/area/shuttle/administration)
+"xQ" = (
+/obj/structure/chair{
+ dir = 4
+ },
+/obj/machinery/light/spot{
+ dir = 1;
+ icon_state = "tube1";
+ tag = "icon-tube1 (NORTH)"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"xU" = (
+/obj/structure/table/reinforced,
+/obj/item/stack/sheet/metal/fifty,
+/obj/item/stack/sheet/glass/fifty,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"xZ" = (
+/turf/space,
+/area/shuttle/administration)
+"yg" = (
+/obj/structure/table/reinforced,
+/obj/machinery/door/window/brigdoor/eastleft{
+ name = "Armory";
+ req_access_txt = "3"
+ },
+/obj/machinery/door/window/brigdoor/westleft{
+ name = "Armory"
+ },
+/obj/machinery/door/poddoor/shutters/preopen{
+ dir = 8;
+ id_tag = "adminarmoryshutters"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"yu" = (
+/obj/structure/table/reinforced,
+/obj/machinery/recharger{
+ pixel_x = -5
+ },
+/obj/machinery/recharger{
+ pixel_x = 8
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"yF" = (
+/obj/machinery/status_display,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall3"
+ },
+/area/shuttle/administration)
+"zY" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall14"
+ },
+/area/shuttle/administration)
+"AH" = (
+/obj/machinery/light/spot,
+/obj/machinery/porta_turret{
+ installation = /obj/item/gun/energy/gun
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Bc" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/shield/riot,
+/obj/item/shield/riot{
+ pixel_x = 2;
+ pixel_y = -4
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Bj" = (
+/obj/structure/fans/tiny,
+/obj/machinery/door/airlock/external{
+ req_one_access_txt = "2;19"
+ },
+/obj/machinery/door/poddoor{
+ density = 0;
+ icon_state = "open";
+ id_tag = "asclblast";
+ name = "Blast Door";
+ opacity = 0
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Bn" = (
+/obj/machinery/door/window/brigdoor/southleft{
+ req_one_access_txt = "2;19"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Bq" = (
+/obj/structure/sign/nosmoking_2,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall1";
+ tag = "icon-swall1"
+ },
+/area/shuttle/administration)
+"BZ" = (
+/obj/machinery/light/spot,
+/obj/machinery/porta_turret{
+ installation = /obj/item/gun/energy/gun
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Cl" = (
+/obj/structure/table/reinforced,
+/obj/item/storage/toolbox/emergency{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/storage/toolbox/mechanical,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Du" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced,
+/obj/item/gun/energy/gun,
+/obj/item/gun/energy/gun{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"DZ" = (
+/obj/machinery/light/spot,
+/obj/structure/closet/emcloset,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Eq" = (
+/obj/structure/sign/securearea,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall12"
+ },
+/area/shuttle/administration)
+"EQ" = (
+/obj/machinery/door/airlock/shuttle/glass,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Fd" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/gun/energy/gun,
+/obj/item/gun/energy/gun{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Fo" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/gun/energy/immolator/multi{
+ pixel_y = -3
+ },
+/obj/item/gun/energy/immolator/multi,
+/obj/item/gun/energy/immolator/multi{
+ pixel_y = 3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"FF" = (
+/obj/structure/sign/poster/official/enlist,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall12"
+ },
+/area/shuttle/administration)
+"FT" = (
+/obj/structure/rack,
+/obj/item/storage/box/buck{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/storage/box/buck,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Gf" = (
+/obj/structure/sign/vacuum{
+ pixel_x = -32
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Gn" = (
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/rack,
+/obj/item/gun/projectile/shotgun/riot/buckshot{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/gun/projectile/shotgun/riot/buckshot,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Gx" = (
+/obj/structure/fans/tiny,
+/obj/machinery/door/airlock/external,
+/obj/docking_port/mobile{
+ dir = 2;
+ dwidth = 9;
+ height = 18;
+ id = "admin";
+ name = "armory";
+ roundstart_move = "cc_bay_1";
+ timid = 1;
+ width = 19
+ },
+/obj/machinery/door/poddoor{
+ density = 0;
+ icon_state = "open";
+ id_tag = "asclblast";
+ name = "Blast Door";
+ opacity = 0
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Gy" = (
+/obj/structure/chair{
+ dir = 1
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Hc" = (
+/obj/machinery/porta_turret{
+ installation = /obj/item/gun/energy/gun
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"HN" = (
+/obj/structure/sign/poster/official/do_not_question,
+/obj/item/tank/emergency_oxygen/engi,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall3"
+ },
+/area/shuttle/administration)
+"Ir" = (
+/obj/machinery/computer/shuttle/admin{
+ name = "NRV Sparta shuttle console"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"IC" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall3"
+ },
+/area/shuttle/administration)
+"Jt" = (
+/obj/machinery/suit_storage_unit/standard_unit,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Ke" = (
+/obj/machinery/door/window/brigdoor/southright{
+ req_one_access_txt = "2;19"
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Kh" = (
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Ki" = (
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/clothing/suit/armor/bulletproof{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/obj/structure/rack,
+/obj/item/clothing/head/helmet/alt,
+/obj/item/clothing/suit/armor/bulletproof{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/obj/item/clothing/head/helmet/alt,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"KM" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall7"
+ },
+/area/shuttle/administration)
+"Lh" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/gun/energy/ionrifle{
+ pixel_x = 3;
+ pixel_y = 3
+ },
+/obj/item/gun/energy/ionrifle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"LK" = (
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/machinery/computer/secure_data,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"LP" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall_s9"
+ },
+/area/shuttle/administration)
+"Mi" = (
+/obj/structure/table/reinforced,
+/obj/machinery/recharger,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Mj" = (
+/obj/machinery/vending/medical,
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Mo" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/clothing/suit/armor/bulletproof{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/obj/item/clothing/head/helmet/alt,
+/obj/item/clothing/suit/armor/bulletproof{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/obj/item/clothing/head/helmet/alt,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"MN" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall12"
+ },
+/area/shuttle/administration)
+"MZ" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall3";
+ tag = "icon-swall3"
+ },
+/area/shuttle/administration)
+"Ni" = (
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/rack,
+/obj/item/gun/projectile/shotgun/riot/buckshot{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/gun/projectile/shotgun/riot/buckshot,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Ns" = (
+/obj/machinery/recharger/wallcharger{
+ pixel_y = 25
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"NF" = (
+/obj/structure/sign/poster/official/here_for_your_safety,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall3";
+ tag = "icon-swall3"
+ },
+/area/shuttle/administration)
+"NQ" = (
+/obj/machinery/door/poddoor/shutters{
+ density = 0;
+ dir = 1;
+ icon_state = "open";
+ id_tag = "Asclshutters";
+ name = "Blast Shutters";
+ opacity = 0
+ },
+/obj/structure/grille,
+/obj/structure/window/full/shuttle,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Op" = (
+/obj/structure/dispenser/oxygen,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Ou" = (
+/obj/machinery/autolathe/upgraded{
+ hacked = 1
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Pe" = (
+/obj/machinery/door/window/brigdoor/southright{
+ req_one_access_txt = "2;19"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Pn" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/clothing/suit/armor/riot,
+/obj/item/clothing/suit/armor/riot{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"PL" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall_s10"
+ },
+/area/shuttle/administration)
+"PR" = (
+/obj/machinery/light/spot{
+ dir = 8;
+ tag = "icon-tube1 (WEST)"
+ },
+/obj/machinery/door_control{
+ id = "adminarmoryshutters";
+ name = "Armory Internal Shutters";
+ pixel_x = -26;
+ req_access_txt = "3"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"PS" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/gun/energy/gun/nuclear{
+ pixel_y = -3
+ },
+/obj/item/gun/energy/gun/nuclear,
+/obj/item/gun/energy/gun/nuclear{
+ pixel_y = 3
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"PV" = (
+/obj/structure/rack,
+/obj/item/gun/energy/sniperrifle{
+ pixel_y = -4
+ },
+/obj/item/gun/energy/sniperrifle,
+/obj/item/gun/energy/sniperrifle{
+ pixel_y = 3
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"RM" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall8";
+ tag = "icon-swall12"
+ },
+/area/shuttle/administration)
+"TD" = (
+/obj/machinery/door/airlock/command{
+ name = "Command Center";
+ req_access = null;
+ req_access_txt = "19"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"TF" = (
+/obj/structure/table/reinforced,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced,
+/obj/item/storage/fancy/donut_box,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Un" = (
+/obj/structure/table/reinforced,
+/obj/item/tank/emergency_oxygen/engi{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/obj/item/tank/emergency_oxygen/engi,
+/obj/item/tank/emergency_oxygen/engi{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/machinery/light/spot{
+ dir = 8;
+ icon_state = "tube1";
+ tag = "icon-tube1 (WEST)"
+ },
+/obj/item/clothing/mask/breath{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/clothing/mask/breath,
+/obj/item/clothing/mask/breath{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"Ur" = (
+/obj/machinery/porta_turret{
+ installation = /obj/item/gun/energy/gun
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/turf/simulated/shuttle/floor,
+/area/shuttle/administration)
+"UR" = (
+/obj/machinery/status_display,
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall12"
+ },
+/area/shuttle/administration)
+"UX" = (
+/obj/structure/fans/tiny,
+/obj/machinery/door/airlock/external,
+/obj/machinery/door/poddoor{
+ density = 0;
+ icon_state = "open";
+ id_tag = "asclblast";
+ name = "Blast Door";
+ opacity = 0
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"Vc" = (
+/turf/simulated/shuttle/wall{
+ dir = 2;
+ icon_state = "swall_s5"
+ },
+/area/shuttle/administration)
+"VC" = (
+/obj/machinery/door/airlock/shuttle{
+ name = "Shuttle Armory";
+ req_access_txt = "3"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"VQ" = (
+/obj/structure/rack,
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/gun/energy/gun/advtaser{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/obj/item/gun/energy/gun/advtaser{
+ pixel_x = 0;
+ pixel_y = 0
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"WV" = (
+/obj/structure/table/reinforced,
+/obj/item/restraints/handcuffs{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/obj/item/restraints/handcuffs,
+/obj/item/restraints/handcuffs{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/machinery/light/spot{
+ dir = 4;
+ icon_state = "tube1";
+ tag = "icon-tube1 (EAST)"
+ },
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+"XL" = (
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/rack,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/gun/projectile/shotgun/riot/buckshot{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/gun/projectile/shotgun/riot/buckshot,
+/turf/simulated/shuttle/floor{
+ icon_state = "floor4"
+ },
+/area/shuttle/administration)
+
+(1,1,1) = {"
+vk
+MZ
+HN
+IC
+IC
+IC
+IC
+KM
+wQ
+wQ
+wQ
+wQ
+KM
+IC
+IC
+IC
+IC
+Vc
+"}
+(2,1,1) = {"
+MN
+Un
+xm
+Mj
+aP
+dP
+tx
+MN
+eI
+Ir
+dn
+gj
+MN
+vA
+cl
+cl
+DZ
+Eq
+"}
+(3,1,1) = {"
+MN
+Ns
+mw
+mw
+mw
+mw
+mw
+MN
+ua
+gh
+gh
+gh
+FF
+Op
+Kh
+Kh
+Kh
+Bj
+"}
+(4,1,1) = {"
+MN
+Ns
+mw
+mw
+mw
+mw
+Gy
+MN
+qQ
+Kh
+Kh
+Kh
+UR
+Jt
+Kh
+Kh
+Kh
+Bj
+"}
+(5,1,1) = {"
+MN
+Ns
+mw
+mw
+mw
+mw
+fo
+zY
+MZ
+TD
+rW
+rW
+pa
+MZ
+xH
+nG
+nG
+Eq
+"}
+(6,1,1) = {"
+MN
+bi
+Cl
+vy
+Ou
+mw
+mw
+MN
+cV
+Kh
+Kh
+Kh
+Bn
+Gf
+Kh
+Mi
+Mi
+MN
+"}
+(7,1,1) = {"
+zY
+nG
+nG
+nG
+nG
+gd
+nG
+MN
+xQ
+Kh
+hN
+Kh
+Pe
+Kh
+Kh
+Kh
+oj
+MN
+"}
+(8,1,1) = {"
+RM
+oY
+mw
+mw
+mw
+mw
+Ur
+kf
+vW
+al
+aI
+LK
+TF
+Kh
+Kh
+Kh
+fQ
+mq
+"}
+(9,1,1) = {"
+UX
+mw
+mw
+mw
+mw
+mw
+mw
+EQ
+mw
+mw
+mw
+mw
+rb
+Kh
+Kh
+Kh
+fQ
+jn
+"}
+(10,1,1) = {"
+Gx
+mw
+mw
+mw
+mw
+mw
+mw
+EQ
+mw
+mw
+mw
+mw
+Ke
+Kh
+Kh
+Kh
+Kh
+jn
+"}
+(11,1,1) = {"
+uz
+oY
+mw
+mw
+mw
+mw
+Hc
+ae
+ir
+pQ
+pQ
+pQ
+uY
+Kh
+Kh
+Kh
+BZ
+MN
+"}
+(12,1,1) = {"
+zY
+MZ
+Bq
+yg
+VC
+yg
+yF
+pa
+bm
+bm
+bm
+MZ
+MZ
+xK
+VC
+NF
+MZ
+mM
+"}
+(13,1,1) = {"
+MN
+Kh
+wH
+eL
+Kh
+eL
+Kh
+PR
+PV
+PS
+Fo
+wH
+VQ
+Bc
+Kh
+Kh
+AH
+MN
+"}
+(14,1,1) = {"
+NQ
+Kh
+dH
+Ni
+Kh
+FT
+Mo
+Kh
+Kh
+Kh
+Kh
+Kh
+Lh
+Pn
+Kh
+Kh
+Kh
+mq
+"}
+(15,1,1) = {"
+NQ
+Kh
+Du
+Gn
+Kh
+df
+wG
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+mq
+"}
+(16,1,1) = {"
+NQ
+Kh
+Fd
+XL
+Kh
+xF
+Ki
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+Kh
+mq
+"}
+(17,1,1) = {"
+MN
+fF
+Kh
+Kh
+fF
+Kh
+Kh
+fF
+iz
+yu
+sq
+WV
+xU
+ce
+jB
+jU
+de
+MN
+"}
+(18,1,1) = {"
+PL
+KM
+uW
+uW
+KM
+IC
+IC
+KM
+uW
+uW
+KM
+IC
+IC
+KM
+uW
+uW
+KM
+LP
+"}
+(19,1,1) = {"
+xZ
+PL
+pK
+pK
+LP
+xZ
+xZ
+PL
+pK
+pK
+LP
+xZ
+xZ
+PL
+pK
+pK
+LP
+xZ
+"}
diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm
index 66ffca0aef5..5725a7a8e9b 100644
--- a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm
@@ -128,66 +128,56 @@
/obj/machinery/atmospherics/unary/vent_pump/process_atmos()
..()
- if((stat & (NOPOWER|BROKEN)))
- return 0
+ if(stat & (NOPOWER|BROKEN))
+ return FALSE
if(!node)
- on = 0
+ on = FALSE
//broadcast_status() // from now air alarm/control computer should request update purposely --rastaf0
if(!on)
- return 0
+ return FALSE
if(welded)
if(air_contents.return_pressure() >= weld_burst_pressure && prob(5)) //the weld is on but the cover is welded shut, can it withstand the internal pressure?
visible_message("The welded cover of [src] bursts open!")
- for(var/mob/M in range(1, src))
+ for(var/mob/living/M in range(1))
unsafe_pressure_release(M, air_contents.return_pressure()) //let's send everyone flying
welded = FALSE
update_icon()
- return 0
+ return FALSE
var/datum/gas_mixture/environment = loc.return_air()
var/environment_pressure = environment.return_pressure()
-
if(pump_direction) //internal -> external
var/pressure_delta = 10000
-
- if(pressure_checks&1)
+ if(pressure_checks & 1)
pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
- if(pressure_checks&2)
+ if(pressure_checks & 2)
pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound))
- if(pressure_delta > 0.5)
- if(air_contents.temperature > 0)
- var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
-
- loc.assume_air(removed)
- air_update_turf()
-
- parent.update = 1
+ if(pressure_delta > 0.5 && air_contents.temperature > 0)
+ var/transfer_moles = pressure_delta * environment.volume / (air_contents.temperature * R_IDEAL_GAS_EQUATION)
+ var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
+ loc.assume_air(removed)
+ air_update_turf()
+ parent.update = TRUE
else //external -> internal
var/pressure_delta = 10000
- if(pressure_checks&1)
+ if(pressure_checks & 1)
pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
- if(pressure_checks&2)
+ if(pressure_checks & 2)
pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure()))
- if(pressure_delta > 0.5)
- if(environment.temperature > 0)
- var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
+ if(pressure_delta > 0.5 && environment.temperature > 0)
+ var/transfer_moles = pressure_delta * air_contents.volume / (environment.temperature * R_IDEAL_GAS_EQUATION)
+ var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
+ if(isnull(removed)) //in space
+ return
+ air_contents.merge(removed)
+ air_update_turf()
+ parent.update = TRUE
- var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
- if(isnull(removed)) //in space
- return
-
- air_contents.merge(removed)
- air_update_turf()
-
- parent.update = 1
-
- return 1
+ return TRUE
//Radio remote control
@@ -386,11 +376,11 @@
if(I.use_tool(src, user, 20, volume = I.tool_volume))
if(!welded)
welded = TRUE
- visible_message("[user] welds [src] shut!",\
+ user.visible_message("[user] welds [src] shut!",\
"You weld [src] shut!")
else
welded = FALSE
- visible_message("[user] unwelds [src]!",\
+ user.visible_message("[user] unwelds [src]!",\
"You unweld [src]!")
update_icon()
diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm
index ed06dd68ce6..bbd5e760d2f 100644
--- a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm
@@ -404,10 +404,10 @@
if(I.use_tool(src, user, 20, volume = I.tool_volume))
if(!welded)
welded = TRUE
- visible_message("[user] welds [src] shut!",\
+ user.visible_message("[user] welds [src] shut!",\
"You weld [src] shut!")
else
welded = FALSE
- visible_message("[user] unwelds [src]!",\
+ user.visible_message("[user] unwelds [src]!",\
"You unweld [src]!")
update_icon()
diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm
index fc5b6eb74b9..1ef94c8ba65 100644
--- a/code/__DEFINES/construction.dm
+++ b/code/__DEFINES/construction.dm
@@ -28,6 +28,12 @@
#define AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS 1
#define AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER 2
+//used by airlocks and airlock wires.
+#define AICONTROLDISABLED_OFF 0 // Silicons can control the airlock normally.
+#define AICONTROLDISABLED_ON 1 // Silicons cannot control the airlock, but can hack the airlock.
+#define AICONTROLDISABLED_BYPASS 2 // Silicons can control the airlock because they succeeded on the hack
+#define AICONTROLDISABLED_PERMA 3 // Wire cutting an airlock on AICONTROLDISABLED_BYPASS toggles it between AICONTROLDISABLED_BYPASS and this.
+
//plastic flaps construction states
#define PLASTIC_FLAPS_NORMAL 0
#define PLASTIC_FLAPS_DETACHED 1
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 10aa1718dda..26b1d2d44aa 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -726,3 +726,7 @@
#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt"
///from monkey CtrlClickOn(): (/mob)
#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl"
+
+///SSalarm signals
+#define COMSIG_TRIGGERED_ALARM "ssalarm_triggered"
+#define COMSIG_CANCELLED_ALARM "ssalarm_cancelled"
diff --git a/code/__DEFINES/instruments.dm b/code/__DEFINES/instruments.dm
new file mode 100644
index 00000000000..64c77abc9fa
--- /dev/null
+++ b/code/__DEFINES/instruments.dm
@@ -0,0 +1,29 @@
+#define INSTRUMENT_MIN_OCTAVE 1
+#define INSTRUMENT_MAX_OCTAVE 9
+#define INSTRUMENT_MIN_KEY 0
+#define INSTRUMENT_MAX_KEY 127
+
+/// Max number of playing notes per instrument.
+#define CHANNELS_PER_INSTRUMENT 128
+
+/// Distance multiplier that makes us not be impacted by 3d sound as much. This is a multiplier so lower it is the closer we will pretend to be to people.
+#define INSTRUMENT_DISTANCE_FALLOFF_BUFF 0.2
+/// How many tiles instruments have no falloff for
+#define INSTRUMENT_DISTANCE_NO_FALLOFF 3
+
+/// Maximum length a note should ever go for
+#define INSTRUMENT_MAX_TOTAL_SUSTAIN (5 SECONDS)
+
+/// These are per decisecond.
+#define INSTRUMENT_EXP_FALLOFF_MIN 1.025 //100/(1.025^50) calculated for [INSTRUMENT_MIN_SUSTAIN_DROPOFF] to be 30.
+#define INSTRUMENT_EXP_FALLOFF_MAX 10
+
+/// Minimum volume for when the sound is considered dead.
+#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0.1
+
+#define SUSTAIN_LINEAR 1
+#define SUSTAIN_EXPONENTIAL 2
+
+// /datum/instrument instrument_flags
+#define INSTRUMENT_LEGACY (1<<0) //Legacy instrument. Implies INSTRUMENT_DO_NOT_AUTOSAMPLE
+#define INSTRUMENT_DO_NOT_AUTOSAMPLE (1<<1) //Do not automatically sample
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index ad5e5df7583..0a36db1b185 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -69,9 +69,6 @@
#define ZONE_ACTIVE 1
#define ZONE_SLEEPING 0
-#define shuttle_time_in_station 1800 // 3 minutes in the station
-#define shuttle_time_to_arrive 6000 // 10 minutes to arrive
-
#define EVENT_LEVEL_MUNDANE 1
#define EVENT_LEVEL_MODERATE 2
#define EVENT_LEVEL_MAJOR 3
@@ -245,28 +242,75 @@
0.4,0.6,0.0,\
0.2,0.2,0.6)
-#define LIST_REPLACE_RENAME list("rebeccapurple" = "dark purple", "darkslategrey" = "dark grey", "darkolivegreen" = "dark green", "darkslateblue" = "dark blue",\
- "darkkhaki" = "khaki", "darkseagreen" = "light green", "midnightblue" = "blue", "lightgrey" = "light grey", "darkgrey" = "dark grey",\
- "steelblue" = "blue", "goldenrod" = "gold")
+/*
+ Used for wire name appearances. Replaces the color name on the left with the one on the right.
+ The color on the left is the one used as the actual color of the wire, but it doesn't look good when written.
+ So, we need to replace the name to something that looks better.
+*/
+#define LIST_COLOR_RENAME \
+ list( \
+ "rebeccapurple" = "dark purple",\
+ "darkslategrey" = "dark grey", \
+ "darkolivegreen"= "dark green", \
+ "darkslateblue" = "dark blue", \
+ "darkkhaki" = "khaki", \
+ "darkseagreen" = "light green",\
+ "midnightblue" = "blue", \
+ "lightgrey" = "light grey", \
+ "darkgrey" = "dark grey", \
+ "steelblue" = "blue", \
+ "goldenrod" = "gold" \
+ )
-#define LIST_GREYSCALE_REPLACE list("red" = "lightgrey", "blue" = "grey", "green" = "grey", "orange" = "lightgrey", "brown" = "grey",\
- "gold" = "lightgrey", "cyan" = "lightgrey", "navy" = "grey", "purple" = "grey", "pink"= "lightgrey")
+/// Pure Black and white colorblindness. Every species except Vulpkanins and Tajarans will have this.
+#define GREYSCALE_COLOR_REPLACE \
+ list( \
+ "red" = "grey", \
+ "blue" = "grey", \
+ "green" = "grey", \
+ "orange" = "light grey", \
+ "brown" = "grey", \
+ "gold" = "light grey", \
+ "cyan" = "silver", \
+ "magenta" = "grey", \
+ "purple" = "grey", \
+ "pink" = "light grey" \
+ )
-#define LIST_VULP_REPLACE list("pink" = "beige", "orange" = "goldenrod", "gold" = "goldenrod", "red" = "darkolivegreen", "brown" = "darkolivegreen",\
- "green" = "darkslategrey", "cyan" = "steelblue", "purple" = "darkslategrey", "navy" = "midnightblue")
-
-#define LIST_TAJ_REPLACE list("red" = "rebeccapurple", "brown" = "rebeccapurple", "purple" = "darkslateblue", "blue" = "darkslateblue",\
- "green" = "darkolivegreen", "orange" = "darkkhaki", "gold" = "darkkhaki", "cyan" = "darkseagreen", \
- "navy" = "midnightblue", "pink" = "lightgrey")
+/// Red colorblindness. Vulpkanins/Wolpins have this.
+#define PROTANOPIA_COLOR_REPLACE \
+ list( \
+ "red" = "darkolivegreen", \
+ "green" = "darkslategrey", \
+ "orange" = "goldenrod", \
+ "gold" = "goldenrod", \
+ "brown" = "darkolivegreen", \
+ "cyan" = "steelblue", \
+ "magenta" = "blue", \
+ "purple" = "darkslategrey", \
+ "pink" = "beige" \
+ )
+/// Yellow-Blue colorblindness. Tajarans/Farwas have this.
+#define TRITANOPIA_COLOR_REPLACE \
+ list( \
+ "red" = "rebeccapurple", \
+ "blue" = "darkslateblue", \
+ "green" = "darkolivegreen", \
+ "orange" = "darkkhaki", \
+ "gold" = "darkkhaki", \
+ "brown" = "rebeccapurple", \
+ "cyan" = "darkseagreen", \
+ "magenta" = "darkslateblue", \
+ "purple" = "darkslateblue", \
+ "pink" = "lightgrey" \
+ )
//Gun trigger guards
#define TRIGGER_GUARD_ALLOW_ALL -1
#define TRIGGER_GUARD_NONE 0
#define TRIGGER_GUARD_NORMAL 1
-#define CLIENT_FROM_VAR(I) (ismob(I) ? I:client : (istype(I, /client) ? I : (istype(I, /datum/mind) ? I:current?:client : null)))
-
// Macro to get the current elapsed round time, rather than total world runtime
#define ROUND_TIME (SSticker.round_start_time ? (world.time - SSticker.round_start_time) : 0)
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index c15dfd78b0f..3fcb9cc3410 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -12,6 +12,7 @@
#define CHANNEL_HIGHEST_AVAILABLE 1017
+#define MAX_INSTRUMENT_CHANNELS (128 * 6)
#define SOUND_MINIMUM_PRESSURE 10
#define FALLOFF_SOUNDS 0.5
diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm
index c9763e4836c..9c115bed86f 100644
--- a/code/__DEFINES/stat.dm
+++ b/code/__DEFINES/stat.dm
@@ -22,16 +22,9 @@
// these define the time taken for the shuttle to get to SS13
// and the time before it leaves again
-#define SHUTTLE_PREPTIME 300 // 5 minutes = 300 seconds - after this time, the shuttle departs centcom and cannot be recalled
-#define SHUTTLE_LEAVETIME 180 // 3 minutes = 180 seconds - the duration for which the shuttle will wait at the station after arriving
-#define SHUTTLE_TRANSIT_DURATION 300 // 5 minutes = 300 seconds - how long it takes for the shuttle to get to the station
-#define SHUTTLE_TRANSIT_DURATION_RETURN 120 // 2 minutes = 120 seconds - for some reason it takes less time to come back, go figure.
-
-//Ferry shuttle processing status
-#define IDLE_STATE 0
-#define WAIT_LAUNCH 1
-#define WAIT_ARRIVE 2
-#define WAIT_FINISH 3
+#define SHUTTLE_CALLTIME 6000 //10 minutes = 6000 deciseconds - time taken for emergency shuttle to reach the station when called (in deciseconds)
+#define SHUTTLE_DOCKTIME 1800 //3 minutes = 1800 deciseconds - time taken for emergency shuttle to leave again once it has docked (in deciseconds)
+#define SHUTTLE_ESCAPETIME 1200 //2 minutes = 1200 deciseconds - time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
//shuttle mode defines
#define SHUTTLE_IGNITING 0
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 153eca7f373..6e3603f83a3 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -45,11 +45,13 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_TITLE 100 // This **MUST** load first or people will se blank lobby screens
-#define INIT_ORDER_GARBAGE 19
-#define INIT_ORDER_DBCORE 18
-#define INIT_ORDER_BLACKBOX 17
-#define INIT_ORDER_SERVER_MAINT 16
-#define INIT_ORDER_INPUT 15
+#define INIT_ORDER_GARBAGE 21
+#define INIT_ORDER_DBCORE 20
+#define INIT_ORDER_BLACKBOX 19
+#define INIT_ORDER_SERVER_MAINT 18
+#define INIT_ORDER_INPUT 17
+#define INIT_ORDER_SOUNDS 16
+#define INIT_ORDER_INSTRUMENTS 15
#define INIT_ORDER_RESEARCH 14
#define INIT_ORDER_EVENTS 13
#define INIT_ORDER_JOBS 12
@@ -80,8 +82,7 @@
#define INIT_ORDER_NANOMOB -23
#define INIT_ORDER_SQUEAK -40
#define INIT_ORDER_PATH -50
-#define INIT_ORDER_PERSISTENCE -95
-#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init.
+#define INIT_ORDER_PERSISTENCE -95
// Subsystem fire priority, from lowest to highest priority
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
@@ -113,7 +114,6 @@
#define FIRE_PRIORITY_MOBS 100
#define FIRE_PRIORITY_NANOUI 110
#define FIRE_PRIORITY_TICKER 200
-#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_OVERLAYS 500
#define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost.
diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm
new file mode 100644
index 00000000000..f7ea1c3cf38
--- /dev/null
+++ b/code/__DEFINES/wires.dm
@@ -0,0 +1,77 @@
+// Wire defines for all machines/items.
+
+// Miscellaneous
+#define WIRE_DUD_PREFIX "__dud"
+
+// General
+#define WIRE_IDSCAN "ID Scan"
+#define WIRE_MAIN_POWER1 "Primary Power"
+#define WIRE_MAIN_POWER2 "Secondary Power"
+#define WIRE_AI_CONTROL "AI Control"
+#define WIRE_ELECTRIFY "Electrification"
+#define WIRE_SAFETY "Safety"
+
+// Vendors and smartfridges
+#define WIRE_THROW_ITEM "Item Throw"
+#define WIRE_CONTRABAND "Contraband"
+
+// Airlock
+#define WIRE_DOOR_BOLTS "Door Bolts"
+#define WIRE_BACKUP_POWER1 "Primary Backup Power"
+#define WIRE_OPEN_DOOR "Door State"
+#define WIRE_SPEED "Door Timing"
+#define WIRE_BOLT_LIGHT "Bolt Lights"
+
+// Air alarm
+#define WIRE_SYPHON "Siphon"
+#define WIRE_AALARM "Atmospherics Alarm"
+
+// Camera
+#define WIRE_FOCUS "Focus"
+
+// Mulebot
+#define WIRE_MOB_AVOIDANCE "Mob Avoidance"
+#define WIRE_LOADCHECK "Load Checking"
+#define WIRE_MOTOR1 "Primary Motor"
+#define WIRE_MOTOR2 "Secondary Motor"
+#define WIRE_REMOTE_RX "Signal Receiver"
+#define WIRE_REMOTE_TX "Signal Sender"
+#define WIRE_BEACON_RX "Beacon Receiver"
+
+// Explosives, bombs
+#define WIRE_EXPLODE "Explode" // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut.
+#define WIRE_BOMB_UNBOLT "Unbolt" // Unbolts the bomb if cut, hint on pulsed.
+#define WIRE_BOMB_DELAY "Delay" // Raises the timer on pulse, does nothing on cut.
+#define WIRE_BOMB_PROCEED "Proceed" // Lowers the timer, explodes if cut while the bomb is active.
+#define WIRE_BOMB_ACTIVATE "Activate" // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut.
+
+// Nuclear bomb
+#define WIRE_BOMB_LIGHT "Bomb Light"
+#define WIRE_BOMB_TIMING "Bomb Timing"
+#define WIRE_BOMB_SAFETY "Bomb Safety"
+
+// Particle accelerator
+#define WIRE_PARTICLE_POWER "Power Toggle" // Toggles whether the PA is on or not.
+#define WIRE_PARTICLE_STRENGTH "Strength" // Determines the strength of the PA.
+#define WIRE_PARTICLE_INTERFACE "Interface" // Determines the interface showing up.
+#define WIRE_PARTICLE_POWER_LIMIT "Maximum Power" // Determines how strong the PA can be.
+
+// Autolathe
+#define WIRE_AUTOLATHE_HACK "Hack"
+#define WIRE_AUTOLATHE_DISABLE "Disable"
+
+// Radio
+#define WIRE_RADIO_SIGNAL "Signal"
+#define WIRE_RADIO_RECEIVER "Receiver"
+#define WIRE_RADIO_TRANSMIT "Transmitter"
+
+// Cyborg
+#define WIRE_BORG_LOCKED "Lockdown"
+#define WIRE_BORG_CAMERA "Camera"
+#define WIRE_BORG_LAWCHECK "Law Check"
+
+// Suit storage unit
+#define WIRE_SSU_UV "UV wire"
+
+// Tesla coil
+#define WIRE_TESLACOIL_ZAP "Zap"
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 181d2a5140c..829c2c4fade 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -5,11 +5,11 @@
var/turf/T = get_turf(A)
return T ? T.loc : null
-/proc/get_area_name(N) //get area by its name
- for(var/area/A in world)
- if(A.name == N)
- return A
- return 0
+/proc/get_area_name(atom/X, format_text = FALSE)
+ var/area/A = isarea(X) ? X : get_area(X)
+ if(!A)
+ return null
+ return format_text ? format_text(A.name) : A.name
/proc/get_location_name(atom/X, format_text = FALSE)
var/area/A = isarea(X) ? X : get_area(X)
@@ -31,6 +31,24 @@
areas |= T.loc
return areas
+/proc/get_open_turf_in_dir(atom/center, dir)
+ var/turf/T = get_ranged_target_turf(center, dir, 1)
+ if(T && !T.density)
+ return T
+
+/proc/get_adjacent_open_turfs(atom/center)
+ . = list(get_open_turf_in_dir(center, NORTH),
+ get_open_turf_in_dir(center, SOUTH),
+ get_open_turf_in_dir(center, EAST),
+ get_open_turf_in_dir(center, WEST))
+ listclearnulls(.)
+
+/proc/get_adjacent_open_areas(atom/center)
+ . = list()
+ var/list/adjacent_turfs = get_adjacent_open_turfs(center)
+ for(var/I in adjacent_turfs)
+ . |= get_area(I)
+
// Like view but bypasses luminosity check
/proc/hear(var/range, var/atom/source)
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index c1cf5d53520..075f3370965 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -672,9 +672,6 @@ proc/dd_sortedObjectList(list/incoming)
/obj/machinery/camera/dd_SortValue()
return "[c_tag]"
-/datum/alarm/dd_SortValue()
- return "[sanitize(last_name)]"
-
//Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 47650177283..034e8851c18 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -615,5 +615,3 @@ proc/checkhtml(var/t)
text = replacetext(text, "
", "\[cell\]")
text = replacetext(text, " ", "\[logo\]")
return text
-
-#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, ""))
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 21af95cab48..ab3bfa85451 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -47,7 +47,10 @@ GLOBAL_LIST_EMPTY(ladders)
GLOBAL_LIST_INIT(active_diseases, list()) //List of Active disease in all mobs; purely for quick referencing.
GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects
-
+GLOBAL_LIST_EMPTY(alert_consoles) // Station alert consoles, /obj/machinery/computer/station_alert
GLOBAL_LIST_EMPTY(explosive_walls)
GLOBAL_LIST_EMPTY(engine_beacon_list)
+
+/// List of wire colors for each object type of that round. One for airlocks, one for vendors, etc.
+GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the `holder_type` as the key, and a list of colors as the value.
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index ada0ce8cd30..856f724f925 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -64,7 +64,7 @@
/obj/screen/ai/alerts/Click()
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
- AI.subsystem_alarm_monitor()
+ AI.ai_alerts()
/obj/screen/ai/announcement
name = "Make Announcement"
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index a291120fb85..67df5eb3013 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -81,6 +81,9 @@
/obj/screen/ghost/respawn_pai/Click()
var/mob/dead/observer/G = usr
+ if(!GLOB.paiController.check_recruit(G))
+ to_chat(G, "You are not eligible to become a pAI.")
+ return
GLOB.paiController.recruitWindow(G)
/datum/hud/ghost
diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm
index 289adf6de40..3d91d763be3 100644
--- a/code/controllers/subsystem/alarm.dm
+++ b/code/controllers/subsystem/alarm.dm
@@ -1,31 +1,31 @@
-SUBSYSTEM_DEF(alarms)
- name = "Alarms"
- init_order = INIT_ORDER_ALARMS // 2
- offline_implications = "Alarms (Power, camera, fire, etc) will no longer be checked. No immediate action is needed."
- var/datum/alarm_handler/atmosphere/atmosphere_alarm = new()
- var/datum/alarm_handler/burglar/burglar_alarm = new()
- var/datum/alarm_handler/camera/camera_alarm = new()
- var/datum/alarm_handler/fire/fire_alarm = new()
- var/datum/alarm_handler/motion/motion_alarm = new()
- var/datum/alarm_handler/power/power_alarm = new()
- var/list/datum/alarm/all_handlers
+SUBSYSTEM_DEF(alarm)
+ name = "Alarm"
+ flags = SS_NO_INIT | SS_NO_FIRE
+ var/list/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list(), "Burglar" = list())
-/datum/controller/subsystem/alarms/Initialize(start_timeofday)
- all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
- return ..()
+/datum/controller/subsystem/alarm/proc/triggerAlarm(class, area/A, list/O, obj/alarmsource)
+ var/list/L = alarms[class]
+ for(var/I in L)
+ if(I == A.name)
+ var/list/alarm = L[I]
+ var/list/sources = alarm[3]
+ if(!(alarmsource.UID() in sources))
+ sources += alarmsource.UID()
+ return TRUE
+ L[A.name] = list(get_area_name(A, TRUE), O, list(alarmsource.UID()))
+ SEND_SIGNAL(SSalarm, COMSIG_TRIGGERED_ALARM, class, A, O, alarmsource)
+ return TRUE
-/datum/controller/subsystem/alarms/fire()
- for(var/datum/alarm_handler/AH in all_handlers)
- AH.process()
+/datum/controller/subsystem/alarm/proc/cancelAlarm(class, area/A, obj/origin)
+ var/list/L = alarms[class]
+ var/cleared = FALSE
+ for(var/I in L)
+ if(I == A.name)
+ var/list/alarm = L[I]
+ var/list/srcs = alarm[3]
+ srcs -= origin.UID()
+ if(!length(srcs))
+ cleared = TRUE
+ L -= I
-/datum/controller/subsystem/alarms/proc/active_alarms()
- var/list/all_alarms = new ()
- for(var/datum/alarm_handler/AH in all_handlers)
- var/list/alarms = AH.alarms
- all_alarms += alarms
-
- return all_alarms
-
-/datum/controller/subsystem/alarms/proc/number_of_active_alarms()
- var/list/alarms = active_alarms()
- return alarms.len
+ SEND_SIGNAL(SSalarm, COMSIG_CANCELLED_ALARM, class, A, origin, cleared)
diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm
deleted file mode 100644
index 4eb468a0952..00000000000
--- a/code/controllers/subsystem/chat.dm
+++ /dev/null
@@ -1,67 +0,0 @@
-SUBSYSTEM_DEF(chat)
- name = "Chat"
- flags = SS_TICKER|SS_NO_INIT
- wait = 1
- priority = FIRE_PRIORITY_CHAT
- init_order = INIT_ORDER_CHAT
- offline_implications = "Chat messages will no longer be cleanly queued. No immediate action is needed."
-
- var/list/payload = list()
-
-
-/datum/controller/subsystem/chat/fire()
- for(var/i in payload)
- var/client/C = i
- if(C)
- C << output(payload[C], "browseroutput:output")
- payload -= C
-
- if(MC_TICK_CHECK)
- return
-
-
-/datum/controller/subsystem/chat/proc/queue(target, message, flag)
- if(!target || !message)
- return
-
- if(!istext(message))
- stack_trace("to_chat called with invalid input type")
- return
-
- if(target == world)
- target = GLOB.clients
-
- //Some macros remain in the string even after parsing and fuck up the eventual output
- message = replacetext(message, "\improper", "")
- message = replacetext(message, "\proper", "")
- message += " "
-
-
- //url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
- //Do the double-encoding here to save nanoseconds
- var/twiceEncoded = url_encode(url_encode(message))
-
- if(islist(target))
- for(var/I in target)
- var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
-
- if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
- continue
-
- if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
- C.chatOutput.messageQueue += message
- continue
-
- payload[C] += twiceEncoded
-
- else
- var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
-
- if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
- return
-
- if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
- C.chatOutput.messageQueue += message
- return
-
- payload[C] += twiceEncoded
diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm
new file mode 100644
index 00000000000..3d571d2a13d
--- /dev/null
+++ b/code/controllers/subsystem/processing/instruments.dm
@@ -0,0 +1,86 @@
+PROCESSING_SUBSYSTEM_DEF(instruments)
+ name = "Instruments"
+ init_order = INIT_ORDER_INSTRUMENTS
+ wait = 1
+ flags = SS_TICKER|SS_BACKGROUND|SS_KEEP_TIMING
+ offline_implications = "Instruments will no longer play. No immediate action is needed."
+
+ /// List of all instrument data, associative id = datum
+ var/list/datum/instrument/instrument_data
+ /// List of all song datums.
+ var/list/datum/song/songs
+ /// Max lines in songs
+ var/musician_maxlines = 600
+ /// Max characters per line in songs
+ var/musician_maxlinechars = 300
+ /// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this.
+ var/musician_hearcheck_mindelay = 5
+ /// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels.
+ var/max_instrument_channels = MAX_INSTRUMENT_CHANNELS
+ /// Current number of channels allocated for instruments
+ var/current_instrument_channels = 0
+ /// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer.
+ var/list/synthesizer_instrument_ids
+
+/datum/controller/subsystem/processing/instruments/Initialize()
+ initialize_instrument_data()
+ synthesizer_instrument_ids = get_allowed_instrument_ids()
+ return ..()
+
+/**
+ * Initializes all instrument datums
+ */
+/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data()
+ instrument_data = list()
+ for(var/path in subtypesof(/datum/instrument))
+ var/datum/instrument/I = path
+ if(initial(I.abstract_type) == path)
+ continue
+ I = new path
+ I.Initialize()
+ if(!I.id)
+ qdel(I)
+ continue
+ else
+ instrument_data[I.id] = I
+ CHECK_TICK
+
+/**
+ * Reserves a sound channel for a given instrument datum
+ *
+ * Arguments:
+ * * I - The instrument datum
+ */
+/datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I)
+ if(current_instrument_channels > max_instrument_channels)
+ return
+ . = SSsounds.reserve_sound_channel(I)
+ if(!isnull(.))
+ current_instrument_channels++
+
+/**
+ * Called when a datum/song is created
+ *
+ * Arguments:
+ * * S - The created datum/song
+ */
+/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S)
+ LAZYADD(songs, S)
+
+/**
+ * Called when a datum/song is deleted
+ *
+ * Arguments:
+ * * S - The deleted datum/song
+ */
+/datum/controller/subsystem/processing/instruments/proc/on_song_del(datum/song/S)
+ LAZYREMOVE(songs, S)
+
+/**
+ * Returns the instrument datum at the given ID or path
+ *
+ * Arguments:
+ * * id_or_path - The ID or path of the instrument
+ */
+/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path)
+ return instrument_data["[id_or_path]"]
diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm
index 247d71d05b8..08fa7709320 100644
--- a/code/controllers/subsystem/shuttles.dm
+++ b/code/controllers/subsystem/shuttles.dm
@@ -14,9 +14,9 @@ SUBSYSTEM_DEF(shuttle)
//emergency shuttle stuff
var/obj/docking_port/mobile/emergency/emergency
var/obj/docking_port/mobile/emergency/backup/backup_shuttle
- var/emergencyCallTime = 6000 //time taken for emergency shuttle to reach the station when called (in deciseconds)
- var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
- var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
+ var/emergencyCallTime = SHUTTLE_CALLTIME //time taken for emergency shuttle to reach the station when called (in deciseconds)
+ var/emergencyDockTime = SHUTTLE_DOCKTIME //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
+ var/emergencyEscapeTime = SHUTTLE_ESCAPETIME //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
var/emergency_sec_level_time = 0 // time sec level was last raised to red or higher
var/area/emergencyLastCallLoc
var/emergencyNoEscape
diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm
new file mode 100644
index 00000000000..33d97fcfe04
--- /dev/null
+++ b/code/controllers/subsystem/sounds.dm
@@ -0,0 +1,165 @@
+#define DATUMLESS "NO_DATUM"
+
+SUBSYSTEM_DEF(sounds)
+ name = "Sounds"
+ init_order = INIT_ORDER_SOUNDS
+ flags = SS_NO_FIRE
+ offline_implications = "Sounds may not play correctly. Shuttle call recommended."
+
+ var/using_channels_max = CHANNEL_HIGHEST_AVAILABLE // BYOND max channels
+ /// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
+ var/random_channels_min = 50
+ // Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing.
+ /// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel
+ var/list/using_channels
+ /// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved.
+ var/list/using_channels_by_datum
+ // Special datastructure for fast channel management
+ /// List of all channels as numbers
+ var/list/channel_list
+ /// Associative list of all reserved channels associated to their position. "[channel_number]" = index as number
+ var/list/reserved_channels
+ /// lower iteration position - Incremented and looped to get "random" sound channels for normal sounds. The channel at this index is returned when asking for a random channel.
+ var/channel_random_low
+ /// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel.
+ var/channel_reserve_high
+
+/datum/controller/subsystem/sounds/Initialize()
+ setup_available_channels()
+ return ..()
+
+/**
+ * Sets up all available sound channels
+ */
+/datum/controller/subsystem/sounds/proc/setup_available_channels()
+ channel_list = list()
+ reserved_channels = list()
+ using_channels = list()
+ using_channels_by_datum = list()
+ for(var/i in 1 to using_channels_max)
+ channel_list += i
+ channel_random_low = 1
+ channel_reserve_high = length(channel_list)
+
+/**
+ * Removes a channel from using list
+ *
+ * Arguments:
+ * * channel - The channel number
+ */
+/datum/controller/subsystem/sounds/proc/free_sound_channel(channel)
+ var/text_channel = num2text(channel)
+ var/using = using_channels[text_channel]
+ using_channels -= text_channel
+ if(!using) // datum channel
+ using_channels_by_datum[using] -= channel
+ if(!length(using_channels_by_datum[using]))
+ using_channels_by_datum -= using
+ free_channel(channel)
+
+/**
+ * Frees all the channels a datum is using
+ *
+ * Arguments:
+ * * D - The datum
+ */
+/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D)
+ var/list/L = using_channels_by_datum[D]
+ if(!L)
+ return
+ for(var/channel in L)
+ using_channels -= num2text(channel)
+ free_channel(channel)
+ using_channels_by_datum -= D
+
+/**
+ * Frees all datumless channels
+ */
+/datum/controller/subsystem/sounds/proc/free_datumless_channels()
+ free_datum_channels(DATUMLESS)
+
+/**
+ * NO AUTOMATIC CLEANUP - If you use this, you better manually free it later!
+ *
+ * Returns an integer for channel
+ */
+/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
+ . = reserve_channel()
+ if(!.) // oh no..
+ return FALSE
+ var/text_channel = num2text(.)
+ using_channels[text_channel] = DATUMLESS
+ LAZYADD(using_channels_by_datum[DATUMLESS], .)
+
+/**
+ * Reserves a channel for a datum. Automatic cleanup only when the datum is deleted.
+ *
+ * Returns an integer for channel
+ * Arguments:
+ * * D - The datum
+ */
+/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
+ if(!D) // i don't like typechecks but someone will fuck it up
+ CRASH("Attempted to reserve sound channel without datum using the managed proc.")
+ . = reserve_channel()
+ if(!.)
+ return FALSE
+ var/text_channel = num2text(.)
+ using_channels[text_channel] = D
+ LAZYADD(using_channels_by_datum[D], .)
+
+/**
+ * Reserves a channel and updates the datastructure. Private proc.
+ */
+/datum/controller/subsystem/sounds/proc/reserve_channel()
+ PRIVATE_PROC(TRUE)
+ if(channel_reserve_high <= random_channels_min) // out of channels
+ return
+ var/channel = channel_list[channel_reserve_high]
+ reserved_channels[num2text(channel)] = channel_reserve_high--
+ return channel
+
+/**
+ * Frees a channel and updates the datastructure. Private proc.
+ */
+/datum/controller/subsystem/sounds/proc/free_channel(number)
+ PRIVATE_PROC(TRUE)
+ var/text_channel = num2text(number)
+ var/index = reserved_channels[text_channel]
+ if(!index)
+ CRASH("Attempted to (internally) free a channel that wasn't reserved.")
+ reserved_channels -= text_channel
+ // push reserve index up, which makes it now on a channel that is reserved
+ channel_reserve_high++
+ // swap the reserved channel with the unreserved channel so the reserve index is now on an unoccupied channel and the freed channel is next to be used.
+ channel_list.Swap(channel_reserve_high, index)
+ // now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
+ // get it, and update position.
+ var/text_reserved = num2text(channel_list[index])
+ if(!reserved_channels[text_reserved]) // if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
+ return
+ reserved_channels[text_reserved] = index
+
+/**
+ * Random available channel, returns text
+ */
+/datum/controller/subsystem/sounds/proc/random_available_channel_text()
+ if(channel_random_low > channel_reserve_high)
+ channel_random_low = 1
+ . = "[channel_list[channel_random_low++]]"
+
+/**
+ * Random available channel, returns number
+ */
+/datum/controller/subsystem/sounds/proc/random_available_channel()
+ if(channel_random_low > channel_reserve_high)
+ channel_random_low = 1
+ . = channel_list[channel_random_low++]
+
+/**
+ * How many channels we have left
+ */
+/datum/controller/subsystem/sounds/proc/available_channels_left()
+ return length(channel_list) - random_channels_min
+
+#undef DATUMLESS
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index abb0b604069..8d02529ae6b 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -20,7 +20,7 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI",
- "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires",
+ "Cameras", "Garbage", "Event", "Nano", "Vote", "Fires",
"Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input"))
set category = "Debug"
set name = "Debug Controller"
@@ -65,9 +65,6 @@
if("Event")
debug_variables(SSevents)
feedback_add_details("admin_verb","DEvent")
- if("Alarm")
- debug_variables(SSalarms)
- feedback_add_details("admin_verb", "DAlarm")
if("Nano")
debug_variables(SSnanoui)
feedback_add_details("admin_verb","DNano")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index c1802da47c1..11ab496d386 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -190,9 +190,6 @@
/datum/action/item_action/toggle_mister
name = "Toggle Mister"
-/datum/action/item_action/toggle_headphones
- name = "Toggle Headphones"
-
/datum/action/item_action/toggle_helmet_light
name = "Toggle Helmet Light"
@@ -232,19 +229,6 @@
button.name = name
..()
-/datum/action/item_action/synthswitch
- name = "Change Synthesizer Instrument"
- desc = "Change the type of instrument your synthesizer is playing as."
-
-/datum/action/item_action/synthswitch/Trigger()
- if(istype(target, /obj/item/instrument/piano_synth))
- var/obj/item/instrument/piano_synth/synth = target
- var/chosen = input("Choose the type of instrument you want to use", "Instrument Selection", "piano") as null|anything in synth.insTypes
- if(!synth.insTypes[chosen])
- return
- return synth.changeInstrument(chosen)
- return ..()
-
/datum/action/item_action/vortex_recall
name = "Vortex Recall"
desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time. If the beacon is still attached, will detach it."
@@ -257,6 +241,9 @@
return 0
return ..()
+/datum/action/item_action/change_headphones_song
+ name = "Change Headphones Song"
+
/datum/action/item_action/toggle
/datum/action/item_action/toggle/New(Target)
diff --git a/code/datums/components/spooky.dm b/code/datums/components/spooky.dm
new file mode 100644
index 00000000000..f5ee9c94666
--- /dev/null
+++ b/code/datums/components/spooky.dm
@@ -0,0 +1,58 @@
+/datum/component/spooky
+ var/too_spooky = TRUE //will it spawn a new instrument?
+
+/datum/component/spooky/Initialize()
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/spectral_attack)
+
+/datum/component/spooky/proc/spectral_attack(datum/source, mob/living/carbon/C, mob/user)
+ if(ishuman(user)) //this weapon wasn't meant for mortals.
+ var/mob/living/carbon/human/U = user
+ if(!istype(U.dna.species, /datum/species/skeleton))
+ U.adjustStaminaLoss(35) //Extra Damage
+ U.Jitter(35)
+ U.stuttering = 20
+ if(U.getStaminaLoss() > 95)
+ to_chat(U, "Your ears weren't meant for this spectral sound.")
+ spectral_change(U)
+ return
+
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ if(istype(H.dna.species, /datum/species/skeleton))
+ return //undeads are unaffected by the spook-pocalypse.
+ C.Jitter(35)
+ C.stuttering = 20
+ if(!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman))
+ C.adjustStaminaLoss(25) //boneless humanoids don't lose the will to live
+ to_chat(C, "DOOT")
+ spectral_change(H)
+
+ else //the sound will spook monkeys.
+ C.Jitter(15)
+ C.stuttering = 20
+
+/datum/component/spooky/proc/spectral_change(mob/living/carbon/human/H, mob/user)
+ if((H.getStaminaLoss() > 95) && (!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman) && !istype(H.dna.species, /datum/species/skeleton)))
+ H.Stun(20)
+ H.set_species(/datum/species/skeleton)
+ H.visible_message("[H] has given up on life as a mortal.")
+ var/T = get_turf(H)
+ if(too_spooky)
+ if(prob(30))
+ new/obj/item/instrument/saxophone/spectral(T)
+ else if(prob(30))
+ new/obj/item/instrument/trumpet/spectral(T)
+ else if(prob(30))
+ new/obj/item/instrument/trombone/spectral(T)
+ else
+ to_chat(H, "The spooky gods forgot to ship your instrument. Better luck next unlife.")
+ to_chat(H, "You are the spooky skeleton!")
+ to_chat(H, "A new life and identity has begun. Help your fellow skeletons into bringing out the spooky-pocalypse. You haven't forgotten your past life, and are still beholden to past loyalties.")
+ change_name(H) //time for a new name!
+
+/datum/component/spooky/proc/change_name(mob/living/carbon/human/H)
+ var/t = stripped_input(H, "Enter your new skeleton name", H.real_name, null, MAX_NAME_LEN)
+ if(!t)
+ t = "spooky skeleton"
+ H.real_name = t
+ H.name = t
diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm
index f44a87bdd7a..006e92c305c 100644
--- a/code/datums/looping_sounds/looping_sound.dm
+++ b/code/datums/looping_sounds/looping_sound.dm
@@ -71,7 +71,7 @@
var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
if(direct)
- S.channel = open_sound_channel()
+ S.channel = SSsounds.random_available_channel()
S.volume = volume
for(var/i in 1 to atoms_cache.len)
var/atom/thing = atoms_cache[i]
diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm
index 799a12f4db9..3541be89983 100644
--- a/code/datums/ruins/space.dm
+++ b/code/datums/ruins/space.dm
@@ -248,3 +248,29 @@
allow_duplicates = FALSE // I dont even want to think about what happens if you have 2 shuttles with the same ID. Likely scary stuff.
always_place = TRUE // Its designed to make exploring other space ruins more accessible
cost = 0 // Force spawned so shouldnt have a cost
+
+/datum/map_template/ruin/space/syndiecakesfactory
+ id = "Syndiecakes Factory"
+ suffix = "syndiecakesfactory.dmm"
+ name = "Syndicakes Factory"
+ description = "Syndicate used to get funds selling corgi cakes produced here. Was it hit by meteors or by a Nanotrasen comando?"
+ allow_duplicates = FALSE
+ cost = 2 //telecomms + multiple mobs
+
+/datum/map_template/ruin/space/debris1
+ id = "debris1"
+ suffix = "debris1.dmm"
+ name = "Debris field 1"
+ description = "A bunch of metal chunks, wires and space waste"
+
+/datum/map_template/ruin/space/debris2
+ id = "debris2"
+ suffix = "debris2.dmm"
+ name = "Debris field 2"
+ description = "A bunch of metal chunks, wires and space waste that used to be some kind of secure storage facility"
+
+/datum/map_template/ruin/space/debris3
+ id = "debris3"
+ suffix = "debris3.dmm"
+ name = "Debris field 3"
+ description = "A bunch of metal chunks, wires and space waste. It used to be an arcade."
diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm
index 4d6310041e5..dacc08dee31 100644
--- a/code/datums/shuttles.dm
+++ b/code/datums/shuttles.dm
@@ -133,3 +133,8 @@
suffix = "admin"
name = "NTV Argos"
description = "Default Admin ship. An older ship used for special operations."
+
+/datum/map_template/shuttle/admin/armory
+ suffix = "armory"
+ name = "NRV Sparta"
+ description = "Armory Shuttle, with plenty of guns to hand out and some general supplies."
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index 4814e27380b..3562cfd9d80 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -736,6 +736,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 12 // normally 18
gamemodes = list(/datum/game_mode/nuclear)
+/datum/uplink_item/ammo/bulldog_XLmagsbag
+ name = "Bulldog - 12g XL Magazine Duffel Bag"
+ desc = "A duffel bag containing three 16 round drum magazines(Slug, Buckshot, Dragon's Breath)."
+ reference = "12XLDB"
+ item = /obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags
+ cost = 12 // normally 18
+ gamemodes = list(/datum/game_mode/nuclear)
+
/datum/uplink_item/ammo/smg
name = "C-20r - .45 Magazine"
desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage."
@@ -1725,8 +1733,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
U.purchase_log += "[bicon(C)]"
for(var/item in bought_items)
- new item(C)
- U.purchase_log += "[bicon(item)]"
+ var/obj/purchased = new item(C)
+ U.purchase_log += "[bicon(purchased)]"
log_game("[key_name(usr)] purchased a surplus crate with [jointext(itemlog, ", ")]")
/datum/uplink_item/bundles_TC/telecrystal
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index dcde08d7ec1..ff7a0169c15 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -1,67 +1,29 @@
// Wires for airlocks
/datum/wires/airlock/secure
- random = 1
+ randomize = TRUE
/datum/wires/airlock
holder_type = /obj/machinery/door/airlock
- wire_count = 12
- window_x = 410
- window_y = 570
+ wire_count = 12 // 10 actual, 2 duds.
+ proper_name = "Airlock"
+ window_x = 400
+ window_y = 101
-#define AIRLOCK_WIRE_IDSCAN 1
-#define AIRLOCK_WIRE_MAIN_POWER1 2
-#define AIRLOCK_WIRE_DOOR_BOLTS 4
-#define AIRLOCK_WIRE_BACKUP_POWER1 8
-#define AIRLOCK_WIRE_OPEN_DOOR 16
-#define AIRLOCK_WIRE_AI_CONTROL 32
-#define AIRLOCK_WIRE_ELECTRIFY 64
-#define AIRLOCK_WIRE_SAFETY 128
-#define AIRLOCK_WIRE_SPEED 256
-#define AIRLOCK_WIRE_LIGHT 512
+/datum/wires/airlock/New(atom/_holder)
+ wires = list(
+ WIRE_IDSCAN, WIRE_MAIN_POWER1, WIRE_DOOR_BOLTS, WIRE_BACKUP_POWER1, WIRE_OPEN_DOOR,
+ WIRE_AI_CONTROL, WIRE_ELECTRIFY, WIRE_SAFETY, WIRE_SPEED, WIRE_BOLT_LIGHT
+ )
+ return ..()
-/datum/wires/airlock/GetWireName(index)
- switch(index)
- if(AIRLOCK_WIRE_IDSCAN)
- return "ID Scan"
-
- if(AIRLOCK_WIRE_MAIN_POWER1)
- return "Primary Power"
-
- if(AIRLOCK_WIRE_DOOR_BOLTS)
- return "Door Bolts"
-
- if(AIRLOCK_WIRE_BACKUP_POWER1)
- return "Primary Backup Power"
-
- if(AIRLOCK_WIRE_OPEN_DOOR)
- return "Door State"
-
- if(AIRLOCK_WIRE_AI_CONTROL)
- return "AI Control"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Electrification"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Door Safeties"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Door Timing"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Bolt Lights"
-
-/datum/wires/airlock/CanUse(mob/living/L)
+/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
- if(iscarbon(L))
- if(A.Adjacent(L))
- if(A.isElectrified())
- if(A.shock(L, 100))
- return 0
+ if(iscarbon(user) && A.Adjacent(user) && A.isElectrified() && A.shock(user, 100))
+ return FALSE
if(A.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/airlock/get_status()
. = ..()
@@ -70,21 +32,20 @@
. += "The door bolts [A.locked ? "have fallen!" : "look up."]"
. += "The door bolt lights are [(A.lights && haspower) ? "on." : "off!"]"
- . += "The test light is [haspower ? "on." : "off!"]"
- . += "The 'AI control allowed' light is [(A.aiControlDisabled == 0 && !A.emagged && haspower) ? "on" : "off"]."
+ . += "The test light is [haspower ? "on." : "off!"]"
+ . += "The 'AI control allowed' light is [(A.aiControlDisabled == AICONTROLDISABLED_OFF && !A.emagged && haspower) ? "on" : "off"]."
. += "The 'Check Wiring' light is [(A.safe == 0 && haspower) ? "on" : "off"]."
. += "The 'Check Timing Mechanism' light is [(A.normalspeed == 0 && haspower) ? "on" : "off"]."
. += "The emergency lights are [(A.emergency && haspower) ? "on" : "off"]."
-/datum/wires/airlock/UpdateCut(index, mended)
-
+/datum/wires/airlock/on_cut(wire, mend)
var/obj/machinery/door/airlock/A = holder
- switch(index)
- if(AIRLOCK_WIRE_IDSCAN)
- A.aiDisabledIdScanner = !mended
- if(AIRLOCK_WIRE_MAIN_POWER1)
+ switch(wire)
+ if(WIRE_IDSCAN)
+ A.aiDisabledIdScanner = !mend
+ if(WIRE_MAIN_POWER1)
- if(!mended)
+ if(!mend)
//Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be crowbarred open, but bolts-raising will not work. Cutting these wires may electocute the user.
A.loseMainPower()
A.shock(usr, 50)
@@ -92,9 +53,9 @@
A.regainMainPower()
A.shock(usr, 50)
- if(AIRLOCK_WIRE_BACKUP_POWER1)
+ if(WIRE_BACKUP_POWER1)
- if(!mended)
+ if(!mend)
//Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user.
A.loseBackupPower()
A.shock(usr, 50)
@@ -102,66 +63,66 @@
A.regainBackupPower()
A.shock(usr, 50)
- if(AIRLOCK_WIRE_DOOR_BOLTS)
+ if(WIRE_DOOR_BOLTS)
- if(!mended)
+ if(!mend)
//Cutting this wire also drops the door bolts, and mending it does not raise them. (This is what happens now, except there are a lot more wires going to door bolts at present)
A.lock(1)
A.update_icon()
- if(AIRLOCK_WIRE_AI_CONTROL)
+ if(WIRE_AI_CONTROL)
- if(!mended)
+ if(!mend)
//one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all.
- //aiControlDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
- if(A.aiControlDisabled == 0)
- A.aiControlDisabled = 1
- else if(A.aiControlDisabled == -1)
- A.aiControlDisabled = 2
+ //aiControlDisabled: see explanation in code\__DEFINES\construction.dm#32
+ if(A.aiControlDisabled == AICONTROLDISABLED_OFF)
+ A.aiControlDisabled = AICONTROLDISABLED_ON
+ else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA)
+ A.aiControlDisabled = AICONTROLDISABLED_BYPASS
else
- if(A.aiControlDisabled == 1)
- A.aiControlDisabled = 0
- else if(A.aiControlDisabled == 2)
- A.aiControlDisabled = -1
+ if(A.aiControlDisabled == AICONTROLDISABLED_ON)
+ A.aiControlDisabled = AICONTROLDISABLED_OFF
+ else if(A.aiControlDisabled == AICONTROLDISABLED_BYPASS)
+ A.aiControlDisabled = AICONTROLDISABLED_PERMA
- if(AIRLOCK_WIRE_ELECTRIFY)
- if(!mended)
+ if(WIRE_ELECTRIFY)
+ if(!mend)
//Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted.
A.electrify(-1)
else
A.electrify(0)
return // Don't update the dialog.
- if(AIRLOCK_WIRE_SAFETY)
- A.safe = mended
+ if(WIRE_SAFETY)
+ A.safe = mend
- if(AIRLOCK_WIRE_SPEED)
- A.autoclose = mended
- if(mended)
+ if(WIRE_SPEED)
+ A.autoclose = mend
+ if(mend)
if(!A.density)
- spawn(0)
- A.close()
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close)
- if(AIRLOCK_WIRE_LIGHT)
- A.lights = mended
+ if(WIRE_BOLT_LIGHT)
+ A.lights = mend
A.update_icon()
..()
-/datum/wires/airlock/UpdatePulsed(index)
-
+/datum/wires/airlock/on_pulse(wire)
var/obj/machinery/door/airlock/A = holder
- switch(index)
- if(AIRLOCK_WIRE_IDSCAN)
+ switch(wire)
+ if(WIRE_IDSCAN)
//Sending a pulse through flashes the red light on the door (if the door has power).
if(A.arePowerSystemsOn() && A.density)
A.do_animate("deny")
if(A.emergency)
A.emergency = 0
A.update_icon()
- if(AIRLOCK_WIRE_MAIN_POWER1)
+
+ if(WIRE_MAIN_POWER1)
//Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter).
A.loseMainPower()
- if(AIRLOCK_WIRE_DOOR_BOLTS)
+
+ if(WIRE_DOOR_BOLTS)
//one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not),
//raises them if they are down (only if power's on)
if(!A.locked)
@@ -170,45 +131,41 @@
else if(A.unlock())
A.audible_message("You hear a click from the bottom of the door.", hearing_distance = 1)
- if(AIRLOCK_WIRE_BACKUP_POWER1)
+ if(WIRE_BACKUP_POWER1)
//two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter).
A.loseBackupPower()
- if(AIRLOCK_WIRE_AI_CONTROL)
- if(A.aiControlDisabled == 0)
- A.aiControlDisabled = 1
- else if(A.aiControlDisabled == -1)
- A.aiControlDisabled = 2
- spawn(10)
- if(A)
- if(A.aiControlDisabled == 1)
- A.aiControlDisabled = 0
- else if(A.aiControlDisabled == 2)
- A.aiControlDisabled = -1
+ if(WIRE_AI_CONTROL)
+ if(A.aiControlDisabled == AICONTROLDISABLED_OFF)
+ A.aiControlDisabled = AICONTROLDISABLED_ON
+ else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA)
+ A.aiControlDisabled = AICONTROLDISABLED_BYPASS
- if(AIRLOCK_WIRE_ELECTRIFY)
+ addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/ai_control_callback), 1 SECONDS)
+
+ if(WIRE_ELECTRIFY)
//one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds.
A.electrify(30)
- if(AIRLOCK_WIRE_OPEN_DOOR)
+
+ if(WIRE_OPEN_DOOR)
//tries to open the door without ID
//will succeed only if the ID wire is cut or the door requires no access and it's not emagged
if(A.emagged) return
if(!A.requiresID() || A.check_access(null))
- spawn(0)
- if(A.density)
- A.open()
- else
- A.close()
- if(AIRLOCK_WIRE_SAFETY)
+ if(A.density)
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/open)
+ else
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close)
+
+ if(WIRE_SAFETY)
A.safe = !A.safe
if(!A.density)
- spawn(0)
- A.close()
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close)
- if(AIRLOCK_WIRE_SPEED)
+ if(WIRE_SPEED)
A.normalspeed = !A.normalspeed
- if(AIRLOCK_WIRE_LIGHT)
+ if(WIRE_BOLT_LIGHT)
A.lights = !A.lights
A.update_icon()
diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm
index f6b49b0dd99..cd13c9fe1d8 100644
--- a/code/datums/wires/alarm.dm
+++ b/code/datums/wires/alarm.dm
@@ -2,35 +2,22 @@
/datum/wires/alarm
holder_type = /obj/machinery/alarm
wire_count = 5
+ window_x = 385
+ window_y = 90
+ proper_name = "Air alarm"
-#define AALARM_WIRE_IDSCAN 1
-#define AALARM_WIRE_POWER 2
-#define AALARM_WIRE_SYPHON 4
-#define AALARM_WIRE_AI_CONTROL 8
-#define AALARM_WIRE_AALARM 16
+/datum/wires/alarm/New(atom/_holder)
+ wires = list(
+ WIRE_IDSCAN , WIRE_MAIN_POWER1 , WIRE_SYPHON,
+ WIRE_AI_CONTROL, WIRE_AALARM
+ )
+ return ..()
-/datum/wires/alarm/GetWireName(index)
- switch(index)
- if(AALARM_WIRE_IDSCAN)
- return "ID Scan"
-
- if(AALARM_WIRE_POWER)
- return "Power"
-
- if(AALARM_WIRE_SYPHON)
- return "Syphon"
-
- if(AALARM_WIRE_AI_CONTROL)
- return "AI Control"
-
- if(AALARM_WIRE_AALARM)
- return "Atmospherics Alarm"
-
-/datum/wires/alarm/CanUse(mob/living/L)
+/datum/wires/alarm/interactable(mob/user)
var/obj/machinery/alarm/A = holder
if(A.wiresexposed)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/alarm/get_status()
. = ..()
@@ -39,75 +26,60 @@
. += "The Air Alarm is [(A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "offline." : "working properly!"]"
. += "The 'AI control allowed' light is [A.aidisabled ? "off" : "on"]."
-/datum/wires/alarm/UpdateCut(index, mended)
+/datum/wires/alarm/on_cut(wire, mend)
var/obj/machinery/alarm/A = holder
- switch(index)
- if(AALARM_WIRE_IDSCAN)
- if(!mended)
+ switch(wire)
+ if(WIRE_IDSCAN)
+ if(!mend)
A.locked = 1
-// to_chat(world, "Idscan wire cut")
- if(AALARM_WIRE_POWER)
+ if(WIRE_MAIN_POWER1)
A.shock(usr, 50)
- A.shorted = !mended
+ A.shorted = !mend
A.update_icon()
-// to_chat(world, "Power wire cut")
- if(AALARM_WIRE_AI_CONTROL)
- A.aidisabled = !mended
-// to_chat(world, "AI Control Wire Cut")
+ if(WIRE_AI_CONTROL)
+ A.aidisabled = !mend
- if(AALARM_WIRE_SYPHON)
- if(!mended)
+ if(WIRE_SYPHON)
+ if(!mend)
A.mode = 3 // AALARM_MODE_PANIC
A.apply_mode()
-// to_chat(world, "Syphon Wire Cut")
- if(AALARM_WIRE_AALARM)
- if(A.alarm_area.atmosalert(2, A))
- A.post_alert(2)
+ if(WIRE_AALARM)
+ if(A.alarm_area.atmosalert(ATMOS_ALARM_DANGER, A))
+ A.post_alert(ATMOS_ALARM_DANGER)
A.update_icon()
..()
-/datum/wires/alarm/UpdatePulsed(index)
+/datum/wires/alarm/on_pulse(wire)
var/obj/machinery/alarm/A = holder
- switch(index)
- if(AALARM_WIRE_IDSCAN)
+ switch(wire)
+ if(WIRE_IDSCAN)
A.locked = !A.locked
-// to_chat(world, "Idscan wire pulsed")
- if(AALARM_WIRE_POWER)
-// to_chat(world, "Power wire pulsed")
- if(A.shorted == 0)
- A.shorted = 1
+ if(WIRE_MAIN_POWER1)
+ if(!A.shorted)
+ A.shorted = TRUE
A.update_icon()
+ addtimer(CALLBACK(A, /obj/machinery/alarm/.proc/unshort_callback), 120 SECONDS)
- spawn(12000)
- if(A.shorted == 1)
- A.shorted = 0
- A.update_icon()
-
-
- if(AALARM_WIRE_AI_CONTROL)
-// to_chat(world, "AI Control wire pulsed")
- if(A.aidisabled == 0)
- A.aidisabled = 1
+ if(WIRE_AI_CONTROL)
+ if(!A.aidisabled)
+ A.aidisabled = TRUE
A.updateDialog()
- spawn(100)
- if(A.aidisabled == 1)
- A.aidisabled = 0
+ addtimer(CALLBACK(A, /obj/machinery/alarm/.proc/enable_ai_control_callback), 10 SECONDS)
- if(AALARM_WIRE_SYPHON)
-// to_chat(world, "Syphon wire pulsed")
+
+ if(WIRE_SYPHON)
if(A.mode == 1) // AALARM_MODE_SCRUB
A.mode = 3 // AALARM_MODE_PANIC
else
A.mode = 1 // AALARM_MODE_SCRUB
A.apply_mode()
- if(AALARM_WIRE_AALARM)
-// to_chat(world, "Aalarm wire pulsed")
- if(A.alarm_area.atmosalert(0, A))
- A.post_alert(0)
+ if(WIRE_AALARM)
+ if(A.alarm_area.atmosalert(ATMOS_ALARM_NONE, A))
+ A.post_alert(ATMOS_ALARM_NONE)
A.update_icon()
..()
diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm
index b1076c8e29b..49931d8fa31 100644
--- a/code/datums/wires/apc.dm
+++ b/code/datums/wires/apc.dm
@@ -1,25 +1,13 @@
/datum/wires/apc
holder_type = /obj/machinery/power/apc
wire_count = 4
+ proper_name = "APC"
+ window_x = 355
+ window_y = 97
-#define APC_WIRE_IDSCAN 1
-#define APC_WIRE_MAIN_POWER1 2
-#define APC_WIRE_MAIN_POWER2 4
-#define APC_WIRE_AI_CONTROL 8
-
-/datum/wires/apc/GetWireName(index)
- switch(index)
- if(APC_WIRE_IDSCAN)
- return "ID Scan"
-
- if(APC_WIRE_MAIN_POWER1)
- return "Primary Power"
-
- if(APC_WIRE_MAIN_POWER2)
- return "Secondary Power"
-
- if(APC_WIRE_AI_CONTROL)
- return "AI Control"
+/datum/wires/apc/New(atom/_holder)
+ wires = list(WIRE_IDSCAN, WIRE_MAIN_POWER1, WIRE_MAIN_POWER2, WIRE_AI_CONTROL)
+ return ..()
/datum/wires/apc/get_status()
. = ..()
@@ -29,66 +17,53 @@
. += "The 'AI control allowed' light is [A.aidisabled ? "off" : "on"]."
-/datum/wires/apc/CanUse(mob/living/L)
+/datum/wires/apc/interactable(mob/user)
var/obj/machinery/power/apc/A = holder
if(A.panel_open && !A.opened)
return TRUE
return FALSE
-/datum/wires/apc/UpdatePulsed(index)
+/datum/wires/apc/on_pulse(wire)
var/obj/machinery/power/apc/A = holder
- switch(index)
+ switch(wire)
+ if(WIRE_IDSCAN)
+ A.locked = FALSE
+ addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/relock_callback), 30 SECONDS)
- if(APC_WIRE_IDSCAN)
- A.locked = 0
- spawn(300)
- if(A)
- A.locked = 1
- A.updateDialog()
+ if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2)
+ if(!A.shorted)
+ A.shorted = TRUE
+ addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/check_main_power_callback), 120 SECONDS)
- if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2)
- if(A.shorted == 0)
- A.shorted = 1
- spawn(1200)
- if(A && !IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2))
- A.shorted = 0
- A.updateDialog()
-
- if(APC_WIRE_AI_CONTROL)
- if(A.aidisabled == 0)
- A.aidisabled = 1
-
- spawn(10)
- if(A && !IsIndexCut(APC_WIRE_AI_CONTROL))
- A.aidisabled = 0
- A.updateDialog()
+ if(WIRE_AI_CONTROL)
+ if(!A.aidisabled)
+ A.aidisabled = TRUE
+ addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/check_ai_control_callback), 1 SECONDS)
..()
-/datum/wires/apc/UpdateCut(index, mended)
+/datum/wires/apc/on_cut(wire, mend)
var/obj/machinery/power/apc/A = holder
- switch(index)
- if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2)
-
- if(!mended)
+ switch(wire)
+ if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2)
+ if(!mend)
A.shock(usr, 50)
- A.shorted = 1
+ A.shorted = TRUE
- else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2))
- A.shorted = 0
+ else if(!is_cut(WIRE_MAIN_POWER1) && !is_cut(WIRE_MAIN_POWER2))
+ A.shorted = FALSE
A.shock(usr, 50)
- if(APC_WIRE_AI_CONTROL)
-
- if(!mended)
- if(A.aidisabled == 0)
- A.aidisabled = 1
+ if(WIRE_AI_CONTROL)
+ if(!mend)
+ if(!A.aidisabled)
+ A.aidisabled = TRUE
else
- if(A.aidisabled == 1)
- A.aidisabled = 0
+ if(A.aidisabled)
+ A.aidisabled = FALSE
..()
diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm
index c1413abd0cc..0d2aab5a81f 100644
--- a/code/datums/wires/autolathe.dm
+++ b/code/datums/wires/autolathe.dm
@@ -1,21 +1,13 @@
/datum/wires/autolathe
holder_type = /obj/machinery/autolathe
wire_count = 10
+ proper_name = "Autolathe"
+ window_x = 340
+ window_y = 55
-#define AUTOLATHE_HACK_WIRE 1
-#define AUTOLATHE_SHOCK_WIRE 2
-#define AUTOLATHE_DISABLE_WIRE 4
-
-/datum/wires/autolathe/GetWireName(index)
- switch(index)
- if(AUTOLATHE_HACK_WIRE)
- return "Hack"
-
- if(AUTOLATHE_SHOCK_WIRE)
- return "Shock"
-
- if(AUTOLATHE_DISABLE_WIRE)
- return "Disable"
+/datum/wires/autolathe/New(atom/_holder)
+ wires = list(WIRE_AUTOLATHE_HACK, WIRE_ELECTRIFY, WIRE_AUTOLATHE_DISABLE)
+ return ..()
/datum/wires/autolathe/get_status()
. = ..()
@@ -24,51 +16,38 @@
. += "The green light is [A.shocked ? "off" : "on"]."
. += "The blue light is [A.hacked ? "off" : "on"]."
-/datum/wires/autolathe/CanUse()
+/datum/wires/autolathe/interactable(mob/user)
var/obj/machinery/autolathe/A = holder
+ if(iscarbon(user) && A.Adjacent(user) && A.shocked && A.shock(user, 100))
+ return FALSE
if(A.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/autolathe/UpdateCut(index, mended)
+/datum/wires/autolathe/on_cut(wire, mend)
var/obj/machinery/autolathe/A = holder
- switch(index)
- if(AUTOLATHE_HACK_WIRE)
- A.adjust_hacked(!mended)
- if(AUTOLATHE_SHOCK_WIRE)
- A.shocked = !mended
- if(AUTOLATHE_DISABLE_WIRE)
- A.disabled = !mended
+ switch(wire)
+ if(WIRE_AUTOLATHE_HACK)
+ A.adjust_hacked(!mend)
+ if(WIRE_ELECTRIFY)
+ A.shocked = !mend
+ if(WIRE_AUTOLATHE_DISABLE)
+ A.disabled = !mend
..()
-/datum/wires/autolathe/UpdatePulsed(index)
- if(IsIndexCut(index))
+/datum/wires/autolathe/on_pulse(wire)
+ if(is_cut(wire))
return
var/obj/machinery/autolathe/A = holder
- switch(index)
- if(AUTOLATHE_HACK_WIRE)
+ switch(wire)
+ if(WIRE_AUTOLATHE_HACK)
A.adjust_hacked(!A.hacked)
- updateUIs()
- spawn(50)
- if(A && !IsIndexCut(index))
- A.adjust_hacked(0)
- updateUIs()
- if(AUTOLATHE_SHOCK_WIRE)
- A.shocked = !A.shocked
- updateUIs()
- spawn(50)
- if(A && !IsIndexCut(index))
- A.shocked = 0
- updateUIs()
- if(AUTOLATHE_DISABLE_WIRE)
- A.disabled = !A.disabled
- updateUIs()
- spawn(50)
- if(A && !IsIndexCut(index))
- A.disabled = 0
- updateUIs()
+ addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_hacked_callback), 5 SECONDS)
-/datum/wires/autolathe/proc/updateUIs()
- SSnanoui.update_uis(src)
- if(holder)
- SSnanoui.update_uis(holder)
+ if(WIRE_ELECTRIFY)
+ A.shocked = !A.shocked
+ addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_electrified_callback), 5 SECONDS)
+
+ if(WIRE_AUTOLATHE_DISABLE)
+ A.disabled = !A.disabled
+ addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_disabled_callback), 5 SECONDS)
diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm
index a6b1549fbe1..39e0cb2c3e1 100644
--- a/code/datums/wires/camera.dm
+++ b/code/datums/wires/camera.dm
@@ -1,9 +1,15 @@
// Wires for cameras.
/datum/wires/camera
- random = 0
holder_type = /obj/machinery/camera
wire_count = 2
+ proper_name = "Camera"
+ window_x = 350
+ window_y = 95
+
+/datum/wires/camera/New(atom/_holder)
+ wires = list(WIRE_FOCUS, WIRE_MAIN_POWER1)
+ return ..()
/datum/wires/camera/get_status()
. = ..()
@@ -11,51 +17,40 @@
. += "The focus light is [(C.view_range == initial(C.view_range)) ? "on" : "off"]."
. += "The power link light is [C.can_use() ? "on" : "off"]."
-/datum/wires/camera/CanUse(mob/living/L)
+/datum/wires/camera/interactable(mob/user)
var/obj/machinery/camera/C = holder
if(!C.panel_open)
return FALSE
return TRUE
-#define CAMERA_WIRE_FOCUS 1
-#define CAMERA_WIRE_POWER 2
-
-/datum/wires/camera/GetWireName(index)
- switch(index)
- if(CAMERA_WIRE_FOCUS)
- return "Focus"
-
- if(CAMERA_WIRE_POWER)
- return "Power"
-
-/datum/wires/camera/UpdateCut(index, mended)
+/datum/wires/camera/on_cut(wire, mend)
var/obj/machinery/camera/C = holder
- switch(index)
- if(CAMERA_WIRE_FOCUS)
- var/range = (mended ? initial(C.view_range) : C.short_range)
+ switch(wire)
+ if(WIRE_FOCUS)
+ var/range = (mend ? initial(C.view_range) : C.short_range)
C.setViewRange(range)
- if(CAMERA_WIRE_POWER)
- if(C.status && !mended || !C.status && mended)
+ if(WIRE_MAIN_POWER1)
+ if(C.status && !mend || !C.status && mend)
C.toggle_cam(usr, TRUE)
C.obj_integrity = C.max_integrity //this is a pretty simplistic way to heal the camera, but there's no reason for this to be complex.
..()
-/datum/wires/camera/UpdatePulsed(index)
+/datum/wires/camera/on_pulse(wire)
var/obj/machinery/camera/C = holder
- if(IsIndexCut(index))
+ if(is_cut(wire))
return
- switch(index)
- if(CAMERA_WIRE_FOCUS)
+ switch(wire)
+ if(WIRE_FOCUS)
var/new_range = (C.view_range == initial(C.view_range) ? C.short_range : initial(C.view_range))
C.setViewRange(new_range)
- if(CAMERA_WIRE_POWER)
+ if(WIRE_MAIN_POWER1)
C.toggle_cam(null) // Deactivate the camera
..()
/datum/wires/camera/proc/CanDeconstruct()
- if(IsIndexCut(CAMERA_WIRE_POWER) && IsIndexCut(CAMERA_WIRE_FOCUS))
+ if(is_cut(WIRE_MAIN_POWER1) && is_cut(WIRE_FOCUS))
return TRUE
else
return FALSE
diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm
index 47fd627d597..988aa0f37a8 100644
--- a/code/datums/wires/explosive.dm
+++ b/code/datums/wires/explosive.dm
@@ -1,36 +1,36 @@
/datum/wires/explosive
wire_count = 1
+ proper_name = "Explosive"
+ window_x = 320
+ window_y = 50
-#define WIRE_EXPLODE 1
-
-/datum/wires/explosive/GetWireName(index)
- switch(index)
- if(WIRE_EXPLODE)
- return "Explode"
+/datum/wires/explosive/New(atom/_holder)
+ wires = list(WIRE_EXPLODE)
+ return ..()
/datum/wires/explosive/proc/explode()
return
-/datum/wires/explosive/UpdatePulsed(index)
- switch(index)
+/datum/wires/explosive/on_pulse(wire)
+ switch(wire)
if(WIRE_EXPLODE)
explode()
..()
-/datum/wires/explosive/UpdateCut(index, mended)
- switch(index)
+/datum/wires/explosive/on_cut(wire, mend)
+ switch(wire)
if(WIRE_EXPLODE)
- if(!mended)
+ if(!mend)
explode()
..()
/datum/wires/explosive/gibtonite
holder_type = /obj/item/twohanded/required/gibtonite
-/datum/wires/explosive/gibtonite/CanUse(mob/L)
- return 1
+/datum/wires/explosive/gibtonite/interactable(mob/user)
+ return TRUE
-/datum/wires/explosive/gibtonite/UpdateCut(index, mended)
+/datum/wires/explosive/gibtonite/on_cut(wire, mend)
return
/datum/wires/explosive/gibtonite/explode()
diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm
index 988b520854f..cb13b0d6718 100644
--- a/code/datums/wires/mulebot.dm
+++ b/code/datums/wires/mulebot.dm
@@ -1,90 +1,35 @@
/datum/wires/mulebot
- random = 1
+ randomize = TRUE
holder_type = /mob/living/simple_animal/bot/mulebot
wire_count = 10
- window_x = 410
+ proper_name = "Mulebot"
+ window_x = 370
+ window_y = -12
-#define MULEBOT_WIRE_POWER1 1 // power connections
-#define MULEBOT_WIRE_POWER2 2
-#define MULEBOT_WIRE_AVOIDANCE 4 // mob avoidance
-#define MULEBOT_WIRE_LOADCHECK 8 // load checking (non-crate)
-#define MULEBOT_WIRE_MOTOR1 16 // motor wires
-#define MULEBOT_WIRE_MOTOR2 32 //
-#define MULEBOT_WIRE_REMOTE_RX 64 // remote recv functions
-#define MULEBOT_WIRE_REMOTE_TX 128 // remote trans status
-#define MULEBOT_WIRE_BEACON_RX 256 // beacon ping recv
+/datum/wires/mulebot/New(atom/_holder)
+ wires = list(
+ WIRE_MAIN_POWER1, WIRE_MAIN_POWER2, WIRE_MOB_AVOIDANCE,
+ WIRE_LOADCHECK, WIRE_MOTOR1, WIRE_MOTOR2,
+ WIRE_REMOTE_RX, WIRE_REMOTE_TX, WIRE_BEACON_RX
+ )
+ return ..()
-/datum/wires/mulebot/GetWireName(index)
- switch(index)
- if(MULEBOT_WIRE_POWER1)
- return "Primary Power"
-
- if(MULEBOT_WIRE_POWER2)
- return "Secondary Power"
-
- if(MULEBOT_WIRE_AVOIDANCE)
- return "Mob Avoidance"
-
- if(MULEBOT_WIRE_LOADCHECK)
- return "Load Checking"
-
- if(MULEBOT_WIRE_MOTOR1)
- return "Primary Motor"
-
- if(MULEBOT_WIRE_MOTOR2)
- return "Secondary Motor"
-
- if(MULEBOT_WIRE_REMOTE_RX)
- return "Remote Signal Receiver"
-
- if(MULEBOT_WIRE_REMOTE_TX)
- return "Remote Signal Sender"
-
- if(MULEBOT_WIRE_BEACON_RX)
- return "Navigation Beacon Receiver"
-
-/datum/wires/mulebot/CanUse(mob/living/L)
+/datum/wires/mulebot/interactable(mob/user)
var/mob/living/simple_animal/bot/mulebot/M = holder
if(M.open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/mulebot/UpdatePulsed(index)
- switch(index)
- if(MULEBOT_WIRE_POWER1, MULEBOT_WIRE_POWER2)
+/datum/wires/mulebot/on_pulse(wire)
+ switch(wire)
+ if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2)
holder.visible_message("[bicon(holder)] The charge light flickers.")
- if(MULEBOT_WIRE_AVOIDANCE)
+ if(WIRE_MOB_AVOIDANCE)
holder.visible_message("[bicon(holder)] The external warning lights flash briefly.")
- if(MULEBOT_WIRE_LOADCHECK)
+ if(WIRE_LOADCHECK)
holder.visible_message("[bicon(holder)] The load platform clunks.")
- if(MULEBOT_WIRE_MOTOR1, MULEBOT_WIRE_MOTOR2)
+ if(WIRE_MOTOR1, WIRE_MOTOR2)
holder.visible_message("[bicon(holder)] The drive motor whines briefly.")
else
holder.visible_message("[bicon(holder)] You hear a radio crackle.")
..()
-
-// HELPER PROCS
-
-/datum/wires/mulebot/proc/Motor1()
- return !(wires_status & MULEBOT_WIRE_MOTOR1)
-
-/datum/wires/mulebot/proc/Motor2()
- return !(wires_status & MULEBOT_WIRE_MOTOR2)
-
-/datum/wires/mulebot/proc/HasPower()
- return !(wires_status & MULEBOT_WIRE_POWER1) && !(wires_status & MULEBOT_WIRE_POWER2)
-
-/datum/wires/mulebot/proc/LoadCheck()
- return !(wires_status & MULEBOT_WIRE_LOADCHECK)
-
-/datum/wires/mulebot/proc/MobAvoid()
- return !(wires_status & MULEBOT_WIRE_AVOIDANCE)
-
-/datum/wires/mulebot/proc/RemoteTX()
- return !(wires_status & MULEBOT_WIRE_REMOTE_TX)
-
-/datum/wires/mulebot/proc/RemoteRX()
- return !(wires_status & MULEBOT_WIRE_REMOTE_RX)
-
-/datum/wires/mulebot/proc/BeaconRX()
- return !(wires_status & MULEBOT_WIRE_BEACON_RX)
diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm
index 3b8ff5db098..51918c02ff4 100644
--- a/code/datums/wires/nuclearbomb.dm
+++ b/code/datums/wires/nuclearbomb.dm
@@ -1,28 +1,20 @@
/datum/wires/nuclearbomb
holder_type = /obj/machinery/nuclearbomb
- random = 1
- wire_count = 7
+ randomize = TRUE
+ wire_count = 7 // 3 actual, 4 duds.
+ proper_name = "Nuclear bomb"
+ window_x = 345
+ window_y = 75
-#define NUCLEARBOMB_WIRE_LIGHT 1
-#define NUCLEARBOMB_WIRE_TIMING 2
-#define NUCLEARBOMB_WIRE_SAFETY 4
+/datum/wires/nuclearbomb/New(atom/_holder)
+ wires = list(WIRE_BOMB_LIGHT, WIRE_BOMB_TIMING, WIRE_BOMB_SAFETY)
+ return ..()
-/datum/wires/nuclearbomb/GetWireName(index)
- switch(index)
- if(NUCLEARBOMB_WIRE_LIGHT)
- return "Bomb Light"
-
- if(NUCLEARBOMB_WIRE_TIMING)
- return "Bomb Timing"
-
- if(NUCLEARBOMB_WIRE_SAFETY)
- return "Bomb Safety"
-
-/datum/wires/nuclearbomb/CanUse(mob/living/L)
+/datum/wires/nuclearbomb/interactable(mob/user)
var/obj/machinery/nuclearbomb/N = holder
if(N.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/nuclearbomb/get_status()
. = ..()
@@ -31,52 +23,36 @@
. += "The device is is [N.safety ? "quiet" : "whirring"]."
. += "The lights are [N.lighthack ? "static" : "functional"]."
-/datum/wires/nuclearbomb/UpdatePulsed(index)
+/datum/wires/nuclearbomb/on_pulse(wire)
var/obj/machinery/nuclearbomb/N = holder
- switch(index)
- if(NUCLEARBOMB_WIRE_LIGHT)
+ switch(wire)
+ if(WIRE_BOMB_LIGHT)
N.lighthack = !N.lighthack
- updateUIs()
- spawn(100)
- N.lighthack = !N.lighthack
- updateUIs()
- if(NUCLEARBOMB_WIRE_TIMING)
+ addtimer(CALLBACK(N, /obj/machinery/nuclearbomb/.proc/reset_lighthack_callback), 10 SECONDS)
+
+ if(WIRE_BOMB_TIMING)
if(N.timing)
message_admins("[key_name_admin(usr)] pulsed a nuclear bomb's detonation wire, causing it to explode (JMP)")
N.explode()
- if(NUCLEARBOMB_WIRE_SAFETY)
- N.safety = !N.safety
- updateUIs()
- spawn(100)
- N.safety = !N.safety
- if(N.safety == 1)
- if(!N.is_syndicate)
- set_security_level(N.previous_level)
- N.visible_message("The [N] quiets down.")
- if(!N.lighthack)
- if(N.icon_state == "nuclearbomb2")
- N.icon_state = "nuclearbomb1"
- else
- N.visible_message("The [N] emits a quiet whirling noise!")
- updateUIs()
-/datum/wires/nuclearbomb/UpdateCut(index, mended)
+ if(WIRE_BOMB_SAFETY)
+ N.safety = !N.safety
+ addtimer(CALLBACK(N, /obj/machinery/nuclearbomb/.proc/reset_safety_callback), 10 SECONDS)
+
+/datum/wires/nuclearbomb/on_cut(wire, mend)
var/obj/machinery/nuclearbomb/N = holder
- switch(index)
- if(NUCLEARBOMB_WIRE_SAFETY)
+ switch(wire)
+ if(WIRE_BOMB_SAFETY)
if(N.timing)
message_admins("[key_name_admin(usr)] cut a nuclear bomb's timing wire, causing it to explode (JMP)")
N.explode()
- if(NUCLEARBOMB_WIRE_TIMING)
+
+ if(WIRE_BOMB_TIMING)
if(!N.lighthack)
if(N.icon_state == "nuclearbomb2")
N.icon_state = "nuclearbomb1"
N.timing = 0
- GLOB.bomb_set = 0
- if(NUCLEARBOMB_WIRE_LIGHT)
- N.lighthack = !N.lighthack
+ GLOB.bomb_set = FALSE
-/datum/wires/nuclearbomb/proc/updateUIs()
- SSnanoui.update_uis(src)
- if(holder)
- SSnanoui.update_uis(holder)
+ if(WIRE_BOMB_LIGHT)
+ N.lighthack = !N.lighthack
diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm
index d6e68a79cfb..e285f4cf898 100644
--- a/code/datums/wires/particle_accelerator.dm
+++ b/code/datums/wires/particle_accelerator.dm
@@ -1,67 +1,52 @@
/datum/wires/particle_acc/control_box
wire_count = 5
holder_type = /obj/machinery/particle_accelerator/control_box
+ proper_name = "Particle accelerator control"
+ window_x = 361
+ window_y = 22
-#define PARTICLE_TOGGLE_WIRE 1 // Toggles whether the PA is on or not.
-#define PARTICLE_STRENGTH_WIRE 2 // Determines the strength of the PA.
-#define PARTICLE_INTERFACE_WIRE 4 // Determines the interface showing up.
-#define PARTICLE_LIMIT_POWER_WIRE 8 // Determines how strong the PA can be.
+/datum/wires/particle_acc/control_box/New(atom/_holder)
+ wires = list(WIRE_PARTICLE_POWER, WIRE_PARTICLE_STRENGTH, WIRE_PARTICLE_INTERFACE, WIRE_PARTICLE_POWER_LIMIT)
+ return ..()
-/datum/wires/particle_acc/control_box/GetWireName(index)
- switch(index)
- if(PARTICLE_TOGGLE_WIRE)
- return "Power Toggle"
-
- if(PARTICLE_STRENGTH_WIRE)
- return "Strength"
-
- if(PARTICLE_INTERFACE_WIRE)
- return "Interface"
-
- if(PARTICLE_LIMIT_POWER_WIRE)
- return "Maximum Power"
-
-/datum/wires/particle_acc/control_box/CanUse(mob/living/L)
+/datum/wires/particle_acc/control_box/interactable(mob/user)
var/obj/machinery/particle_accelerator/control_box/C = holder
if(C.construction_state == 2)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/particle_acc/control_box/UpdatePulsed(index)
+/datum/wires/particle_acc/control_box/on_pulse(wire)
var/obj/machinery/particle_accelerator/control_box/C = holder
- switch(index)
-
- if(PARTICLE_TOGGLE_WIRE)
+ switch(wire)
+ if(WIRE_PARTICLE_POWER)
C.toggle_power()
- if(PARTICLE_STRENGTH_WIRE)
+ if(WIRE_PARTICLE_STRENGTH)
C.add_strength()
- if(PARTICLE_INTERFACE_WIRE)
+ if(WIRE_PARTICLE_INTERFACE)
C.interface_control = !C.interface_control
- if(PARTICLE_LIMIT_POWER_WIRE)
+ if(WIRE_PARTICLE_POWER_LIMIT)
C.visible_message("[bicon(C)][C] makes a large whirring noise.")
..()
-/datum/wires/particle_acc/control_box/UpdateCut(index, mended)
+/datum/wires/particle_acc/control_box/on_cut(wire, mend)
var/obj/machinery/particle_accelerator/control_box/C = holder
- switch(index)
-
- if(PARTICLE_TOGGLE_WIRE)
- if(C.active == !mended)
+ switch(wire)
+ if(WIRE_PARTICLE_POWER)
+ if(C.active == !mend)
C.toggle_power()
- if(PARTICLE_STRENGTH_WIRE)
-
- for(var/i = 1; i < 3; i++)
+ if(WIRE_PARTICLE_STRENGTH)
+ for(var/i in 1 to 2)
C.remove_strength()
- if(PARTICLE_INTERFACE_WIRE)
- C.interface_control = mended
+ if(WIRE_PARTICLE_INTERFACE)
+ C.interface_control = mend
- if(PARTICLE_LIMIT_POWER_WIRE)
- C.strength_upper_limit = (mended ? 2 : 3)
+ if(WIRE_PARTICLE_POWER_LIMIT)
+ C.strength_upper_limit = (mend ? 2 : 3)
if(C.strength_upper_limit < C.strength)
C.remove_strength()
..()
diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm
index 90a72935c53..1efee71692d 100644
--- a/code/datums/wires/radio.dm
+++ b/code/datums/wires/radio.dm
@@ -1,52 +1,44 @@
/datum/wires/radio
holder_type = /obj/item/radio
wire_count = 3
+ proper_name = "Radio"
+ window_x = 330
+ window_y = 37
-#define RADIO_WIRE_SIGNAL 1
-#define RADIO_WIRE_RECEIVE 2
-#define RADIO_WIRE_TRANSMIT 4
+/datum/wires/radio/New(atom/_holder)
+ wires = list(WIRE_RADIO_SIGNAL, WIRE_RADIO_RECEIVER, WIRE_RADIO_TRANSMIT)
+ return ..()
-/datum/wires/radio/GetWireName(index)
- switch(index)
- if(RADIO_WIRE_SIGNAL)
- return "Signal"
-
- if(RADIO_WIRE_RECEIVE)
- return "Receiver"
-
- if(RADIO_WIRE_TRANSMIT)
- return "Transmitter"
-
-/datum/wires/radio/CanUse(mob/living/L)
+/datum/wires/radio/interactable(mob/user)
var/obj/item/radio/R = holder
if(R.b_stat)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/radio/UpdatePulsed(index)
+/datum/wires/radio/on_pulse(wire)
var/obj/item/radio/R = holder
- switch(index)
- if(RADIO_WIRE_SIGNAL)
- R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_RECEIVE)
- R.broadcasting = R.listening && !IsIndexCut(RADIO_WIRE_TRANSMIT)
+ switch(wire)
+ if(WIRE_RADIO_SIGNAL)
+ R.listening = !R.listening && !is_cut(WIRE_RADIO_RECEIVER)
+ R.broadcasting = R.listening && !is_cut(WIRE_RADIO_TRANSMIT)
- if(RADIO_WIRE_RECEIVE)
- R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_RECEIVER)
+ R.listening = !R.listening && !is_cut(WIRE_RADIO_SIGNAL)
- if(RADIO_WIRE_TRANSMIT)
- R.broadcasting = !R.broadcasting && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_TRANSMIT)
+ R.broadcasting = !R.broadcasting && !is_cut(WIRE_RADIO_SIGNAL)
..()
-/datum/wires/radio/UpdateCut(index, mended)
+/datum/wires/radio/on_cut(wire, mend)
var/obj/item/radio/R = holder
- switch(index)
- if(RADIO_WIRE_SIGNAL)
- R.listening = mended && !IsIndexCut(RADIO_WIRE_RECEIVE)
- R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_TRANSMIT)
+ switch(wire)
+ if(WIRE_RADIO_SIGNAL)
+ R.listening = mend && !is_cut(WIRE_RADIO_RECEIVER)
+ R.broadcasting = mend && !is_cut(WIRE_RADIO_TRANSMIT)
- if(RADIO_WIRE_RECEIVE)
- R.listening = mended && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_RECEIVER)
+ R.listening = mend && !is_cut(WIRE_RADIO_SIGNAL)
- if(RADIO_WIRE_TRANSMIT)
- R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_TRANSMIT)
+ R.broadcasting = mend && !is_cut(WIRE_RADIO_SIGNAL)
..()
diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm
index 04a9509cfe2..927980c57fa 100644
--- a/code/datums/wires/robot.dm
+++ b/code/datums/wires/robot.dm
@@ -1,32 +1,14 @@
/datum/wires/robot
- random = 1
+ randomize = TRUE
holder_type = /mob/living/silicon/robot
wire_count = 5
+ window_x = 340
+ window_y = 106
+ proper_name = "Cyborg"
-// /vg/ ordering
-
-#define BORG_WIRE_MAIN_POWER 1 // The power wires do nothing whyyyyyyyyyyyyy
-#define BORG_WIRE_LOCKED_DOWN 2
-#define BORG_WIRE_CAMERA 4
-#define BORG_WIRE_AI_CONTROL 8 // Not used on MoMMIs
-#define BORG_WIRE_LAWCHECK 16 // Not used on MoMMIs
-
-/datum/wires/robot/GetWireName(index)
- switch(index)
- if(BORG_WIRE_MAIN_POWER)
- return "Main Power"
-
- if(BORG_WIRE_LOCKED_DOWN)
- return "Lockdown"
-
- if(BORG_WIRE_CAMERA)
- return "Camera"
-
- if(BORG_WIRE_AI_CONTROL)
- return "AI Control"
-
- if(BORG_WIRE_LAWCHECK)
- return "Law Check"
+/datum/wires/robot/New(atom/_holder)
+ wires = list(WIRE_AI_CONTROL, WIRE_BORG_CAMERA, WIRE_BORG_LAWCHECK, WIRE_BORG_LOCKED)
+ return ..()
/datum/wires/robot/get_status()
. = ..()
@@ -36,70 +18,53 @@
. += "The Camera light is [(R.camera && R.camera.status == 1) ? "on" : "off"]."
. += "The lockdown light is [R.lockcharge ? "on" : "off"]."
-/datum/wires/robot/UpdateCut(index, mended)
-
+/datum/wires/robot/on_cut(wire, mend)
var/mob/living/silicon/robot/R = holder
- switch(index)
- if(BORG_WIRE_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI
- if(!mended)
- if(R.lawupdate == 1)
+ switch(wire)
+ if(WIRE_BORG_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI
+ if(!mend)
+ if(R.lawupdate)
to_chat(R, "LawSync protocol engaged.")
+ R.lawsync()
R.show_laws()
else
- if(R.lawupdate == 0 && !R.emagged)
- R.lawupdate = 1
+ if(!R.lawupdate && !R.emagged)
+ R.lawupdate = TRUE
- if(BORG_WIRE_AI_CONTROL) //Cut the AI wire to reset AI control
- if(!mended)
+ if(WIRE_AI_CONTROL) //Cut the AI wire to reset AI control
+ if(!mend)
if(R.connected_ai)
R.disconnect_from_ai()
- if(BORG_WIRE_CAMERA)
+ if(WIRE_BORG_CAMERA)
if(!isnull(R.camera) && !R.scrambledcodes)
- R.camera.status = mended
+ R.camera.status = mend
R.camera.toggle_cam(usr, 0) // Will kick anyone who is watching the Cyborg's camera.
- if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh
- if(R.lawupdate)
- R.lawsync()
-
- if(BORG_WIRE_LOCKED_DOWN)
- R.SetLockdown(!mended)
+ if(WIRE_BORG_LOCKED)
+ R.SetLockdown(!mend)
..()
-/datum/wires/robot/UpdatePulsed(index)
-
+/datum/wires/robot/on_pulse(wire)
var/mob/living/silicon/robot/R = holder
- switch(index)
- if(BORG_WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI
+ switch(wire)
+ if(WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI
if(!R.emagged)
R.connect_to_ai(select_active_ai())
- if(BORG_WIRE_CAMERA)
+ if(WIRE_BORG_CAMERA)
if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes)
R.camera.toggle_cam(usr, 0) // Kick anyone watching the Cyborg's camera, doesn't display you disconnecting the camera.
R.visible_message("[R]'s camera lense focuses loudly.")
to_chat(R, "Your camera lense focuses loudly.")
- if(BORG_WIRE_LOCKED_DOWN)
+ if(WIRE_BORG_LOCKED)
R.SetLockdown(!R.lockcharge) // Toggle
..()
-/datum/wires/robot/CanUse(mob/living/L)
+/datum/wires/robot/interactable(mob/user)
var/mob/living/silicon/robot/R = holder
if(R.wiresexposed)
- return 1
- return 0
-
-/datum/wires/robot/proc/IsCameraCut()
- return wires_status & BORG_WIRE_CAMERA
-
-/datum/wires/robot/proc/LockedCut()
- return wires_status & BORG_WIRE_LOCKED_DOWN
-
-/datum/wires/robot/proc/CanLawCheck()
- return wires_status & BORG_WIRE_LAWCHECK
-
-/datum/wires/robot/proc/AIHasControl()
- return wires_status & BORG_WIRE_AI_CONTROL
+ return TRUE
+ return FALSE
diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm
index a65377ead1a..3955b1849b0 100644
--- a/code/datums/wires/smartfridge.dm
+++ b/code/datums/wires/smartfridge.dm
@@ -1,35 +1,26 @@
/datum/wires/smartfridge
holder_type = /obj/machinery/smartfridge
wire_count = 3
+ proper_name = "Smartfridge"
+ window_x = 340
+ window_y = 103
+
+/datum/wires/smartfridge/New(atom/_holder)
+ wires = list(WIRE_ELECTRIFY, WIRE_IDSCAN, WIRE_THROW_ITEM)
+ return ..()
/datum/wires/smartfridge/secure
- random = 1
- wire_count = 4
+ randomize = TRUE
+ wire_count = 4 // 3 actual, 1 dud.
+ window_y = 97
-#define SMARTFRIDGE_WIRE_ELECTRIFY 1
-#define SMARTFRIDGE_WIRE_THROW 2
-#define SMARTFRIDGE_WIRE_IDSCAN 4
-
-/datum/wires/smartfridge/GetWireName(index)
- switch(index)
- if(SMARTFRIDGE_WIRE_ELECTRIFY)
- return "Electrification"
-
- if(SMARTFRIDGE_WIRE_THROW)
- return "Item Throw"
-
- if(SMARTFRIDGE_WIRE_IDSCAN)
- return "ID Scan"
-
-/datum/wires/smartfridge/CanUse(mob/living/L)
+/datum/wires/smartfridge/interactable(mob/user)
var/obj/machinery/smartfridge/S = holder
- if(!issilicon(L))
- if(S.seconds_electrified)
- if(S.shock(L, 100))
- return 0
+ if(iscarbon(user) && S.Adjacent(user) && S.seconds_electrified && S.shock(user, 100))
+ return FALSE
if(S.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/smartfridge/get_status()
. = ..()
@@ -38,27 +29,27 @@
. += "The red light is [S.shoot_inventory ? "off" : "blinking"]."
. += "A [S.scan_id ? "purple" : "yellow"] light is on."
-/datum/wires/smartfridge/UpdatePulsed(index)
+/datum/wires/smartfridge/on_pulse(wire)
var/obj/machinery/smartfridge/S = holder
- switch(index)
- if(SMARTFRIDGE_WIRE_THROW)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
S.shoot_inventory = !S.shoot_inventory
- if(SMARTFRIDGE_WIRE_ELECTRIFY)
+ if(WIRE_ELECTRIFY)
S.seconds_electrified = 30
- if(SMARTFRIDGE_WIRE_IDSCAN)
+ if(WIRE_IDSCAN)
S.scan_id = !S.scan_id
..()
-/datum/wires/smartfridge/UpdateCut(index, mended)
+/datum/wires/smartfridge/on_cut(wire, mend)
var/obj/machinery/smartfridge/S = holder
- switch(index)
- if(SMARTFRIDGE_WIRE_THROW)
- S.shoot_inventory = !mended
- if(SMARTFRIDGE_WIRE_ELECTRIFY)
- if(mended)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
+ S.shoot_inventory = !mend
+ if(WIRE_ELECTRIFY)
+ if(mend)
S.seconds_electrified = 0
else
S.seconds_electrified = -1
- if(SMARTFRIDGE_WIRE_IDSCAN)
- S.scan_id = 1
+ if(WIRE_IDSCAN)
+ S.scan_id = TRUE
..()
diff --git a/code/datums/wires/suitstorage.dm b/code/datums/wires/suitstorage.dm
index 175b36e6081..828e56884bf 100644
--- a/code/datums/wires/suitstorage.dm
+++ b/code/datums/wires/suitstorage.dm
@@ -1,24 +1,13 @@
/datum/wires/suitstorage
holder_type = /obj/machinery/suit_storage_unit
wire_count = 8
+ proper_name = "Suit storage unit"
+ window_x = 350
+ window_y = 85
-#define SSU_WIRE_ID 1
-#define SSU_WIRE_SHOCK 2
-#define SSU_WIRE_SAFETY 4
-#define SSU_WIRE_UV 8
-
-
-/datum/wires/suitstorage/GetWireName(index)
- switch(index)
- if(SSU_WIRE_ID)
- return "ID lock"
- if(SSU_WIRE_SHOCK)
- return "Shock wire"
- if(SSU_WIRE_SAFETY)
- return "Safety wire"
- if(SSU_WIRE_UV)
- return "UV wire"
-
+/datum/wires/suitstorage/New(atom/_holder)
+ wires = list(WIRE_IDSCAN, WIRE_ELECTRIFY, WIRE_SAFETY, WIRE_SSU_UV)
+ return ..()
/datum/wires/suitstorage/get_status()
. = ..()
@@ -28,42 +17,48 @@
. += "The green light is [A.shocked ? "on" : "off"]."
. += "The UV display shows [A.uv_super ? "15 nm" : "185 nm"]."
-datum/wires/suitstorage/CanUse()
+datum/wires/suitstorage/interactable(mob/user)
var/obj/machinery/suit_storage_unit/A = holder
+ if(iscarbon(user) && A.Adjacent(user) && A.shocked)
+ return A.shock(user, 100)
if(A.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/suitstorage/UpdateCut(index, mended)
+/datum/wires/suitstorage/on_cut(wire, mend)
var/obj/machinery/suit_storage_unit/A = holder
- switch(index)
- if(SSU_WIRE_ID)
- A.secure = mended
- if(SSU_WIRE_SAFETY)
- A.safeties = mended
- if(SSU_WIRE_SHOCK)
- A.shocked = !mended
+ switch(wire)
+ if(WIRE_IDSCAN)
+ A.secure = mend
+
+ if(WIRE_SAFETY)
+ A.safeties = mend
+
+ if(WIRE_ELECTRIFY)
+ A.shocked = !mend
A.shock(usr, 50)
- if(SSU_WIRE_UV)
- A.uv_super = !mended
+
+ if(WIRE_SSU_UV)
+ A.uv_super = !mend
..()
-datum/wires/suitstorage/UpdatePulsed(index)
+datum/wires/suitstorage/on_pulse(wire)
var/obj/machinery/suit_storage_unit/A = holder
- if(IsIndexCut(index))
+ if(is_cut(wire))
return
- switch(index)
- if(SSU_WIRE_ID)
+ switch(wire)
+ if(WIRE_IDSCAN)
A.secure = !A.secure
- if(SSU_WIRE_SAFETY)
+
+ if(WIRE_SAFETY)
A.safeties = !A.safeties
- if(SSU_WIRE_SHOCK)
+
+ if(WIRE_ELECTRIFY)
A.shocked = !A.shocked
if(A.shocked)
A.shock(usr, 100)
- spawn(50)
- if(A && !IsIndexCut(index))
- A.shocked = FALSE
- if(SSU_WIRE_UV)
+ addtimer(CALLBACK(A, /obj/machinery/suit_storage_unit/.proc/check_electrified_callback), 5 SECONDS)
+
+ if(WIRE_SSU_UV)
A.uv_super = !A.uv_super
..()
diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm
index 430b5ce88aa..bd996f72fcc 100644
--- a/code/datums/wires/syndicatebomb.dm
+++ b/code/datums/wires/syndicatebomb.dm
@@ -1,47 +1,31 @@
/datum/wires/syndicatebomb
- random = TRUE
+ randomize = TRUE
holder_type = /obj/machinery/syndicatebomb
wire_count = 5
+ proper_name = "Syndicate bomb"
+ window_x = 320
+ window_y = 22
-#define BOMB_WIRE_BOOM 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut
-#define BOMB_WIRE_UNBOLT 2 // Unbolts the bomb if cut, hint on pulsed
-#define BOMB_WIRE_DELAY 4 // Raises the timer on pulse, does nothing on cut
-#define BOMB_WIRE_PROCEED 8 // Lowers the timer, explodes if cut while the bomb is active
-#define BOMB_WIRE_ACTIVATE 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut
+/datum/wires/syndicatebomb/New(atom/_holder)
+ wires = list(WIRE_BOMB_DELAY, WIRE_EXPLODE, WIRE_BOMB_UNBOLT,WIRE_BOMB_PROCEED, WIRE_BOMB_ACTIVATE)
+ return ..()
-/datum/wires/syndicatebomb/GetWireName(index)
- switch(index)
- if(BOMB_WIRE_BOOM)
- return "Explode"
-
- if(BOMB_WIRE_UNBOLT)
- return "Unbolt"
-
- if(BOMB_WIRE_DELAY)
- return "Delay"
-
- if(BOMB_WIRE_PROCEED)
- return "Proceed"
-
- if(BOMB_WIRE_ACTIVATE)
- return "Activate"
-
-/datum/wires/syndicatebomb/CanUse(mob/living/L)
+/datum/wires/syndicatebomb/interactable(mob/user)
var/obj/machinery/syndicatebomb/P = holder
if(P.open_panel)
return TRUE
return FALSE
-/datum/wires/syndicatebomb/UpdatePulsed(index)
+/datum/wires/syndicatebomb/on_pulse(wire)
var/obj/machinery/syndicatebomb/B = holder
- switch(index)
- if(BOMB_WIRE_BOOM)
+ switch(wire)
+ if(WIRE_EXPLODE)
if(B.active)
holder.visible_message("[bicon(B)] An alarm sounds! It's go-")
B.explode_now = TRUE
- if(BOMB_WIRE_UNBOLT)
+ if(WIRE_BOMB_UNBOLT)
holder.visible_message("[bicon(holder)] The bolts spin in place for a moment.")
- if(BOMB_WIRE_DELAY)
+ if(WIRE_BOMB_DELAY)
if(B.delayedbig)
holder.visible_message("[bicon(B)] The bomb has already been delayed.")
else
@@ -49,7 +33,7 @@
playsound(B, 'sound/machines/chime.ogg', 30, 1)
B.detonation_timer += 300
B.delayedbig = TRUE
- if(BOMB_WIRE_PROCEED)
+ if(WIRE_BOMB_PROCEED)
holder.visible_message("[bicon(B)] The bomb buzzes ominously!")
playsound(B, 'sound/machines/buzz-sigh.ogg', 30, 1)
var/seconds = B.seconds_remaining()
@@ -59,7 +43,7 @@
B.detonation_timer -= 100
else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy.
B.detonation_timer = world.time + 100
- if(BOMB_WIRE_ACTIVATE)
+ if(WIRE_BOMB_ACTIVATE)
if(!B.active && !B.defused)
holder.visible_message("[bicon(B)] You hear the bomb start ticking!")
B.activate()
@@ -72,11 +56,11 @@
B.delayedlittle = TRUE
..()
-/datum/wires/syndicatebomb/UpdateCut(index, mended)
+/datum/wires/syndicatebomb/on_cut(wire, mend)
var/obj/machinery/syndicatebomb/B = holder
- switch(index)
- if(BOMB_WIRE_BOOM)
- if(mended)
+ switch(wire)
+ if(WIRE_EXPLODE)
+ if(mend)
B.defused = FALSE // Cutting and mending all the wires of an inactive bomb will thus cure any sabotage.
else
if(B.active)
@@ -84,17 +68,17 @@
B.explode_now = TRUE
else
B.defused = TRUE
- if(BOMB_WIRE_UNBOLT)
- if(!mended && B.anchored)
+ if(WIRE_BOMB_UNBOLT)
+ if(!mend && B.anchored)
holder.visible_message("[bicon(B)] The bolts lift out of the ground!")
playsound(B, 'sound/effects/stealthoff.ogg', 30, 1)
B.anchored = FALSE
- if(BOMB_WIRE_PROCEED)
- if(!mended && B.active)
+ if(WIRE_BOMB_PROCEED)
+ if(!mend && B.active)
holder.visible_message("[bicon(B)] An alarm sounds! It's go-")
B.explode_now = TRUE
- if(BOMB_WIRE_ACTIVATE)
- if(!mended && B.active)
+ if(WIRE_BOMB_ACTIVATE)
+ if(!mend && B.active)
holder.visible_message("[bicon(B)] The timer stops! The bomb has been defused!")
B.defused = TRUE
B.update_icon()
diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm
index bc513c6c6c0..f4dbf39017a 100644
--- a/code/datums/wires/tesla_coil.dm
+++ b/code/datums/wires/tesla_coil.dm
@@ -1,23 +1,23 @@
/datum/wires/tesla_coil
wire_count = 1
holder_type = /obj/machinery/power/tesla_coil
+ proper_name = "Tesla coil"
+ window_x = 320
+ window_y = 50
-#define TESLACOIL_WIRE_ZAP 1
+/datum/wires/tesla_coil/New(atom/_holder)
+ wires = list(WIRE_TESLACOIL_ZAP)
+ return ..()
-/datum/wires/tesla_coil/GetWireName(index)
- switch(index)
- if(TESLACOIL_WIRE_ZAP)
- return "Zap"
-
-/datum/wires/tesla_coil/CanUse(mob/living/L)
+/datum/wires/tesla_coil/interactable(mob/user)
var/obj/machinery/power/tesla_coil/T = holder
if(T && T.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/tesla_coil/UpdatePulsed(index)
+/datum/wires/tesla_coil/on_pulse(wire)
var/obj/machinery/power/tesla_coil/T = holder
- switch(index)
- if(TESLACOIL_WIRE_ZAP)
+ switch(wire)
+ if(WIRE_TESLACOIL_ZAP)
T.zap()
..()
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index c9e6032afee..299e18d815a 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -1,35 +1,21 @@
/datum/wires/vending
holder_type = /obj/machinery/vending
wire_count = 4
+ window_y = 112
+ window_x = 350
+ proper_name = "Vending machine"
-#define VENDING_WIRE_THROW 1
-#define VENDING_WIRE_CONTRABAND 2
-#define VENDING_WIRE_ELECTRIFY 4
-#define VENDING_WIRE_IDSCAN 8
+/datum/wires/vending/New(atom/_holder)
+ wires = list(WIRE_THROW_ITEM, WIRE_IDSCAN, WIRE_ELECTRIFY, WIRE_CONTRABAND)
+ return ..()
-/datum/wires/vending/GetWireName(index)
- switch(index)
- if(VENDING_WIRE_THROW)
- return "Item Throw"
-
- if(VENDING_WIRE_CONTRABAND)
- return "Contraband"
-
- if(VENDING_WIRE_ELECTRIFY)
- return "Electrification"
-
- if(VENDING_WIRE_IDSCAN)
- return "ID Scan"
-
-/datum/wires/vending/CanUse(mob/living/L)
+/datum/wires/vending/interactable(mob/user)
var/obj/machinery/vending/V = holder
- if(!istype(L, /mob/living/silicon))
- if(V.seconds_electrified)
- if(V.shock(L, 100))
- return 0
+ if(!istype(user, /mob/living/silicon) && V.seconds_electrified && V.shock(user, 100))
+ return FALSE
if(V.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/vending/get_status()
. = ..()
@@ -39,31 +25,31 @@
. += "The green light is [V.extended_inventory ? "on" : "off"]."
. += "A [V.scan_id ? "purple" : "yellow"] light is on."
-/datum/wires/vending/UpdatePulsed(index)
+/datum/wires/vending/on_pulse(wire)
var/obj/machinery/vending/V = holder
- switch(index)
- if(VENDING_WIRE_THROW)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
V.shoot_inventory = !V.shoot_inventory
- if(VENDING_WIRE_CONTRABAND)
+ if(WIRE_CONTRABAND)
V.extended_inventory = !V.extended_inventory
- if(VENDING_WIRE_ELECTRIFY)
+ if(WIRE_ELECTRIFY)
V.seconds_electrified = 30
- if(VENDING_WIRE_IDSCAN)
+ if(WIRE_IDSCAN)
V.scan_id = !V.scan_id
..()
-/datum/wires/vending/UpdateCut(index, mended)
+/datum/wires/vending/on_cut(wire, mend)
var/obj/machinery/vending/V = holder
- switch(index)
- if(VENDING_WIRE_THROW)
- V.shoot_inventory = !mended
- if(VENDING_WIRE_CONTRABAND)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
+ V.shoot_inventory = !mend
+ if(WIRE_CONTRABAND)
V.extended_inventory = FALSE
- if(VENDING_WIRE_ELECTRIFY)
- if(mended)
+ if(WIRE_ELECTRIFY)
+ if(mend)
V.seconds_electrified = 0
else
V.seconds_electrified = -1
- if(VENDING_WIRE_IDSCAN)
- V.scan_id = 1
+ if(WIRE_IDSCAN)
+ V.scan_id = TRUE
..()
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index 40aeda21b58..27d1448ac77 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -1,331 +1,456 @@
-// Wire datums. Created by Giacomand.
-// Was created to replace a horrible case of copy and pasted code with no care for maintability.
-// Goodbye Door wires, Cyborg wires, Vending Machine wires, Autolathe wires
-// Protolathe wires, APC wires and Camera wires!
-
-#define MAX_FLAG 65535
-
-GLOBAL_LIST_EMPTY(same_wires)
-// 12 colours, if you're adding more than 12 wires then add more colours here
-GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink"))
/datum/wires
+ /// TRUE if the wires will be different every time a new wire datum is created.
+ var/randomize = FALSE
+ /// The atom the wires belong too. For example: an airlock.
+ var/atom/holder
+ /// The holder type; used to make sure that the holder is the correct type.
+ var/holder_type
+ /// The display name for the TGUI window. For example, given the var is "APC"...
+ /// When the TGUI window is opened, "wires" will be appended to it's title, and it would become "APC wires".
+ var/proper_name = "Unknown"
+ /// The total number of wires that our holder atom has.
+ var/wire_count = NONE
+ /// A list of all wires. For a list of valid wires defines that can go here, see `code/__DEFINES/wires.dm`
+ var/list/wires
+ /// A list of all cut wires. The same values that can go into `wires` will get added and removed from this list.
+ var/list/cut_wires
+ /// An associative list with the wire color as the key, and the wire define as the value.
+ var/list/colors
+ /// An associative list of signalers attached to the wires. The wire color is the key, and the signaler object reference is the value.
+ var/list/assemblies
+ /// The width of the wire TGUI window.
+ var/window_x = 300
+ /// The height of the wire TGUI window. Will get longer as needed, based on the `wire_count`.
+ var/window_y = 100
- var/random = 0 // Will the wires be different for every single instance.
- var/atom/holder = null // The holder
- var/holder_type = null // The holder type; used to make sure that the holder is the correct type.
- var/wire_count = 0 // Max is 16
- var/wires_status = 0 // BITFLAG OF WIRES
-
- var/list/wires = list()
- var/list/signallers = list()
-
- var/table_options = " align='center'"
- var/row_options1 = " width='80px'"
- var/row_options2 = " width='260px'"
- var/window_x = 370
- var/window_y = 470
-
-/datum/wires/New(atom/holder)
+/datum/wires/New(atom/_holder)
..()
- src.holder = holder
- if(!istype(holder, holder_type))
+ if(!istype(_holder, holder_type))
CRASH("Our holder is null/the wrong type!")
- // Generate new wires
- if(random)
- GenerateWires()
- // Get the same wires
+ holder = _holder
+ cut_wires = list()
+ colors = list()
+ assemblies = list()
+
+ // Add in the appropriate amount of dud wires.
+ var/wire_len = length(wires)
+ if(wire_len < wire_count) // If the amount of "real" wires is less than the total we're suppose to have...
+ add_duds(wire_count - wire_len) // Add in the appropriate amount of duds to reach `wire_count`.
+
+ // If the randomize is true, we need to generate a new set of wires and ignore any wire color directories.
+ if(randomize)
+ randomize()
+ return
+
+ if(!GLOB.wire_color_directory[holder_type])
+ randomize()
+ GLOB.wire_color_directory[holder_type] = colors
else
- // We don't have any wires to copy yet, generate some and then copy it.
- if(!GLOB.same_wires[holder_type])
- GenerateWires()
- GLOB.same_wires[holder_type] = src.wires.Copy()
- else
- var/list/wires = GLOB.same_wires[holder_type]
- src.wires = wires // Reference the wires list.
+ colors = GLOB.wire_color_directory[holder_type]
/datum/wires/Destroy()
holder = null
+ for(var/color in colors)
+ detach_assembly(color)
return ..()
-/datum/wires/proc/GenerateWires()
- var/list/colours_to_pick = GLOB.wireColours.Copy() // Get a copy, not a reference.
- var/list/indexes_to_pick = list()
- //Generate our indexes
- for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i)
- indexes_to_pick += i
- colours_to_pick.len = wire_count // Downsize it to our specifications.
+/**
+ * Randomly generates a new set of wires. and corresponding colors from the given pool. Assigns the information as an associative list, to `colors`.
+ *
+ * In the `colors` list, the name of the color is the key, and the wire is the value.
+ * For example: `colors["red"] = WIRE_ELECTRIFY`. This will look like `list("red" = WIRE_ELECTRIFY)` internally.
+ */
+datum/wires/proc/randomize()
+ var/static/list/possible_colors = list("red", "blue", "green", "silver", "orange", "brown", "gold", "white", "cyan", "magenta", "purple", "pink")
+ var/list/my_possible_colors = possible_colors.Copy()
- while(colours_to_pick.len && indexes_to_pick.len)
- // Pick and remove a colour
- var/colour = pick_n_take(colours_to_pick)
-
- // Pick and remove an index
- var/index = pick_n_take(indexes_to_pick)
-
- src.wires[colour] = index
- //wires = shuffle(wires)
-
-/datum/wires/proc/get_status()
- return list()
+ for(var/wire in shuffle(wires))
+ colors[pick_n_take(my_possible_colors)] = wire
+/**
+ * Proc called when the user attempts to interact with wires UI.
+ *
+ * Checks if the user exists, is a mob, the wires are attached to something (`holder`) and makes sure `interactable(user)` returns TRUE.
+ * If all the checks succeed, open the TGUI interface for the user.
+ *
+ * Arugments:
+ * * user - the mob trying to interact with the wires.
+ */
/datum/wires/proc/Interact(mob/user)
- if(user && istype(user) && holder && CanUse(user))
- ui_interact(user)
+ if(user && istype(user) && holder && interactable(user))
+ tgui_interact(user)
-/datum/wires/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/**
+ * Base proc, intended to be overriden. Wire datum specific checks you want to run before the TGUI is shown to the user should go here.
+ */
+/datum/wires/proc/interactable(mob/user)
+ return TRUE
+
+/// Users will be interacting with our holder object and not the wire datum directly, therefore we need to return the holder.
+/datum/wires/tgui_host()
+ return holder
+
+/datum/wires/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "wires.tmpl", holder.name, window_x, window_y)
+ ui = new(user, src, ui_key, "Wires", "[proper_name] wires", window_x, window_y + wire_count * 30, master_ui, state)
ui.open()
-/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state)
- var/data[0]
- var/list/replace_colours = null
+/datum/wires/tgui_data(mob/user)
+ var/list/data = list()
+ var/list/replace_colors
+
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes)
- if(eyes && (COLOURBLIND in H.mutations))
- replace_colours = eyes.replace_colours
+ if(eyes && (COLOURBLIND in H.mutations)) // Check if the human has colorblindness.
+ replace_colors = eyes.replace_colours // Get the colorblind replacement colors list.
+ var/list/wires_list = list()
- var/list/W[0]
- for(var/colour in wires)
- var/new_colour = colour
- var/colour_name = colour
- if(colour in replace_colours)
- new_colour = replace_colours[colour]
- if(new_colour in LIST_REPLACE_RENAME)
- colour_name = LIST_REPLACE_RENAME[new_colour]
+ for(var/color in colors)
+ var/replaced_color = color
+ var/color_name = color
+
+ if(color in replace_colors) // If this color is one that needs to be replaced using the colorblindness list.
+ replaced_color = replace_colors[color]
+ if(replaced_color in LIST_COLOR_RENAME) // If its an ugly written color name like "darkolivegreen", rename it to something like "dark green".
+ color_name = LIST_COLOR_RENAME[replaced_color]
else
- colour_name = new_colour
- else
- new_colour = colour
- colour_name = new_colour
- W[++W.len] = list("colour_name" = capitalize(colour_name), "seen_colour" = capitalize(new_colour),"colour" = capitalize(colour), "cut" = IsColourCut(colour), "index" = can_see_wire_index(user) ? GetWireName(GetIndex(colour)) : null, "attached" = IsAttached(colour))
+ color_name = replaced_color // Else just keep the normal color name
- if(W.len > 0)
- data["wires"] = W
+ wires_list += list(list(
+ "seen_color" = replaced_color, // The color of the wire that the mob will see. This will be the same as `color` if the user is NOT colorblind.
+ "color_name" = color_name, // The wire's name. This will be the same as `color` if the user is NOT colorblind.
+ "color" = color, // The "real" color of the wire. No replacements.
+ "wire" = can_see_wire_info(user) && !is_dud_color(color) ? get_wire(color) : null, // Wire define information like "Contraband" or "Door Bolts".
+ "cut" = is_color_cut(color), // Whether the wire is cut or not. Used to display "cut" or "mend".
+ "attached" = is_attached(color) // Whether or not a signaler is attached to this wire.
+ ))
+ data["wires"] = wires_list
+ // Get the information shown at the bottom of wire TGUI window, such as "The red light is blinking", etc.
+ // If the user is colorblind, we need to replace these colors as well.
var/list/status = get_status()
- if(replace_colours)
- var/i
- for(i=1, i<=status.len, i++)
- for(var/colour in replace_colours)
- var/new_colour = replace_colours[colour]
- if(new_colour in LIST_REPLACE_RENAME)
- new_colour = LIST_REPLACE_RENAME[new_colour]
- if(findtext(status[i],colour))
- status[i] = replacetext(status[i],colour,new_colour)
- break
- data["status_len"] = status.len
- data["status"] = status
+ if(replace_colors)
+ var/i
+ for(i in 1 to length(status))
+ for(var/color in replace_colors)
+ var/new_color = replace_colors[color]
+ if(new_color in LIST_COLOR_RENAME)
+ new_color = LIST_COLOR_RENAME[new_color]
+ if(findtext(status[i], color))
+ status[i] = replacetext(status[i], color, new_color)
+ break
+
+ data["status"] = status
return data
-/datum/wires/nano_host()
- return holder
+/datum/wires/tgui_act(action, list/params)
+ if(..())
+ return
-/datum/wires/proc/can_see_wire_index(mob/user)
+ var/mob/user = usr
+ if(!interactable(user))
+ return
+
+ var/obj/item/I = user.get_active_hand()
+ var/color = lowertext(params["wire"])
+ holder.add_hiddenprint(user)
+
+ switch(action)
+ // Toggles the cut/mend status.
+ if("cut")
+ if(!istype(I, /obj/item/wirecutters) && !user.can_admin_interact())
+ to_chat(user, "You need wirecutters!")
+ return
+
+ if(istype(I))
+ playsound(holder, I.usesound, 20, 1)
+ cut_color(color)
+ return TRUE
+
+ // Pulse a wire.
+ if("pulse")
+ if(!istype(I, /obj/item/multitool) && !user.can_admin_interact())
+ to_chat(user, "You need a multitool!")
+ return
+
+ playsound(holder, 'sound/weapons/empty.ogg', 20, 1)
+ pulse_color(color)
+
+ // If they pulse the electrify wire, call interactable() and try to shock them.
+ if(get_wire(color) == WIRE_ELECTRIFY)
+ interactable(user)
+
+ return TRUE
+
+ // Attach a signaler to a wire.
+ if("attach")
+ if(is_attached(color))
+ var/obj/item/O = detach_assembly(color)
+ if(O)
+ user.put_in_hands(O)
+ return TRUE
+
+ if(!istype(I, /obj/item/assembly/signaler))
+ to_chat(user, "You need a remote signaller!")
+ return
+
+ if(user.drop_item())
+ attach_assembly(color, I)
+ return TRUE
+ else
+ to_chat(user, "[user.get_active_hand()] is stuck to your hand!")
+
+/**
+ * Proc called to determine if the user can see wire define information, such as "Contraband", "Door Bolts", etc.
+ *
+ * If the user is an admin, or has a multitool which reveals wire information in their active hand, the proc returns TRUE.
+ *
+ * Arguments:
+ * * user - the mob who is interacting with the wires.
+ */
+/datum/wires/proc/can_see_wire_info(mob/user)
if(user.can_admin_interact())
return TRUE
else if(istype(user.get_active_hand(), /obj/item/multitool))
var/obj/item/multitool/M = user.get_active_hand()
if(M.shows_wire_information)
return TRUE
-
return FALSE
-/datum/wires/Topic(href, href_list)
- if(..())
- return 1
- var/mob/L = usr
- if(CanUse(L) && href_list["action"])
- var/obj/item/I = L.get_active_hand()
- var/colour = lowertext(href_list["wire"])
- holder.add_hiddenprint(L)
- switch(href_list["action"])
- if("cut") // Toggles the cut/mend status
- if(istype(I, /obj/item/wirecutters) || L.can_admin_interact())
- if(istype(I))
- playsound(holder, I.usesound, 20, 1)
- CutWireColour(colour)
- else
- to_chat(L, "You need wirecutters!")
- if("pulse")
- if(istype(I, /obj/item/multitool) || L.can_admin_interact())
- playsound(holder, 'sound/weapons/empty.ogg', 20, 1)
- PulseColour(colour)
- else
- to_chat(L, "You need a multitool!")
- if("attach")
- if(IsAttached(colour))
- var/obj/item/O = Detach(colour)
- if(O)
- L.put_in_hands(O)
- else
- if(istype(I, /obj/item/assembly/signaler))
- if(L.drop_item())
- Attach(colour, I)
- else
- to_chat(L, "[L.get_active_hand()] is stuck to your hand!")
- else
- to_chat(L, "You need a remote signaller!")
+/**
+ * Base proc, intended to be overwritten. Put wire information you'll see at the botton of the TGUI window here, such as "The red light is blinking".
+ */
+/datum/wires/proc/get_status()
+ return list()
- SSnanoui.update_uis(src)
- return 1
+/**
+ * Clears the `colors` list, and randomizes it to a new set of color-to-wire relations.
+ */
+/datum/wires/proc/shuffle_wires()
+ colors.Cut()
+ randomize()
-//
-// Overridable Procs
-//
+/**
+ * Repairs all cut wires.
+ */
+/datum/wires/proc/repair()
+ cut_wires.Cut()
-// Called when wires cut/mended.
-/datum/wires/proc/UpdateCut(index, mended)
- if(holder)
- SSnanoui.update_uis(holder)
+/**
+ * Adds in dud wires, which do nothing when cut/pulsed.
+ *
+ * Arguments:
+ * * duds - the amount of dud wires to generate.
+ */
+/datum/wires/proc/add_duds(duds)
+ while(duds)
+ var/dud = WIRE_DUD_PREFIX + "[--duds]"
+ if(dud in wires)
+ continue
+ wires += dud
-// Called when wire pulsed. Add code here.
-/datum/wires/proc/UpdatePulsed(index)
- if(holder)
- SSnanoui.update_uis(holder)
+/**
+ * Determines if the passed in wire is a dud or not. Returns TRUE if the wire is a dud, FALSE otherwise.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/is_dud(wire)
+ return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1)
-/datum/wires/proc/CanUse(mob/L)
- return 1
+/**
+ * Returns TRUE if the wire that corresponds to the passed in color is a dud. FALSE otherwise.
+ *
+ * Arugments:
+ * * color - a wire color.
+ */
+/datum/wires/proc/is_dud_color(color)
+ return is_dud(get_wire(color))
-/datum/wires/CanUseTopic(mob/user, datum/topic_state/state)
- if(!holder || !CanUse(user))
- return STATUS_CLOSE
- return ..()
+/**
+ * Gets the wire associated with the color passed in.
+ *
+ * Arugments:
+ * * color - a wire color.
+ */
+/datum/wires/proc/get_wire(color)
+ return colors[color]
-// Example of use:
-/*
+/**
+ * Determines if the passed in wire is cut or not. Returns TRUE if it's cut, FALSE otherwise.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/is_cut(wire)
+ return (wire in cut_wires)
-#define NAME_WIRE_BOLTED 1
-#define NAME_WIRE_SHOCKED 2
-#define NAME_WIRE_SAFETY 4
-#define NAME_WIRE_POWER 8
+/**
+ * Determines if the wire associated with the passed in color, is cut or not. Returns TRUE if it's cut, FALSE otherwise.
+ *
+ * Arugments:
+ * * wire - a wire color.
+ */
+/datum/wires/proc/is_color_cut(color)
+ return is_cut(get_wire(color))
-/datum/wires/door/UpdateCut(var/index, var/mended)
- var/obj/machinery/door/airlock/A = holder
- switch(index)
- if(NAME_WIRE_BOLTED)
- if(!mended)
- A.bolt()
- if(NAME_WIRE_SHOCKED)
- A.shock()
- if(NAME_WIRE_SAFETY)
- A.safety()
+/**
+ * Determines if all of the wires are cut. Returns TRUE they're all cut, FALSE otherwise.
+ */
+/datum/wires/proc/is_all_cut()
+ return (length(cut_wires) == length(wires))
-*/
-
-
-//
-// Helper Procs
-//
-
-/datum/wires/proc/PulseColour(colour)
- PulseIndex(GetIndex(colour))
-
-/datum/wires/proc/PulseIndex(index)
- if(IsIndexCut(index))
- return
- UpdatePulsed(index)
-
-/datum/wires/proc/GetIndex(colour)
- if(wires[colour])
- var/index = wires[colour]
- return index
+/**
+ * Cut or mend a wire. Calls `on_cut()`.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/cut(wire)
+ if(is_cut(wire))
+ cut_wires -= wire
+ on_cut(wire, mend = TRUE)
else
- CRASH("[colour] is not a key in wires.")
+ cut_wires += wire
+ on_cut(wire, mend = FALSE)
-/datum/wires/proc/GetWireName(index)
+/**
+ * Cut the wire which corresponds with the passed in color.
+ *
+ * Arugments:
+ * * color - a wire color.
+ */
+/datum/wires/proc/cut_color(color)
+ cut(get_wire(color))
+
+/**
+ * Cuts a random wire.
+ */
+/datum/wires/proc/cut_random()
+ cut(wires[rand(1, length(wires))])
+
+/**
+ * Cuts all wires.
+ */
+/datum/wires/proc/cut_all()
+ for(var/wire in wires)
+ cut(wire)
+
+/**
+ * Proc called when any wire is cut.
+ *
+ * Base proc, intended to be overriden.
+ * Place an behavior you want to happen when certain wires are cut, into this proc.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT color. For example 'WIRE_ELECTRIFY'.
+ * * mend - TRUE if we're mending the wire. FALSE if we're cutting.
+ */
+/datum/wires/proc/on_cut(wire, mend = FALSE)
return
-//
-// Is Index/Colour Cut procs
-//
+/**
+ * Pulses the given wire. Calls `on_pulse()`.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/pulse(wire)
+ if(is_cut(wire))
+ return
+ on_pulse(wire)
-/datum/wires/proc/IsColourCut(colour)
- var/index = GetIndex(colour)
- return IsIndexCut(index)
+/**
+ * Pulses the wire associated with the given color.
+ *
+ * Arugments:
+ * * wire - a wire color.
+ */
+/datum/wires/proc/pulse_color(color)
+ pulse(get_wire(color))
-/datum/wires/proc/IsIndexCut(index)
- return (index & wires_status)
+/**
+ * Proc called when any wire is pulsed.
+ *
+ * Base proc, intended to be overriden.
+ * Place behavior you want to happen when certain wires are pulsed, into this proc.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT color. For example 'WIRE_ELECTRIFY'.
+ */
+/datum/wires/proc/on_pulse(wire)
+ return
-//
-// Signaller Procs
-//
+/**
+ * Proc called when an attached signaler receives a signal.
+ *
+ * Searches through the `assemblies` list for the wire that the signaler is attached to. Pulses the wire when it's found.
+ *
+ * Arugments:
+ * * S - the attached signaler receiving the signal.
+ */
+/datum/wires/proc/pulse_assembly(obj/item/assembly/signaler/S)
+ for(var/color in assemblies)
+ if(S == assemblies[color])
+ pulse_color(color)
+ return TRUE
-/datum/wires/proc/IsAttached(colour)
- if(signallers[colour])
- return 1
- return 0
+/**
+ * Proc called when a mob tries to attach a signaler to a wire.
+ *
+ * Makes sure that `S` is actually a signaler and that something is not already attached to the wire.
+ * Adds the signaler to the `assemblies` list as a value, with the `color` as a the key.
+ *
+ * Arguments:
+ * * color - the wire color.
+ * * S - the signaler that a mob is trying to attach.
+ */
+/datum/wires/proc/attach_assembly(color, obj/item/assembly/signaler/S)
+ if(S && istype(S) && !is_attached(color))
+ assemblies[color] = S
+ S.forceMove(holder)
+ S.connected = src
+ return S
-/datum/wires/proc/GetAttached(colour)
- if(signallers[colour])
- return signallers[colour]
+/**
+ * Proc called when a mob tries to detach a signaler from a wire.
+ *
+ * First checks if there is a signaler on the wire. If so, removes the signaler, and clears it from `assemblies` list.
+ *
+ * Arguments:
+ * * color - the wire color.
+ */
+/datum/wires/proc/detach_assembly(color)
+ var/obj/item/assembly/signaler/S = get_attached(color)
+ if(S && istype(S))
+ assemblies -= color
+ S.connected = null
+ S.forceMove(holder.drop_location())
+ return S
+
+/**
+ * Gets the signaler attached to the given wire color, if there is one.
+ *
+ * Arguments:
+ * * color - the wire color.
+ */
+/datum/wires/proc/get_attached(color)
+ if(assemblies[color])
+ return assemblies[color]
return null
-/datum/wires/proc/Attach(colour, obj/item/assembly/signaler/S)
- if(colour && S)
- if(!IsAttached(colour))
- signallers[colour] = S
- S.loc = holder
- S.connected = src
- return S
-
-/datum/wires/proc/Detach(colour)
- if(colour)
- var/obj/item/assembly/signaler/S = GetAttached(colour)
- if(S)
- signallers -= colour
- S.connected = null
- S.loc = holder.loc
- return S
-
-
-/datum/wires/proc/Pulse(obj/item/assembly/signaler/S)
-
- for(var/colour in signallers)
- if(S == signallers[colour])
- PulseColour(colour)
- break
-
-
-//
-// Cut Wire Colour/Index procs
-//
-
-/datum/wires/proc/CutWireColour(colour)
- var/index = GetIndex(colour)
- CutWireIndex(index)
-
-/datum/wires/proc/CutWireIndex(index)
- if(IsIndexCut(index))
- wires_status &= ~index
- UpdateCut(index, 1)
- else
- wires_status |= index
- UpdateCut(index, 0)
-
-/datum/wires/proc/RandomCut()
- var/r = rand(1, wires.len)
- CutWireIndex(r)
-
-/datum/wires/proc/CutAll()
- for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i)
- CutWireIndex(i)
-
-/datum/wires/proc/IsAllCut()
- if(wires_status == (1 << wire_count) - 1)
- return 1
- return 0
-
-//
-//Shuffle and Mend
-//
-
-/datum/wires/proc/Shuffle()
- wires_status = 0
- GenerateWires()
+/**
+ * Checks if the given wire has a signaler on it.
+ *
+ * Arguments:
+ * * color - the wire color.
+ */
+/datum/wires/proc/is_attached(color)
+ if(assemblies[color])
+ return TRUE
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index c33e7609685..9a333d268f3 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -50,10 +50,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/space/atmosalert()
return
-/area/space/fire_alert()
+/area/space/firealert(obj/source)
return
-/area/space/fire_reset()
+/area/space/firereset(obj/source)
return
/area/space/readyalert()
diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm
index 8f65ac634f4..723574ee949 100644
--- a/code/game/area/ai_monitored.dm
+++ b/code/game/area/ai_monitored.dm
@@ -1,24 +1,30 @@
/area/ai_monitored
name = "AI Monitored Area"
- var/obj/machinery/camera/motioncamera = null
+ var/list/motioncameras = list()
+ var/list/motionTargets = list()
-
-/area/ai_monitored/LateInitialize()
+/area/ai_monitored/Initialize(mapload)
. = ..()
- // locate and store the motioncamera
- for(var/obj/machinery/camera/M in src)
- if(M.isMotion())
- motioncamera = M
- M.area_motion = src
- break
+ if(mapload)
+ for(var/obj/machinery/camera/M in src)
+ if(M.isMotion())
+ motioncameras.Add(M)
+ M.set_area_motion(src)
/area/ai_monitored/Entered(atom/movable/O)
..()
- if(ismob(O) && motioncamera)
- motioncamera.newTarget(O)
+ if(ismob(O) && length(motioncameras))
+ for(var/X in motioncameras)
+ var/obj/machinery/camera/cam = X
+ cam.newTarget(O)
+ return
/area/ai_monitored/Exited(atom/movable/O)
- if(ismob(O) && motioncamera)
- motioncamera.lostTarget(O)
+ ..()
+ if(ismob(O) && length(motioncameras))
+ for(var/X in motioncameras)
+ var/obj/machinery/camera/cam = X
+ cam.lostTargetRef(O.UID())
+ return
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index da80a6e775c..de913ac9f91 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -57,6 +57,11 @@
var/list/ambientsounds = GENERIC_SOUNDS
+ var/list/firedoors
+ var/list/cameras
+ var/list/firealarms
+ var/firedoors_last_closed_on = 0
+
var/fast_despawn = FALSE
var/can_get_auto_cryod = TRUE
var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority.
@@ -125,35 +130,6 @@
cameras += C
return cameras
-
-/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE)
- if(report_alerts)
- if(danger_level == ATMOS_ALARM_NONE)
- SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source)
- else
- SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level)
-
- //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care.
- for(var/obj/machinery/alarm/AA in src)
- if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force)
- danger_level = max(danger_level, AA.danger_level)
-
- if(danger_level != atmosalm)
- if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING)
- //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise
- air_doors_open()
- else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER)
- air_doors_close()
-
- atmosalm = danger_level
- for(var/obj/machinery/alarm/AA in src)
- AA.update_icon()
-
- GLOB.air_alarm_repository.update_cache(src)
- return 1
- GLOB.air_alarm_repository.update_cache(src)
- return 0
-
/area/proc/air_doors_close()
if(!air_doors_activated)
air_doors_activated = TRUE
@@ -179,44 +155,151 @@
D.open()
-/area/proc/fire_alert()
- if(!fire)
- fire = 1 //used for firedoor checks
- updateicon()
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- air_doors_close()
+/area/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
-/area/proc/fire_reset()
- if(fire)
- fire = 0 //used for firedoor checks
- updateicon()
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- air_doors_open()
+/**
+ * Generate a power alert for this area
+ *
+ * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
+ */
+/area/proc/poweralert(state, obj/source)
+ if(state != poweralm)
+ poweralm = state
+ if(istype(source)) //Only report power alarms on the z-level where the source is located.
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ if(state)
+ C.network -= "Power Alarms"
+ else
+ C.network |= "Power Alarms"
- return
+ if(state)
+ SSalarm.cancelAlarm("Power", src, source)
+ else
+ SSalarm.triggerAlarm("Power", src, cameras, source)
-/area/proc/burglaralert(var/obj/trigger)
- if(always_unpowered == 1) //no burglar alarms in space/asteroid
+/**
+ * Generate an atmospheric alert for this area
+ *
+ * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
+ */
+/area/proc/atmosalert(danger_level, obj/source)
+ if(danger_level != atmosalm)
+ if(danger_level == ATMOS_ALARM_DANGER)
+
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network |= "Atmosphere Alarms"
+
+
+ SSalarm.triggerAlarm("Atmosphere", src, cameras, source)
+
+ else if(atmosalm == ATMOS_ALARM_DANGER)
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network -= "Atmosphere Alarms"
+
+ SSalarm.cancelAlarm("Atmosphere", src, source)
+
+ atmosalm = danger_level
+ return TRUE
+ return FALSE
+
+/**
+ * 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
+ 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 ? FD_OPEN : FD_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(always_unpowered) //no fire alarms in space/asteroid
return
- //Trigger alarm effect
- set_fire_alarm_effect()
+ if(!fire)
+ set_fire_alarm_effect()
+ ModifyFiredoors(FALSE)
+ for(var/item in firealarms)
+ var/obj/machinery/firealarm/F = item
+ F.update_icon()
- //Lockdown airlocks
- for(var/obj/machinery/door/airlock/A in src)
- spawn(0)
- A.close()
- if(A.density)
- A.lock()
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network |= "Fire Alarms"
- SSalarms.burglar_alarm.triggerAlarm(src, trigger)
- spawn(600)
- SSalarms.burglar_alarm.clearAlarm(src, trigger)
+ SSalarm.triggerAlarm("Fire", src, cameras, source)
-/area/proc/set_fire_alarm_effect()
- fire = 1
- updateicon()
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ 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)
+ if(fire)
+ unset_fire_alarm_effects()
+ ModifyFiredoors(TRUE)
+ for(var/item in firealarms)
+ var/obj/machinery/firealarm/F = item
+ F.update_icon()
+
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network -= "Fire Alarms"
+
+ SSalarm.cancelAlarm("Fire", src, source)
+
+ STOP_PROCESSING(SSobj, src)
+
+/**
+ * If 100 ticks has elapsed, toggle all the firedoors closed again
+ */
+/area/process()
+ if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds
+ ModifyFiredoors(FALSE)
+
+/**
+ * Close and lock a door passed into this proc
+ *
+ * Does this need to exist on area? probably not
+ */
+/area/proc/close_and_lock_door(obj/machinery/door/DOOR)
+ set waitfor = FALSE
+ DOOR.close()
+ if(DOOR.density)
+ DOOR.lock()
/area/proc/readyalert()
if(!eject)
@@ -240,13 +323,62 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
updateicon()
+/**
+ * Raise a burglar alert for this area
+ *
+ * Close and locks all doors in the area and alerts silicon mobs of a break in
+ *
+ * Alarm auto resets after 600 ticks
+ */
+/area/proc/burglaralert(obj/trigger)
+ if(always_unpowered) //no burglar alarms in space/asteroid
+ return
+
+ //Trigger alarm effect
+ set_fire_alarm_effect()
+ //Lockdown airlocks
+ for(var/obj/machinery/door/DOOR in src)
+ close_and_lock_door(DOOR)
+
+ if(SSalarm.triggerAlarm("Burglar", src, cameras, trigger))
+ //Cancel silicon alert after 1 minute
+ addtimer(CALLBACK(SSalarm, /datum/controller/subsystem/alarm.proc/cancelAlarm, "Burglar", src, trigger), 600)
+
+/**
+ * Trigger the fire alarm visual affects in an area
+ *
+ * Updates the fire light on fire alarms in the area and sets all lights to emergency mode
+ */
+/area/proc/set_fire_alarm_effect()
+ fire = TRUE
+ 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()
+
+/**
+ * unset the fire alarm visual affects in an area
+ *
+ * Updates the fire light on fire alarms in the area and sets all lights to emergency mode
+ */
+/area/proc/unset_fire_alarm_effects()
+ fire = FALSE
+ 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()
+
/area/proc/updateicon()
- if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
- if(fire && !eject && !party)
+ if((eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
+ if(!eject && !party)
icon_state = "red"
- else if(!fire && eject && !party)
+ else if(eject && !party)
icon_state = "red"
- else if(party && !fire && !eject)
+ else if(party && !eject)
icon_state = "party"
else
icon_state = "blue-red"
diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm
index 8b8a86009b9..a887959db48 100644
--- a/code/game/dna/genes/disabilities.dm
+++ b/code/game/dna/genes/disabilities.dm
@@ -249,14 +249,13 @@
block = GLOB.wingdingsblock
/datum/dna/gene/disability/wingdings/OnSay(mob/M, message)
- var/list/chars = string2charlist(message)
var/garbled_message = ""
- for(var/C in chars)
- if(C in GLOB.alphabet_uppercase)
+ for(var/i in 1 to length(message))
+ if(message[i] in GLOB.alphabet_uppercase)
garbled_message += pick(GLOB.alphabet_uppercase)
- else if(C in GLOB.alphabet)
+ else if(message[i] in GLOB.alphabet)
garbled_message += pick(GLOB.alphabet)
else
- garbled_message += C
+ garbled_message += message[i]
message = garbled_message
return message
diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm
index 9c33ffaaca5..b3b4000bbf1 100644
--- a/code/game/gamemodes/changeling/evolution_menu.dm
+++ b/code/game/gamemodes/changeling/evolution_menu.dm
@@ -370,16 +370,11 @@ GLOBAL_LIST_EMPTY(sting_paths)
mind.changeling.purchasedpowers += path
path.on_purchase(src)
else //for respec
- var/datum/action/changeling/hivemind_upload/S1 = new
+ var/datum/action/changeling/hivemind_pick/S1 = new
if(!mind.changeling.has_sting(S1))
mind.changeling.purchasedpowers+=S1
S1.Grant(src)
- var/datum/action/changeling/hivemind_download/S2 = new
- if(!mind.changeling.has_sting(S2))
- mind.changeling.purchasedpowers+=S2
- S2.Grant(src)
-
var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste
mind.changeling.absorbed_dna |= C.dna.Clone()
mind.changeling.trim_dna()
diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm
index 98336608da4..18a5477fd3f 100644
--- a/code/game/gamemodes/changeling/powers/hivemind.dm
+++ b/code/game/gamemodes/changeling/powers/hivemind.dm
@@ -12,27 +12,36 @@
var/datum/changeling/changeling=user.mind.changeling
changeling.changeling_speak = 1
to_chat(user, "Use say \":g message\" to communicate with the other changelings.")
- var/datum/action/changeling/hivemind_upload/S1 = new
+ var/datum/action/changeling/hivemind_pick/S1 = new
if(!changeling.has_sting(S1))
changeling.purchasedpowers+=S1
S1.Grant(user)
- var/datum/action/changeling/hivemind_download/S2 = new
- if(!changeling.has_sting(S2))
- S2.Grant(user)
- changeling.purchasedpowers+=S2
return
// HIVE MIND UPLOAD/DOWNLOAD DNA
GLOBAL_LIST_EMPTY(hivemind_bank)
-/datum/action/changeling/hivemind_upload
+/datum/action/changeling/hivemind_pick
name = "Hive Channel DNA"
- desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it. Costs 10 chemicals."
- button_icon_state = "hivemind_channel"
+ desc = "Allows us to upload or absorb DNA in the airwaves. Does not count towards absorb objectives. Costs 10 chemicals."
+ button_icon_state = "hive_absorb"
chemical_cost = 10
dna_cost = -1
-/datum/action/changeling/hivemind_upload/sting_action(var/mob/user)
+/datum/action/changeling/hivemind_pick/sting_action(mob/user)
+ var/datum/changeling/changeling = user.mind.changeling
+ var/channel_pick = alert("Upload or Absorb DNA?", "Channel Select", "Upload", "Absorb")
+
+ if(channel_pick == "Upload")
+ dna_upload(user)
+ if(channel_pick == "Absorb")
+ if(changeling.using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it.
+ to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.")
+ return
+ else
+ dna_absorb(user)
+
+/datum/action/changeling/proc/dna_upload(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/dna/DNA in (changeling.absorbed_dna+changeling.protected_dna))
@@ -56,23 +65,7 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
feedback_add_details("changeling_powers","HU")
return 1
-/datum/action/changeling/hivemind_download
- name = "Hive Absorb DNA"
- desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives. Costs 10 chemicals."
- button_icon_state = "hive_absorb"
- chemical_cost = 10
- dna_cost = -1
-
-/datum/action/changeling/hivemind_download/can_sting(var/mob/living/carbon/user)
- if(!..())
- return
- var/datum/changeling/changeling = user.mind.changeling
- if(changeling.using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it.
- to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.")
- return
- return 1
-
-/datum/action/changeling/hivemind_download/sting_action(var/mob/user)
+/datum/action/changeling/proc/dna_absorb(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/dna/DNA in GLOB.hivemind_bank)
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index f1f3bc1c72c..a68e4c1e9ca 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -177,26 +177,37 @@
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/special_overlays()
return mutable_appearance('icons/effects/cult_effects.dmi', shield_state, MOB_LAYER + 0.01)
-/obj/item/clothing/suit/hooded/cultrobes/berserker
+/obj/item/clothing/suit/hooded/cultrobes/flagellant_robe
name = "flagellant's robes"
desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
- icon_state = "hardsuit-berserker"
- item_state = "hardsuit-berserker"
+ icon_state = "flagellantrobe"
+ item_state = "flagellantrobe"
flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/tome,/obj/item/melee/cultblade)
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0)
slowdown = -1
- hoodtype = /obj/item/clothing/head/hooded/berserkerhood
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/species/vox/suit.dmi',
+ "Drask" = 'icons/mob/species/drask/suit.dmi',
+ "Grey" = 'icons/mob/species/grey/suit.dmi'
+ )
+ hoodtype = /obj/item/clothing/head/hooded/flagellant_hood
-/obj/item/clothing/head/hooded/berserkerhood
+/obj/item/clothing/head/hooded/flagellant_hood
name = "flagellant's robes"
desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
- icon_state = "culthood"
+ icon_state = "flagellanthood"
+ item_state = "flagellanthood"
flags_inv = HIDEFACE
flags_cover = HEADCOVERSEYES
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/species/vox/head.dmi',
+ "Drask" = 'icons/mob/species/drask/head.dmi',
+ "Grey" = 'icons/mob/species/grey/head.dmi'
+ )
/obj/item/whetstone/cult
name = "eldritch whetstone"
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 0e96dd92062..c06913c1abe 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -120,7 +120,7 @@
selection_prompt = "You study the schematics etched on the forge..."
selection_title = "Forge"
creation_message = "You work the forge as dark knowledge guides your hands, creating %ITEM%!"
- choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/berserker, \
+ choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe, \
"Cultist Hardsuit" = /obj/item/storage/box/cult)
/obj/structure/cult/functional/forge/New()
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index 526a2c01083..9f5db8d5dae 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -616,7 +616,7 @@
active = FALSE
return
var/turf/T = get_turf(owner_AI.eyeobj)
- new /obj/machinery/transformer/conveyor(T)
+ new /obj/machinery/transformer(T, owner_AI)
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
owner_AI.can_shunt = FALSE
to_chat(owner, "You are no longer able to shunt your core to APCs.")
@@ -781,3 +781,17 @@
if(AI.eyeobj)
AI.eyeobj.relay_speech = TRUE
+/datum/AI_Module/large/cameracrack
+ module_name = "Core Camera Cracker"
+ mod_pick_name = "cameracrack"
+ description = "By shortcirucuting the camera network chip, it overheats, preventing the camera console from using your internal camera."
+ cost = 10
+ one_purchase = TRUE
+ upgrade = TRUE
+ unlock_text = "Network chip short circuited. Internal camera disconected from network. Minimal damage to other internal components."
+ unlock_sound = 'sound/items/wirecutter.ogg'
+
+/datum/AI_Module/large/cameracrack/upgrade(mob/living/silicon/ai/AI)
+ if(AI.builtInCamera)
+ QDEL_NULL(AI.builtInCamera)
+
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index 8e38f106cc7..c73293da519 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -42,6 +42,7 @@ GLOBAL_VAR(bomb_set)
GLOB.poi_list |= src
/obj/machinery/nuclearbomb/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
GLOB.poi_list.Remove(src)
return ..()
@@ -407,6 +408,20 @@ GLOBAL_VAR(bomb_set)
return
return
+/obj/machinery/nuclearbomb/proc/reset_lighthack_callback()
+ lighthack = !lighthack
+
+/obj/machinery/nuclearbomb/proc/reset_safety_callback()
+ safety = !safety
+ if(safety == 1)
+ if(!is_syndicate)
+ set_security_level(previous_level)
+ visible_message("The [src] quiets down.")
+ if(!lighthack)
+ if(icon_state == "nuclearbomb2")
+ icon_state = "nuclearbomb1"
+ else
+ visible_message("The [src] emits a quiet whirling noise!")
//==========DAT FUKKEN DISK===============
/obj/item/disk/nuclear
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 9fda2cfe45c..951f2a1e9cf 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -222,6 +222,7 @@
first_run()
/obj/machinery/alarm/Destroy()
+ SStgui.close_uis(wires)
GLOB.air_alarms -= src
if(SSradio)
SSradio.remove_object(src, frequency)
@@ -314,7 +315,7 @@
temperature_dangerlevel
)
- if(old_danger_level!=danger_level)
+ if(old_danger_level != danger_level)
apply_danger_level()
if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05)
@@ -534,28 +535,35 @@
"checks"= 0,
))
-/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level)
- if(report_danger_level && alarm_area.atmosalert(new_danger_level, src))
- post_alert(new_danger_level)
+/obj/machinery/alarm/proc/apply_danger_level()
+ var/new_area_danger_level = ATMOS_ALARM_NONE
+ for(var/obj/machinery/alarm/AA in alarm_area)
+ if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
+ new_area_danger_level = max(new_area_danger_level, AA.danger_level)
+ if(alarm_area.atmosalert(new_area_danger_level, src)) //if area was in normal state or if area was in alert state
+ post_alert(new_area_danger_level)
update_icon()
/obj/machinery/alarm/proc/post_alert(alert_level)
+ if(!report_danger_level)
+ return
var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency)
+
if(!frequency)
return
var/datum/signal/alert_signal = new
alert_signal.source = src
alert_signal.transmission_method = 1
- alert_signal.data["zone"] = alarm_area.name
+ alert_signal.data["zone"] = get_area_name(src, TRUE)
alert_signal.data["type"] = "Atmospheric"
- if(alert_level==2)
+ if(alert_level == ATMOS_ALARM_DANGER)
alert_signal.data["alert"] = "severe"
- else if(alert_level==1)
+ else if(alert_level == ATMOS_ALARM_WARNING)
alert_signal.data["alert"] = "minor"
- else if(alert_level==0)
+ else if(alert_level == ATMOS_ALARM_NONE)
alert_signal.data["alert"] = "clear"
frequency.post_signal(src, alert_signal)
@@ -889,14 +897,14 @@
if(href_list["atmos_alarm"])
if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src))
- apply_danger_level(ATMOS_ALARM_DANGER)
+ post_alert(ATMOS_ALARM_DANGER)
alarmActivated = 1
update_icon()
return 1
if(href_list["atmos_reset"])
if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE))
- apply_danger_level(ATMOS_ALARM_NONE)
+ post_alert(ATMOS_ALARM_NONE)
alarmActivated = 0
update_icon()
return 1
@@ -951,7 +959,7 @@
to_chat(user, "It does nothing")
return
else
- if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN))
+ if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.")
updateUsrDialog()
@@ -1030,7 +1038,7 @@
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
- if(wires.wires_status == 31) // all wires cut
+ if(wires.is_all_cut()) // all wires cut
var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location())
new_coil.amount = 5
buildstage = AIR_ALARM_BUILDING
@@ -1076,6 +1084,15 @@
if(buildstage < 1)
. += "The circuit is missing."
+/obj/machinery/alarm/proc/unshort_callback()
+ if(shorted)
+ shorted = FALSE
+ update_icon()
+
+/obj/machinery/alarm/proc/enable_ai_control_callback()
+ if(aidisabled)
+ aidisabled = FALSE
+
/obj/machinery/alarm/all_access
name = "all-access air alarm"
desc = "This particular atmos control unit appears to have no access restrictions."
diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm
index 7af7751ff2c..d7a49e40d4b 100644
--- a/code/game/machinery/atmoalter/scrubber.dm
+++ b/code/game/machinery/atmoalter/scrubber.dm
@@ -187,24 +187,24 @@
icon_state = "scrubber:0"
/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
- if(istype(W, /obj/item/wrench))
- if(stationary)
- to_chat(user, "The bolts are too tight for you to unscrew!")
- return
- if(on)
- to_chat(user, "Turn it off first!")
- return
-
- anchored = !anchored
- playsound(loc, W.usesound, 50, 1)
- to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].")
- return
-
if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1)
atmosanalyzer_scan(air_contents, user)
return
return ..()
+/obj/machinery/portable_atmospherics/scrubber/huge/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ if(stationary)
+ to_chat(user, "The bolts are too tight for you to unscrew!")
+ return
+ if(on)
+ to_chat(user, "Turn it off first!")
+ return
+ if(!I.use_tool(src, user, 0, volume = I.tool_volume))
+ return
+ anchored = !anchored
+ to_chat(user, "You [anchored ? "wrench" : "unwrench"] [src].")
+
/obj/machinery/portable_atmospherics/scrubber/huge/stationary
name = "Stationary Air Scrubber"
stationary = 1
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index c22922e4d14..5c1b84d8cba 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -65,6 +65,7 @@
RefreshParts()
/obj/machinery/autolathe/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.retrieve_all()
@@ -467,3 +468,15 @@
for(var/datum/design/D in files.known_designs)
if("hacked" in D.category)
files.known_designs -= D.id
+
+/obj/machinery/autolathe/proc/check_hacked_callback()
+ if(!wires.is_cut(WIRE_AUTOLATHE_HACK))
+ adjust_hacked(FALSE)
+
+/obj/machinery/autolathe/proc/check_electrified_callback()
+ if(!wires.is_cut(WIRE_ELECTRIFY))
+ shocked = FALSE
+
+/obj/machinery/autolathe/proc/check_disabled_callback()
+ if(!wires.is_cut(WIRE_AUTOLATHE_DISABLE))
+ disabled = FALSE
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index b66cad8d328..4e185feb4a8 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -28,13 +28,14 @@
var/view_range = 7
var/short_range = 2
+ var/alarm_on = FALSE
var/busy = FALSE
var/emped = FALSE //Number of consecutive EMP's on this camera
var/in_use_lights = 0 // TO BE IMPLEMENTED
var/toggle_sound = 'sound/items/wirecutter.ogg'
-/obj/machinery/camera/Initialize()
+/obj/machinery/camera/Initialize(mapload)
. = ..()
wires = new(src)
assembly = new(src)
@@ -44,11 +45,17 @@
GLOB.cameranet.cameras += src
GLOB.cameranet.addCamera(src)
+ if(isturf(loc))
+ LAZYADD(myArea.cameras, UID())
if(is_station_level(z) && prob(3) && !start_active)
toggle_cam(null, FALSE)
- wires.CutAll()
+ wires.cut_all()
+
+/obj/machinery/camera/proc/set_area_motion(area/A)
+ area_motion = A
/obj/machinery/camera/Destroy()
+ SStgui.close_uis(wires)
toggle_cam(null, FALSE) //kick anyone viewing out
QDEL_NULL(assembly)
if(istype(bug))
@@ -59,10 +66,14 @@
QDEL_NULL(wires)
GLOB.cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
GLOB.cameranet.cameras -= src
+ if(isarea(myArea))
+ LAZYREMOVE(myArea.cameras, UID())
var/area/ai_monitored/A = get_area(src)
if(istype(A))
- A.motioncamera = null
+ A.motioncameras -= src
area_motion = null
+ cancelCameraAlarm()
+ cancelAlarm()
return ..()
/obj/machinery/camera/emp_act(severity)
@@ -252,7 +263,7 @@
if(status && !(flags & NODECONSTRUCT))
triggerCameraAlarm()
toggle_cam(null, FALSE)
- wires.CutAll()
+ wires.cut_all()
/obj/machinery/camera/deconstruct(disassembled = TRUE)
if(!(flags & NODECONSTRUCT))
@@ -282,9 +293,16 @@
status = !status
if(can_use())
GLOB.cameranet.addCamera(src)
+ if(isturf(loc))
+ myArea = get_area(src)
+ LAZYADD(myArea.cameras, UID())
+ else
+ myArea = null
else
set_light(0)
GLOB.cameranet.removeCamera(src)
+ if(isarea(myArea))
+ LAZYREMOVE(myArea.cameras, UID())
GLOB.cameranet.updateChunk(x, y, z)
var/change_msg = "deactivates"
if(status)
@@ -313,12 +331,12 @@
to_chat(O, "The screen bursts into static.")
/obj/machinery/camera/proc/triggerCameraAlarm()
- if(is_station_contact(z))
- SSalarms.camera_alarm.triggerAlarm(loc, src)
+ alarm_on = TRUE
+ SSalarm.triggerAlarm("Camera", get_area(src), list(UID()), src)
/obj/machinery/camera/proc/cancelCameraAlarm()
- if(is_station_contact(z))
- SSalarms.camera_alarm.clearAlarm(loc, src)
+ alarm_on = FALSE
+ SSalarm.cancelAlarm("Camera", get_area(src), src)
/obj/machinery/camera/proc/can_use()
if(!status)
@@ -414,8 +432,8 @@
/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets.
var/turf/prev_turf
-/obj/machinery/camera/portable/New()
- ..()
+/obj/machinery/camera/portable/Initialize(mapload)
+ . = ..()
assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed.
assembly.anchored = 0
assembly.update_icon()
diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm
index 16fb6eca204..7d33361e4b9 100644
--- a/code/game/machinery/camera/motion.dm
+++ b/code/game/machinery/camera/motion.dm
@@ -1,60 +1,60 @@
/obj/machinery/camera
-
- var/list/motionTargets = list()
+ var/list/localMotionTargets = list()
var/detectTime = 0
var/area/ai_monitored/area_motion = null
- var/alarm_delay = 100
-
+ var/alarm_delay = 30 // Don't forget, there's another 3 seconds in queueAlarm()
/obj/machinery/camera/process()
// motion camera event loop
- if(stat & (EMPED|NOPOWER))
- return
if(!isMotion())
. = PROCESS_KILL
return
+ if(stat & (EMPED|NOPOWER))
+ return
if(detectTime > 0)
var/elapsed = world.time - detectTime
if(elapsed > alarm_delay)
triggerAlarm()
else if(detectTime == -1)
- for(var/mob/target in motionTargets)
- if(target.stat == 2) lostTarget(target)
- // If not detecting with motion camera...
- if(!area_motion)
- // See if the camera is still in range
- if(!in_range(src, target))
- // If they aren't in range, lose the target.
- lostTarget(target)
+ for(var/thing in getTargetList())
+ var/mob/target = locateUID(thing)
+ if(QDELETED(target) || target.stat == DEAD || (!area_motion && !in_range(src, target)))
+ //If not part of a monitored area and the camera is not in range or the target is dead
+ lostTargetRef(thing)
-/obj/machinery/camera/proc/newTarget(var/mob/target)
- if(istype(target, /mob/living/silicon/ai)) return 0
+/obj/machinery/camera/proc/getTargetList()
+ if(area_motion)
+ return area_motion.motionTargets
+ return localMotionTargets
+
+/obj/machinery/camera/proc/newTarget(mob/target)
+ if(isAI(target))
+ return FALSE
if(detectTime == 0)
detectTime = world.time // start the clock
- if(!(target in motionTargets))
- motionTargets += target
- return 1
+ var/list/targets = getTargetList()
+ targets |= target.UID()
+ return TRUE
-/obj/machinery/camera/proc/lostTarget(var/mob/target)
- if(target in motionTargets)
- motionTargets -= target
- if(motionTargets.len == 0)
+/obj/machinery/camera/proc/lostTargetRef(uid)
+ var/list/targets = getTargetList()
+ targets -= uid
+ if(length(targets))
cancelAlarm()
/obj/machinery/camera/proc/cancelAlarm()
- if(!status || (stat & NOPOWER))
- return FALSE
- if(detectTime == -1 && is_station_contact(z))
- SSalarms.motion_alarm.clearAlarm(loc, src)
+ if(detectTime == -1)
+ if(status)
+ SSalarm.cancelAlarm("Motion", get_area(src), src)
detectTime = 0
return TRUE
/obj/machinery/camera/proc/triggerAlarm()
- if(!status || (stat & NOPOWER))
+ if(!detectTime)
return FALSE
- if(!detectTime || !is_station_contact(z))
- return FALSE
- SSalarms.motion_alarm.triggerAlarm(loc, src)
+ if(status)
+ SSalarm.triggerAlarm("Motion", get_area(src), list(UID()), src)
+ visible_message("A red light flashes on the [src]!")
detectTime = -1
return TRUE
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index b7ae75cd13f..33c970639e5 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -2,7 +2,7 @@
// EMP
-/obj/machinery/camera/emp_proof/Initialize()
+/obj/machinery/camera/emp_proof/Initialize(mapload)
. = ..()
upgradeEmpProof()
@@ -11,19 +11,23 @@
/obj/machinery/camera/xray
icon_state = "xraycam" // Thanks to Krutchen for the icons.
-/obj/machinery/camera/xray/Initialize()
+/obj/machinery/camera/xray/Initialize(mapload)
. = ..()
upgradeXRay()
// MOTION
+/obj/machinery/camera/motion
+ name = "motion-sensitive security camera"
-/obj/machinery/camera/motion/Initialize()
+/obj/machinery/camera/motion/Initialize(mapload)
. = ..()
upgradeMotion()
// ALL UPGRADES
+/obj/machinery/camera/all
+ icon_state = "xraycamera" //mapping icon.
-/obj/machinery/camera/all/Initialize()
+/obj/machinery/camera/all/Initialize(mapload)
. = ..()
upgradeEmpProof()
upgradeXRay()
@@ -78,6 +82,10 @@
// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list.
/obj/machinery/camera/proc/upgradeMotion()
+ if(isMotion())
+ return
+ if(name == initial(name))
+ name = "motion-sensitive security camera"
assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly))
setPowerUsage()
// Add it to machines that process
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index 123cee3ae4e..0be4aa0449b 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -1,84 +1,90 @@
-GLOBAL_LIST_EMPTY(priority_air_alarms)
-GLOBAL_LIST_EMPTY(minor_air_alarms)
-
-
/obj/machinery/computer/atmos_alert
name = "atmospheric alert computer"
desc = "Used to access the station's atmospheric sensors."
circuit = /obj/item/circuitboard/atmos_alert
+ var/ui_x = 350
+ var/ui_y = 300
icon_keyboard = "atmos_key"
icon_screen = "alert:0"
light_color = LIGHT_COLOR_CYAN
+ var/list/priority_alarms = list()
+ var/list/minor_alarms = list()
+ var/receive_frequency = ATMOS_FIRE_FREQ
+ var/datum/radio_frequency/radio_connection
-/obj/machinery/computer/atmos_alert/New()
- ..()
- SSalarms.atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/.proc/update_icon)
+/obj/machinery/computer/atmos_alert/Initialize(mapload)
+ . = ..()
+ set_frequency(receive_frequency)
/obj/machinery/computer/atmos_alert/Destroy()
- SSalarms.atmosphere_alarm.unregister(src)
- return ..()
+ SSradio.remove_object(src, receive_frequency)
+ return ..()
/obj/machinery/computer/atmos_alert/attack_hand(mob/user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/atmos_alert/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_alert.tmpl", src.name, 500, 500)
+ ui = new(user, src, ui_key, "AtmosAlertConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/computer/atmos_alert/ui_data(mob/user, datum/topic_state/state)
- var/data[0]
- var/major_alarms[0]
- var/minor_alarms[0]
+/obj/machinery/computer/atmos_alert/tgui_data(mob/user)
+ var/list/data = list()
- for(var/datum/alarm/alarm in SSalarms.atmosphere_alarm.major_alarms())
- major_alarms[++major_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]")
-
- for(var/datum/alarm/alarm in SSalarms.atmosphere_alarm.minor_alarms())
- minor_alarms[++minor_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]")
-
- data["priority_alarms"] = major_alarms
- data["minor_alarms"] = minor_alarms
+ data["priority"] = list()
+ for(var/zone in priority_alarms)
+ data["priority"] |= zone
+ data["minor"] = list()
+ for(var/zone in minor_alarms)
+ data["minor"] |= zone
return data
-/obj/machinery/computer/atmos_alert/update_icon()
- var/list/alarms = SSalarms.atmosphere_alarm.major_alarms()
- if(alarms.len)
- icon_screen = "alert:2"
- else
- alarms = SSalarms.atmosphere_alarm.minor_alarms()
- if(alarms.len)
- icon_screen = "alert:1"
- else
- icon_screen = "alert:0"
- ..()
-
-/obj/machinery/computer/atmos_alert/Topic(href, href_list)
+/obj/machinery/computer/atmos_alert/tgui_act(action, params)
if(..())
- return 1
+ return
+ switch(action)
+ if("clear")
+ var/zone = params["zone"]
+ if(zone in priority_alarms)
+ to_chat(usr, "Priority alarm for [zone] cleared.")
+ priority_alarms -= zone
+ . = TRUE
+ if(zone in minor_alarms)
+ to_chat(usr, "Minor alarm for [zone] cleared.")
+ minor_alarms -= zone
+ . = TRUE
+ update_icon()
- if(href_list["clear_alarm"])
- var/datum/alarm/alarm = locate(href_list["clear_alarm"]) in SSalarms.atmosphere_alarm.alarms
- if(alarm)
- for(var/datum/alarm_source/alarm_source in alarm.sources)
- var/obj/machinery/alarm/air_alarm = alarm_source.source
- if(istype(air_alarm))
- var/list/new_ref = list("atmos_reset" = 1)
- air_alarm.Topic(href, new_ref, state = GLOB.air_alarm_topic)
- update_icon()
- return 1
+/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, receive_frequency)
+ receive_frequency = new_frequency
+ radio_connection = SSradio.add_object(src, receive_frequency, RADIO_ATMOSIA)
-GLOBAL_DATUM_INIT(air_alarm_topic, /datum/topic_state/air_alarm_topic, new)
+/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal)
+ if(!signal)
+ return
-/datum/topic_state/air_alarm_topic/href_list(var/mob/user)
- var/list/extra_href = list()
- extra_href["remote_connection"] = 1
- extra_href["remote_access"] = 1
+ var/zone = signal.data["zone"]
+ var/severity = signal.data["alert"]
- return extra_href
+ if(!zone || !severity)
+ return
-/datum/topic_state/air_alarm_topic/can_use_topic(var/src_object, var/mob/user)
- return STATUS_INTERACTIVE
+ minor_alarms -= zone
+ priority_alarms -= zone
+ if(severity == "severe")
+ priority_alarms += zone
+ else if(severity == "minor")
+ minor_alarms += zone
+ update_icon()
+
+/obj/machinery/computer/atmos_alert/update_icon()
+ if(length(priority_alarms))
+ icon_screen = "alert:2"
+ else if(length(minor_alarms))
+ icon_screen = "alert:1"
+ else
+ icon_screen = "alert:0"
+ ..()
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index b502d6e5441..e91e631cc62 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -165,12 +165,9 @@
/obj/item/circuitboard/stationalert_engineering
name = "Circuit Board (Station Alert Console (Engineering))"
build_path = /obj/machinery/computer/station_alert
-/obj/item/circuitboard/stationalert_security
- name = "Circuit Board (Station Alert Console (Security))"
+/obj/item/circuitboard/stationalert
+ name = "Circuit Board (Station Alert Console)"
build_path = /obj/machinery/computer/station_alert
-/obj/item/circuitboard/stationalert_all
- name = "Circuit Board (Station Alert Console (All))"
- build_path = /obj/machinery/computer/station_alert/all
/obj/item/circuitboard/atmos_alert
name = "Circuit Board (Atmospheric Alert Computer)"
build_path = /obj/machinery/computer/atmos_alert
@@ -236,7 +233,10 @@
/obj/item/circuitboard/brigcells
name = "Circuit board (Brig Cell Control)"
build_path = /obj/machinery/computer/brigcells
-
+/obj/item/circuitboard/sm_monitor
+ name = "Circuit board (Supermatter Monitoring Console)"
+ build_path = /obj/machinery/computer/sm_monitor
+ origin_tech = "programming=2;powerstorage=2"
// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever
/obj/item/circuitboard/rdconsole
diff --git a/code/game/machinery/computer/sm_monitor.dm b/code/game/machinery/computer/sm_monitor.dm
new file mode 100644
index 00000000000..f2e01e6c4fa
--- /dev/null
+++ b/code/game/machinery/computer/sm_monitor.dm
@@ -0,0 +1,147 @@
+/obj/machinery/computer/sm_monitor
+ name = "supermatter monitoring console"
+ desc = "Used to monitor supermatter shards."
+ icon_keyboard = "power_key"
+ icon_screen = "smmon_0"
+ circuit = /obj/item/circuitboard/sm_monitor
+ light_color = LIGHT_COLOR_YELLOW
+ /// Cache-list of all supermatter shards
+ var/list/supermatters
+ /// Last status of the active supermatter for caching purposes
+ var/last_status
+ /// Reference to the active shard
+ var/obj/machinery/power/supermatter_shard/active
+
+/obj/machinery/computer/sm_monitor/Destroy()
+ active = null
+ return ..()
+
+/obj/machinery/computer/sm_monitor/attack_ai(mob/user)
+ attack_hand(user)
+
+/obj/machinery/computer/sm_monitor/attack_hand(mob/user)
+ add_fingerprint(user)
+ if(stat & (BROKEN|NOPOWER))
+ return
+ tgui_interact(user)
+
+/obj/machinery/computer/sm_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "SupermatterMonitor", name, 600, 325, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/sm_monitor/tgui_data(mob/user)
+ var/list/data = list()
+
+ if(istype(active))
+ var/turf/T = get_turf(active)
+ // If we somehow delam during this proc, handle it somewhat
+ if(!T)
+ active = null
+ refresh()
+ return
+ var/datum/gas_mixture/air = T.return_air()
+ if(!air)
+ active = null
+ return
+
+ data["active"] = TRUE
+ data["SM_integrity"] = active.get_integrity()
+ data["SM_power"] = active.power
+ data["SM_ambienttemp"] = air.temperature
+ data["SM_ambientpressure"] = air.return_pressure()
+ //data["SM_EPR"] = round((air.total_moles / air.group_multiplier) / 23.1, 0.01)
+ var/other_moles = air.total_trace_moles()
+ var/TM = air.total_moles()
+ if(TM)
+ data["SM_gas_O2"] = round(100*air.oxygen/TM, 0.01)
+ data["SM_gas_CO2"] = round(100*air.carbon_dioxide/TM, 0.01)
+ data["SM_gas_N2"] = round(100*air.nitrogen/TM, 0.01)
+ data["SM_gas_PL"] = round(100*air.toxins/TM, 0.01)
+ if(other_moles)
+ data["SM_gas_OTHER"] = round(100 * other_moles / TM, 0.01)
+ else
+ data["SM_gas_OTHER"] = 0
+ else
+ data["SM_gas_O2"] = 0
+ data["SM_gas_CO2"] = 0
+ data["SM_gas_N2"] = 0
+ data["SM_gas_PH"] = 0
+ data["SM_gas_OTHER"] = 0
+ else
+ var/list/SMS = list()
+ for(var/I in supermatters)
+ var/obj/machinery/power/supermatter_shard/S = I
+ var/area/A = get_area(S)
+ if(!A)
+ continue
+
+ SMS.Add(list(list(
+ "area_name" = A.name,
+ "integrity" = S.get_integrity(),
+ "uid" = S.UID()
+ )))
+
+ data["active"] = FALSE
+ data["supermatters"] = SMS
+
+ return data
+
+/**
+ * Supermatter List Refresher
+ *
+ * This proc loops through the list of supermatters in the atmos SS and adds them to this console's cache list
+ */
+/obj/machinery/computer/sm_monitor/proc/refresh()
+ supermatters = list()
+ var/turf/T = get_turf(tgui_host()) // Get the TGUI host incase this ever turned into a supermatter monitoring module for AIs to use or something
+ if(!T)
+ return
+ for(var/obj/machinery/power/supermatter_shard/S in SSair.atmos_machinery)
+ // Delaminating, not within coverage, not on a tile.
+ if(!(is_station_level(S.z) || is_mining_level(S.z) || atoms_share_level(S, T) || !istype(S.loc, /turf/simulated/)))
+ continue
+ supermatters.Add(S)
+
+ if(!(active in supermatters))
+ active = null
+
+/obj/machinery/computer/sm_monitor/process()
+ if(stat & (NOPOWER|BROKEN))
+ return FALSE
+
+ if(active)
+ var/new_status = active.get_status()
+ if(last_status != new_status)
+ last_status = new_status
+ if(last_status == SUPERMATTER_ERROR)
+ last_status = SUPERMATTER_INACTIVE
+ icon_screen = "smmon_[last_status]"
+ update_icon()
+
+ return TRUE
+
+/obj/machinery/computer/sm_monitor/tgui_act(action, params)
+ if(..())
+ return
+
+ if(stat & (BROKEN|NOPOWER))
+ return
+
+ . = TRUE
+
+ switch(action)
+ if("refresh")
+ refresh()
+
+ if("view")
+ var/newuid = params["view"]
+ for(var/obj/machinery/power/supermatter_shard/S in supermatters)
+ if(S.UID() == newuid)
+ active = S
+ break
+
+ if("back")
+ active = null
+
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index 607918442ea..3b4631b171a 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -6,48 +6,91 @@
icon_screen = "alert:0"
light_color = LIGHT_COLOR_CYAN
circuit = /obj/item/circuitboard/stationalert_engineering
- var/datum/nano_module/alarm_monitor/alarm_monitor
- var/monitor_type = /datum/nano_module/alarm_monitor/engineering
+ var/ui_x = 325
+ var/ui_y = 500
+ var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power")
-/obj/machinery/computer/station_alert/security
- monitor_type = /datum/nano_module/alarm_monitor/security
- circuit = /obj/item/circuitboard/stationalert_security
-
-/obj/machinery/computer/station_alert/all
- monitor_type = /datum/nano_module/alarm_monitor/all
- circuit = /obj/item/circuitboard/stationalert_all
-
-/obj/machinery/computer/station_alert/New()
- ..()
- alarm_monitor = new monitor_type(src)
- alarm_monitor.register(src, /obj/machinery/computer/station_alert/.proc/update_icon)
+/obj/machinery/computer/station_alert/Initialize(mapload)
+ . = ..()
+ GLOB.alert_consoles += src
+ RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
+ RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
/obj/machinery/computer/station_alert/Destroy()
- alarm_monitor.unregister(src)
- QDEL_NULL(alarm_monitor)
+ GLOB.alert_consoles -= src
return ..()
/obj/machinery/computer/station_alert/attack_ai(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- interact(user)
+ tgui_interact(user)
/obj/machinery/computer/station_alert/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/station_alert/interact(mob/user)
- alarm_monitor.ui_interact(user)
+/obj/machinery/computer/station_alert/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "StationAlertConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/station_alert/tgui_data(mob/user)
+ var/list/data = list()
+
+ data["alarms"] = list()
+ for(var/class in SSalarm.alarms)
+ if(!(class in alarms_listend_for))
+ continue
+ data["alarms"][class] = list()
+ for(var/area in SSalarm.alarms[class])
+ for(var/thing in SSalarm.alarms[class][area][3])
+ var/atom/A = locateUID(thing)
+ if(atoms_share_level(A, src))
+ data["alarms"][class] += area
+
+ return data
+
+/obj/machinery/computer/station_alert/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
+ if(!(class in alarms_listend_for))
+ return
+ if(alarmsource.z != z)
+ return
+ if(stat & (BROKEN))
+ return
+ update_icon()
+
+/obj/machinery/computer/station_alert/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
+ if(!(class in alarms_listend_for))
+ return
+ if(origin.z != z)
+ return
+ if(stat & (BROKEN))
+ return
+ update_icon()
/obj/machinery/computer/station_alert/update_icon()
- if(alarm_monitor)
- var/list/alarms = alarm_monitor.major_alarms()
- if(alarms.len)
- icon_screen = "alert:2"
- else
- icon_screen = "alert:0"
+ var/active_alarms = FALSE
+ var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
+ for(var/cat in temp_alarm_list)
+ if(!(cat in alarms_listend_for))
+ continue
+ var/list/list/L = temp_alarm_list[cat].Copy()
+ for(var/alarm in L)
+ var/list/list/alm = L[alarm].Copy()
+ var/list/list/sources = alm[3].Copy()
+ for(var/thing in sources)
+ var/atom/A = locateUID(thing)
+ if(A && A.z != z)
+ L -= alarm
+ if(length(L))
+ active_alarms = TRUE
+ if(active_alarms)
+ icon_screen = "alert:2"
+ else
+ icon_screen = "alert:0"
..()
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 42ff08ff00f..b50d5b9efde 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -38,6 +38,11 @@
#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
+#define TGUI_GREEN 2
+#define TGUI_ORANGE 1
+#define TGUI_RED 0
+
+
GLOBAL_LIST_EMPTY(airlock_overlays)
/obj/machinery/door/airlock
@@ -54,7 +59,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
normalspeed = 1
siemens_strength = 1
var/security_level = 0 //How much are wires secured
- var/aiControlDisabled = FALSE //If TRUE, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
+ var/aiControlDisabled = AICONTROLDISABLED_OFF
var/hackProof = FALSE // if TRUE, this door can't be hacked by the AI
var/electrified_until = 0 // World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
var/main_power_lost_until = 0 //World time when main power is restored.
@@ -65,7 +70,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
var/spawnPowerRestoreRunning = 0
var/lights = TRUE // bolt lights show by default
var/datum/wires/airlock/wires
- var/aiDisabledIdScanner = 0
+ var/aiDisabledIdScanner = FALSE
var/aiHacking = 0
var/obj/machinery/door/airlock/closeOther
var/closeOtherId
@@ -96,7 +101,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's
var/boltUp = 'sound/machines/boltsup.ogg'
var/boltDown = 'sound/machines/boltsdown.ogg'
- var/is_special = 0
+ var/is_special = FALSE
/obj/machinery/door/airlock/welded
welded = TRUE
@@ -142,6 +147,7 @@ About the new airlock wires panel:
break
/obj/machinery/door/airlock/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(electronics)
QDEL_NULL(wires)
QDEL_NULL(note)
@@ -173,7 +179,7 @@ About the new airlock wires panel:
spawn (10)
justzap = 0
return
- else /*if(justzap)*/
+ else
return
else if(user.hallucination > 50 && prob(10) && !operating)
if(user.electrocute_act(50, src, 1, illusion = TRUE)) // We'll just go with a flat 50 damage, instead of doing powernet checks
@@ -188,15 +194,11 @@ About the new airlock wires panel:
return 1
return 0
-/obj/machinery/door/airlock/proc/isWireCut(wireIndex)
- // You can find the wires in the datum folder.
- return wires.IsIndexCut(wireIndex)
-
/obj/machinery/door/airlock/proc/canAIControl()
- return ((aiControlDisabled!=1) && (!isAllPowerLoss()))
+ return ((aiControlDisabled != AICONTROLDISABLED_ON) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/canAIHack()
- return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerLoss()))
+ return ((aiControlDisabled == AICONTROLDISABLED_ON) && (!hackProof) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/arePowerSystemsOn()
if(stat & (NOPOWER|BROKEN))
@@ -204,27 +206,21 @@ About the new airlock wires panel:
return (main_power_lost_until==0 || backup_power_lost_until==0)
/obj/machinery/door/airlock/requiresID()
- return !(isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner)
+ return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner)
/obj/machinery/door/airlock/proc/isAllPowerLoss()
if(stat & (NOPOWER|BROKEN))
return 1
- if(mainPowerCablesCut() && backupPowerCablesCut())
+ if(wires.is_cut(WIRE_MAIN_POWER1) && wires.is_cut(WIRE_BACKUP_POWER1))
return 1
return 0
-/obj/machinery/door/airlock/proc/mainPowerCablesCut()
- return isWireCut(AIRLOCK_WIRE_MAIN_POWER1)
-
-/obj/machinery/door/airlock/proc/backupPowerCablesCut()
- return isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)
-
/obj/machinery/door/airlock/proc/loseMainPower()
- main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + 60 SECONDS
+ main_power_lost_until = wires.is_cut(WIRE_MAIN_POWER1) ? -1 : world.time + 60 SECONDS
if(main_power_lost_until > 0)
main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
// If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running
- if(backup_power_lost_until == -1 && !backupPowerCablesCut())
+ if(backup_power_lost_until == -1 && !wires.is_cut(WIRE_BACKUP_POWER1))
backup_power_lost_until = world.time + 10 SECONDS
backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), 10 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
// Disable electricity if required
@@ -232,7 +228,7 @@ About the new airlock wires panel:
electrify(0)
/obj/machinery/door/airlock/proc/loseBackupPower()
- backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + 60 SECONDS
+ backup_power_lost_until = wires.is_cut(WIRE_BACKUP_POWER1) ? -1 : world.time + 60 SECONDS
if(backup_power_lost_until > 0)
backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
@@ -243,7 +239,7 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/proc/regainMainPower()
main_power_timer = null
- if(!mainPowerCablesCut())
+ if(!wires.is_cut(WIRE_MAIN_POWER1))
main_power_lost_until = 0
// If backup power is currently active then disable, otherwise let it count down and disable itself later
if(!backup_power_lost_until)
@@ -253,7 +249,7 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/proc/regainBackupPower()
backup_power_timer = null
- if(!backupPowerCablesCut())
+ if(!wires.is_cut(WIRE_BACKUP_POWER1))
// Restore backup power only if main power is offline, otherwise permanently disable
backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0
update_icon()
@@ -264,7 +260,7 @@ About the new airlock wires panel:
electrified_timer = null
var/message = ""
- if(isWireCut(AIRLOCK_WIRE_ELECTRIFY) && arePowerSystemsOn())
+ if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn())
message = text("The electrification wire is cut - Door permanently electrified.")
electrified_until = -1
else if(duration && !arePowerSystemsOn())
@@ -570,38 +566,58 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/attack_ghost(mob/user)
if(panel_open)
wires.Interact(user)
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/door/airlock/attack_ai(mob/user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/door/airlock/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "door_control.tmpl", "Door Controls - [src]", 600, 375)
+ ui = new(user, src, ui_key, "AiAirlock", name, 600, 400, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1)
- data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1)
- data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1)
- data["open"] = !density
+/obj/machinery/door/airlock/tgui_data(mob/user)
+ var/list/data = list()
- var/commands[0]
- commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner,"enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1)
- commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised", "disabled" = "Dropped", "danger" = 0, "act" = 0)
- commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1)
- commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0)
- commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0)
- commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0)
- commands[++commands.len] = list("name" = "Emergency Access","command"= "emergency", "active" = !emergency, "enabled" = "Disabled", "disabled" = "Enabled", "danger" = 0, "act" = 0)
+ var/list/power = list()
+ power["main"] = main_power_lost_until ? TGUI_RED : TGUI_GREEN
+ power["main_timeleft"] = max(main_power_lost_until - world.time, 0) / 10
+ power["backup"] = backup_power_lost_until ? TGUI_RED : TGUI_GREEN
+ power["backup_timeleft"] = max(backup_power_lost_until - world.time, 0) / 10
+ data["power"] = power
+ if(electrified_until == -1)
+ data["shock"] = TGUI_RED
+ else if(electrified_until > 0)
+ data["shock"] = TGUI_ORANGE
+ else
+ data["shock"] = TGUI_GREEN
- data["commands"] = commands
+ data["shock_timeleft"] = max(electrified_until - world.time, 0) / 10
+ data["id_scanner"] = !aiDisabledIdScanner
+ data["emergency"] = emergency // access
+ data["locked"] = locked // bolted
+ data["lights"] = lights // bolt lights
+ data["safe"] = safe // safeties
+ data["speed"] = normalspeed // safe speed
+ data["welded"] = welded // welded
+ data["opened"] = !density // opened
+
+ var/list/wire = list()
+ wire["main_power"] = !wires.is_cut(WIRE_MAIN_POWER1)
+ wire["backup_power"] = !wires.is_cut(WIRE_BACKUP_POWER1)
+ wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY)
+ wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN)
+ wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS)
+ wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT)
+ wire["safe"] = !wires.is_cut(WIRE_SAFETY)
+ wire["timing"] = !wires.is_cut(WIRE_SPEED)
+
+ data["wires"] = wire
return data
+
/obj/machinery/door/airlock/proc/hack(mob/user)
set waitfor = 0
if(!aiHacking)
@@ -641,7 +657,7 @@ About the new airlock wires panel:
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
sleep(50)
//disable blocked control
- aiControlDisabled = 2
+ aiControlDisabled = AICONTROLDISABLED_BYPASS
to_chat(user, "Receiving control information from airlock.")
sleep(10)
//bring up airlock dialog
@@ -737,125 +753,125 @@ About the new airlock wires panel:
else
try_to_activate_door(user)
-/obj/machinery/door/airlock/CanUseTopic(mob/user)
- if(!issilicon(user) && !isobserver(user))
- return STATUS_CLOSE
- if(emagged)
- to_chat(user, "Unable to interface: Internal error.")
- return STATUS_CLOSE
- if(!canAIControl() && !isobserver(user))
- if(canAIHack(user))
- hack(user)
- else
- if(isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it.
- to_chat(user, "Unable to interface: Connection timed out.")
- else
- to_chat(user, "Unable to interface: Connection refused.")
- return STATUS_CLOSE
-
- return ..()
-
-/obj/machinery/door/airlock/Topic(href, href_list, nowindow = 0)
+/obj/machinery/door/airlock/tgui_act(action, params)
if(..())
- return 1
-
- var/activate = text2num(href_list["activate"])
- switch(href_list["command"])
- if("idscan")
- if(isWireCut(AIRLOCK_WIRE_IDSCAN))
- to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.")
- else if(activate && aiDisabledIdScanner)
- aiDisabledIdScanner = 0
- to_chat(usr, "IdScan feature has been enabled.")
- else if(!activate && !aiDisabledIdScanner)
- aiDisabledIdScanner = 1
- to_chat(usr, "IdScan feature has been disabled.")
- if("main_power")
+ return
+ if(!issilicon(usr) && !usr.can_admin_interact())
+ to_chat(usr, "Access denied. Only silicons may use this interface.")
+ return
+ if(issilicon(usr) && emagged)
+ to_chat(usr, "Unable to interface: Internal error.")
+ return
+ if(!canAIControl() && !isobserver(usr))
+ if(canAIHack(usr))
+ hack(usr)
+ else
+ if(isAllPowerLoss())
+ to_chat(usr, "Unable to interface: Connection timed out.")
+ else
+ to_chat(usr, "Unable to interface: Connection refused.")
+ return
+ . = TRUE
+ switch(action)
+ if("disrupt-main")
if(!main_power_lost_until)
loseMainPower()
update_icon()
- if("backup_power")
+ else
+ to_chat(usr, "Main power is already offline.")
+ . = FALSE
+ if("disrupt-backup")
if(!backup_power_lost_until)
loseBackupPower()
update_icon()
- if("bolts")
- if(isWireCut(AIRLOCK_WIRE_DOOR_BOLTS))
- to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.")
- else if(activate && lock())
- to_chat(usr, "The door bolts have been dropped.")
- else if(!activate && unlock())
- to_chat(usr, "The door bolts have been raised.")
- if("electrify_temporary")
- if(activate && isWireCut(AIRLOCK_WIRE_ELECTRIFY))
- to_chat(usr, text("The electrification wire is cut - Door permanently electrified."))
- else if(!activate && electrified_until != 0)
- to_chat(usr, "The door is now un-electrified.")
- electrify(0)
- else if(activate) //electrify door for 30 seconds
- shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
- usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
- add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
- to_chat(usr, "The door is now electrified for thirty seconds.")
- electrify(30)
- if("electrify_permanently")
- if(isWireCut(AIRLOCK_WIRE_ELECTRIFY))
- to_chat(usr, text("The electrification wire is cut - Cannot electrify the door."))
- else if(!activate && electrified_until != 0)
- to_chat(usr, "The door is now un-electrified.")
- electrify(0)
- else if(activate)
- shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
- usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
- add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
- to_chat(usr, "The door is now electrified.")
- electrify(-1)
- if("open")
- if(welded)
- to_chat(usr, text("The airlock has been welded shut!"))
- else if(locked)
- to_chat(usr, text("The door bolts are down!"))
- else if(activate && density)
- open()
- else if(!activate && !density)
- close()
- if("safeties")
- // Safeties! We don't need no stinking safeties!
- if(isWireCut(AIRLOCK_WIRE_SAFETY))
- to_chat(usr, text("The safety wire is cut - Cannot secure the door."))
- else if(activate && safe)
- safe = 0
- else if(!activate && !safe)
- safe = 1
- if("timing")
- // Door speed control
- if(isWireCut(AIRLOCK_WIRE_SPEED))
- to_chat(usr, text("The timing wire is cut - Cannot alter timing."))
- else if(activate && normalspeed)
- normalspeed = 0
- else if(!activate && !normalspeed)
- normalspeed = 1
- if("lights")
- // Bolt lights
- if(isWireCut(AIRLOCK_WIRE_LIGHT))
- to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.")
- else if(!activate && lights)
- lights = 0
- to_chat(usr, "The door bolt lights have been disabled.")
- else if(activate && !lights)
- lights = 1
- to_chat(usr, "The door bolt lights have been enabled.")
- update_icon()
- if("emergency")
- // Emergency access
- if(emergency)
- emergency = 0
- to_chat(usr, "Emergency access has been disabled.")
else
- emergency = 1
- to_chat(usr, "Emergency access has been enabled.")
+ to_chat(usr, "Backup power is already offline.")
+ if("shock-restore")
+ to_chat(usr, "The door is now un-electrified.")
+ electrify(0)
+ if("shock-temp")
+ if(wires.is_cut(WIRE_ELECTRIFY))
+ to_chat(usr, "The electrification wire is cut - Door permanently electrified.")
+ . = FALSE
+ else
+ //electrify door for 30 seconds
+ shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
+ usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
+ add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
+ to_chat(usr, "The door is now electrified for thirty seconds.")
+ electrify(30)
+ if("shock-perm")
+ if(wires.is_cut(WIRE_ELECTRIFY))
+ to_chat(usr, "The electrification wire is cut - Cannot electrify the door.")
+ . = FALSE
+ else
+ shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
+ usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
+ add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
+ to_chat(usr, "The door is now electrified.")
+ electrify(-1)
+ if("idscan-toggle")
+ if(wires.is_cut(WIRE_IDSCAN))
+ to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.")
+ . = FALSE
+ else if(aiDisabledIdScanner)
+ aiDisabledIdScanner = FALSE
+ to_chat(usr, "IdScan feature has been enabled.")
+ else
+ aiDisabledIdScanner = TRUE
+ to_chat(usr, "IdScan feature has been disabled.")
+ if("emergency-toggle")
+ emergency = !emergency
+ if(emergency)
+ to_chat(usr, "Emergency access has been enabled.")
+ else
+ to_chat(usr, "Emergency access has been disabled.")
update_icon()
- return 1
+ if("bolt-toggle")
+ if(wires.is_cut(WIRE_DOOR_BOLTS))
+ to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.")
+ else if(lock())
+ to_chat(usr, "The door bolts have been dropped.")
+ else if(unlock())
+ to_chat(usr, "The door bolts have been raised.")
+ if("light-toggle")
+ if(wires.is_cut(WIRE_BOLT_LIGHT))
+ to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.")
+ else if(lights)
+ lights = 0
+ to_chat(usr, "The door bolt lights have been disabled.")
+ else if(!lights)
+ lights = 1
+ to_chat(usr, "The door bolt lights have been enabled.")
+ update_icon()
+ if("safe-toggle")
+ if(wires.is_cut(WIRE_SAFETY))
+ to_chat(usr, "The safety wire is cut - Cannot secure the door.")
+ else if(safe)
+ safe = 0
+ to_chat(usr, "The door safeties have been disabled.")
+ else
+ safe = 1
+ to_chat(usr, "The door safeties have been enabled.")
+ if("speed-toggle")
+ if(wires.is_cut(WIRE_SPEED))
+ to_chat(usr, "The timing wire is cut - Cannot alter timing.")
+ else if(normalspeed)
+ normalspeed = 0
+ else
+ normalspeed = 1
+ if("open-close")
+ if(welded)
+ to_chat(usr, "The airlock has been welded shut!")
+ else if(locked)
+ to_chat(usr, "The door bolts are down!")
+ else if(density)
+ open()
+ else
+ close()
+ else
+ . = FALSE
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
add_fingerprint(user)
@@ -1064,7 +1080,7 @@ About the new airlock wires panel:
return
return TRUE
-/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I) //*scream
+/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I)
if(operating)
return
if(istype(I, /obj/item/twohanded/fireaxe)) //let's make this more specific //FUCK YOU
@@ -1126,7 +1142,7 @@ About the new airlock wires panel:
if(operating || welded || locked || emagged)
return 0
if(!forced)
- if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
+ if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR))
return 0
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(forced)
@@ -1163,7 +1179,7 @@ About the new airlock wires panel:
if(!forced)
//despite the name, this wire is for general door control.
//Bolts are already covered by the check for locked, above
- if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
+ if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR))
return
if(safe)
for(var/turf/turf in locs)
@@ -1219,7 +1235,7 @@ About the new airlock wires panel:
return
if(!forced)
- if(operating || !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS))
+ if(operating || !arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS))
return
locked = 0
@@ -1314,7 +1330,7 @@ About the new airlock wires panel:
stat |= BROKEN
if(!panel_open)
panel_open = TRUE
- wires.CutAll()
+ wires.cut_all()
update_icon()
/obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
@@ -1407,6 +1423,12 @@ About the new airlock wires panel:
A.name = name
qdel(src)
+/obj/machinery/door/airlock/proc/ai_control_callback()
+ if(aiControlDisabled == AICONTROLDISABLED_ON)
+ aiControlDisabled = AICONTROLDISABLED_OFF
+ else if(aiControlDisabled == AICONTROLDISABLED_BYPASS)
+ aiControlDisabled = AICONTROLDISABLED_PERMA
+
#undef AIRLOCK_CLOSED
#undef AIRLOCK_CLOSING
#undef AIRLOCK_OPEN
@@ -1426,3 +1448,7 @@ About the new airlock wires panel:
#undef AIRLOCK_INTEGRITY_MULTIPLIER
#undef AIRLOCK_DAMAGE_DEFLECTION_N
#undef AIRLOCK_DAMAGE_DEFLECTION_R
+
+#undef TGUI_GREEN
+#undef TGUI_ORANGE
+#undef TGUI_RED
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index dd9581a5af3..1d194507357 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -365,14 +365,14 @@
assemblytype = /obj/structure/door_assembly/door_assembly_vault
security_level = 6
hackProof = TRUE
- aiControlDisabled = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
/obj/machinery/door/airlock/hatch/gamma
name = "gamma level hatch"
- hackProof = 1
- aiControlDisabled = 1
+ hackProof = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
resistance_flags = FIRE_PROOF | ACID_PROOF
- is_special = 1
+ is_special = TRUE
/obj/machinery/door/airlock/hatch/gamma/attackby(obj/C, mob/user, params)
if(!issilicon(user))
@@ -432,8 +432,8 @@
/obj/machinery/door/airlock/highsecurity/red
name = "secure armory airlock"
- hackProof = 1
- aiControlDisabled = 1
+ hackProof = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
/obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params)
if(!issilicon(user))
@@ -487,7 +487,7 @@
damage_deflection = 30
explosion_block = 3
hackProof = TRUE
- aiControlDisabled = 1
+ aiControlDisabled = AICONTROLDISABLED_ON
normal_integrity = 700
security_level = 1
paintable = FALSE
@@ -504,7 +504,7 @@
assemblytype = /obj/structure/door_assembly/door_assembly_cult
damage_deflection = 10
hackProof = TRUE
- aiControlDisabled = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
paintable = FALSE
var/openingoverlaytype = /obj/effect/temp_visual/cult/door
var/friendly = FALSE
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 37dbdb6c9c0..c509675868b 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -28,6 +28,11 @@
var/nextstate = null
var/boltslocked = TRUE
var/active_alarm = FALSE
+ var/list/affecting_areas
+
+/obj/machinery/door/firedoor/Initialize(mapload)
+ . = ..()
+ CalculateAffectingAreas()
/obj/machinery/door/firedoor/examine(mob/user)
. = ..()
@@ -40,11 +45,31 @@
else
. += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor."
+/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
+ remove_from_areas()
+ affecting_areas = get_adjacent_open_areas(src) | get_area(src)
+ for(var/I in affecting_areas)
+ var/area/A = I
+ LAZYADD(A.firedoors, src)
+
/obj/machinery/door/firedoor/closed
icon_state = "door_closed"
opacity = TRUE
density = TRUE
+//see also turf/AfterChange for adjacency shennanigans
+
+/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)
+
+/obj/machinery/door/firedoor/Destroy()
+ remove_from_areas()
+ affecting_areas.Cut()
+ return ..()
+
/obj/machinery/door/firedoor/Bumped(atom/AM)
if(panel_open || operating)
return
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index f12361f1664..b387f404d8c 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -25,6 +25,11 @@ FIRE ALARM
active_power_usage = 6
power_channel = ENVIRON
resistance_flags = FIRE_PROOF
+
+ light_power = 0
+ light_range = 7
+ light_color = "#ff3232"
+
var/last_process = 0
var/wiresexposed = 0
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
@@ -191,6 +196,7 @@ FIRE ALARM
/obj/machinery/firealarm/obj_break(damage_flag)
if(!(stat & BROKEN) && !(flags & NODECONSTRUCT) && buildstage != 0) //can't break the electronics if there isn't any inside.
stat |= BROKEN
+ LAZYREMOVE(myArea.firealarms, src)
update_icon()
/obj/machinery/firealarm/deconstruct(disassembled = TRUE)
@@ -203,6 +209,14 @@ FIRE ALARM
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)
+
/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed
if(stat & (NOPOWER|BROKEN))
return
@@ -286,26 +300,16 @@ FIRE ALARM
time = min(max(round(time), 0), 120)
/obj/machinery/firealarm/proc/reset()
- if(!working)
+ if(!working || !report_fire_alarms)
return
var/area/A = get_area(src)
- A.fire_reset()
+ A.firereset(src)
- for(var/obj/machinery/firealarm/FA in A)
- if(is_station_contact(z) && FA.report_fire_alarms)
- SSalarms.fire_alarm.clearAlarm(loc, FA)
-
-/obj/machinery/firealarm/proc/alarm(var/duration = 0)
- if(!working)
+/obj/machinery/firealarm/proc/alarm()
+ if(!working || !report_fire_alarms)
return
-
var/area/A = get_area(src)
- for(var/obj/machinery/firealarm/FA in A)
- if(is_station_contact(z) && FA.report_fire_alarms)
- SSalarms.fire_alarm.triggerAlarm(loc, FA, duration)
- else
- A.fire_alert() // Manually trigger alarms if the alarm isn't reported
-
+ A.firealert(src) // Manually trigger alarms if the alarm isn't reported
update_icon()
/obj/machinery/firealarm/New(location, direction, building)
@@ -323,8 +327,14 @@ FIRE ALARM
else
overlays += image('icons/obj/monitors.dmi', "overlay_green")
+ myArea = get_area(src)
+ LAZYADD(myArea.firealarms, src)
update_icon()
+/obj/machinery/firealarm/Destroy()
+ LAZYREMOVE(myArea.firealarms, src)
+ return ..()
+
/*
FIRE ALARM CIRCUIT
Just a object used in constructing fire alarms
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 5952108b6a9..1624ef4bb92 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -12,29 +12,26 @@
var/resultlvl = null
/obj/machinery/slot_machine/attack_hand(mob/user as mob)
+ tgui_interact(user)
+
+/obj/machinery/slot_machine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "SlotMachine", name, 350, 200, master_ui, state)
+ ui.open()
+
+/obj/machinery/slot_machine/tgui_data(mob/user)
+ var/list/data = list()
+ // Get account
account = user.get_worn_id_account()
if(!account)
if(istype(user.get_active_hand(), /obj/item/card/id))
account = get_card_account(user.get_active_hand())
else
account = null
- ui_interact(user)
-/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I)
- . = TRUE
- if(!I.tool_use_check(user, 0))
- return
- default_unfasten_wrench(user, I)
-/obj/machinery/slot_machine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "slotmachine.tmpl", name, 350, 200)
- ui.open()
- ui.set_auto_update(1)
-
-/obj/machinery/slot_machine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+ // Send data
data["working"] = working
data["money"] = account ? account.money : null
data["plays"] = plays
@@ -42,22 +39,23 @@
data["resultlvl"] = resultlvl
return data
-/obj/machinery/slot_machine/Topic(href, href_list)
+/obj/machinery/slot_machine/tgui_act(action, params)
+ if(..())
+ return
add_fingerprint(usr)
- if(href_list["ops"])
- if(text2num(href_list["ops"])) // Play
- if(working)
- return
- if(!account || account.money < 10)
- return
- if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine"))
- return
- plays++
- working = 1
- icon_state = "slots-on"
- playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
- addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25)
+ if(action == "spin")
+ if(working)
+ return
+ if(!account || account.money < 10)
+ return
+ if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine"))
+ return
+ plays++
+ working = TRUE
+ icon_state = "slots-on"
+ playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
+ addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25)
/obj/machinery/slot_machine/proc/spin_slots(userName)
switch(rand(1,4050))
@@ -65,44 +63,45 @@
atom_say("JACKPOT! [userName] has won a MILLION CREDITS!")
GLOB.event_announcement.Announce("Congratulations to [userName] on winning the Jackpot of ONE MILLION CREDITS!", "Jackpot Winner")
result = "JACKPOT! You win one million credits!"
- resultlvl = "highlight"
+ resultlvl = "teal"
win_money(1000000, 'sound/goonstation/misc/airraid_loop.ogg')
if(2 to 5) // .07%
atom_say("Big Winner! [userName] has won a hundred thousand credits!")
GLOB.event_announcement.Announce("Congratulations to [userName] on winning a hundred thousand credits!", "Big Winner")
result = "Big Winner! You win a hundred thousand credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(100000, 'sound/goonstation/misc/klaxon.ogg')
if(6 to 50) // 1.08%
atom_say("Big Winner! [userName] has won ten thousand credits!")
result = "You win ten thousand credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(10000, 'sound/goonstation/misc/klaxon.ogg')
if(51 to 100) // 1.21%
atom_say("Winner! [userName] has won a thousand credits!")
result = "You win a thousand credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(1000, 'sound/goonstation/misc/bell.ogg')
if(101 to 200) // 2.44%
atom_say("Winner! [userName] has won a hundred credits!")
result = "You win a hundred credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(100, 'sound/goonstation/misc/bell.ogg')
if(201 to 300) // 2.44%
atom_say("Winner! [userName] has won fifty credits!")
result = "You win fifty credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(50)
if(301 to 1000) // 17.26%
atom_say("Winner! [userName] has won ten credits!")
result = "You win ten credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(10)
else // 75.31%
- result = "No luck!"
- resultlvl = "average"
- working = 0
+ result = "No luck!"
+ resultlvl = "orange"
+ working = FALSE
icon_state = "slots-off"
+ SStgui.update_uis(src) // Push a UI update
/obj/machinery/slot_machine/proc/win_money(amt, sound='sound/machines/ping.ogg')
if(sound)
@@ -110,3 +109,9 @@
if(!account)
return
account.credit(amt, "Slot Winnings", "Slot Machine", account.owner_name)
+
+/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.tool_use_check(user, 0))
+ return
+ default_unfasten_wrench(user, I)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index cd6b33d0a95..822c4af3e1a 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -255,6 +255,7 @@
occupant_typecache = typecacheof(occupant_typecache)
/obj/machinery/suit_storage_unit/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(suit)
QDEL_NULL(helmet)
QDEL_NULL(mask)
@@ -802,3 +803,7 @@
/obj/machinery/suit_storage_unit/attack_ai(mob/user as mob)
return attack_hand(user)
+
+/obj/machinery/suit_storage_unit/proc/check_electrified_callback()
+ if(!wires.is_cut(WIRE_ELECTRIFY))
+ shocked = FALSE
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index 181d5e494da..2a476c215ef 100644
--- a/code/game/machinery/syndicatebomb.dm
+++ b/code/game/machinery/syndicatebomb.dm
@@ -91,6 +91,7 @@
..()
/obj/machinery/syndicatebomb/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
QDEL_NULL(countdown)
STOP_PROCESSING(SSfastprocess, src)
@@ -174,7 +175,7 @@
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
- if(open_panel && wires.IsAllCut())
+ if(open_panel && wires.is_all_cut())
if(payload)
to_chat(user, "You carefully pry out [payload].")
payload.loc = user.loc
@@ -188,7 +189,7 @@
/obj/machinery/syndicatebomb/welder_act(mob/user, obj/item/I)
. = TRUE
- if(payload || !wires.IsAllCut() || !open_panel)
+ if(payload || !wires.is_all_cut() || !open_panel)
return
if(!I.tool_use_check(user, 0))
return
@@ -264,9 +265,6 @@
investigate_log("[key_name(user)] has has primed a [name] ([payload]) for detonation at [A.name] [COORD(bombturf)]", INVESTIGATE_BOMB)
payload.adminlog = "\The [src] that [key_name(user)] had primed detonated!"
-/obj/machinery/syndicatebomb/proc/isWireCut(var/index)
- return wires.IsIndexCut(index)
-
///Bomb Subtypes///
/obj/machinery/syndicatebomb/training
@@ -297,7 +295,7 @@
/obj/machinery/syndicatebomb/empty/New()
..()
- wires.CutAll()
+ wires.cut_all()
/obj/machinery/syndicatebomb/self_destruct
name = "self destruct device"
@@ -368,7 +366,7 @@
var/obj/machinery/syndicatebomb/holder = loc
if(istype(holder))
if(holder.wires)
- holder.wires.Shuffle()
+ holder.wires.shuffle_wires()
holder.defused = 0
holder.open_panel = 0
holder.delayedbig = FALSE
diff --git a/code/game/machinery/tcomms/_base.dm b/code/game/machinery/tcomms/_base.dm
index 05c7552aacc..c3531c7f674 100644
--- a/code/game/machinery/tcomms/_base.dm
+++ b/code/game/machinery/tcomms/_base.dm
@@ -106,23 +106,37 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
/**
- * Start of Ion Anomalie Event
+ * Start of Ion Anomaly Event
*
- * Proc to easily start an Ion Anomalie's effects, and update the icon
+ * Proc to easily start an Ion Anomaly's effects, and update the icon
*/
/obj/machinery/tcomms/proc/start_ion()
ion = TRUE
update_icon()
/**
- * End of Ion Anomalie Event
+ * End of Ion Anomaly Event
*
- * Proc to easily stop an Ion Anomalie's effects, and update the icon
+ * Proc to easily stop an Ion Anomaly's effects, and update the icon
*/
/obj/machinery/tcomms/proc/end_ion()
ion = FALSE
update_icon()
+/**
+ * Z-Level transit change helper
+ *
+ * Proc to make sure you cant have two of these active on a Z-level at once. It also makes sure to update the linkage
+ */
+/obj/machinery/tcomms/onTransitZ(old_z, new_z)
+ . = ..()
+ if(active)
+ active = FALSE
+ // This needs a timer because otherwise its on the shuttle Z and the message is missed
+ addtimer(CALLBACK(src, /atom.proc/visible_message, "Radio equipment on [src] has been overloaded by heavy bluespace interference. Please restart the machine."), 5)
+ update_icon()
+
+
/**
* Logging helper
*
diff --git a/code/game/machinery/tcomms/core.dm b/code/game/machinery/tcomms/core.dm
index 46cd0e6df71..622ce2e2b9f 100644
--- a/code/game/machinery/tcomms/core.dm
+++ b/code/game/machinery/tcomms/core.dm
@@ -14,6 +14,8 @@
name = "Telecommunications Core"
desc = "A large rack full of communications equipment. Looks important."
icon_state = "core"
+ // This starts as off so you cant make cores as hot spares
+ active = FALSE
/// The NTTC config for this device
var/datum/nttc_configuration/nttc = new()
/// List of all reachable devices
@@ -35,6 +37,11 @@
link_password = GenerateKey()
reachable_zlevels |= loc.z
component_parts += new /obj/item/circuitboard/tcomms/core(null)
+ if(check_power_on())
+ active = TRUE
+ else
+ visible_message("Error: Another core is already active in this sector. Power-up cancelled due to radio interference.")
+ update_icon()
/**
* Destructor for the core.
@@ -121,6 +128,38 @@
reachable_zlevels |= R.loc.z
+/**
+ * Z-Level transit change helper
+ *
+ * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels
+ */
+/obj/machinery/tcomms/core/onTransitZ(old_z, new_z)
+ . = ..()
+ refresh_zlevels()
+
+/**
+ * Power-on checker
+ *
+ * Checks the z-level to see if an existing core is already powered on, and deny this one turning on if there is one. Returns TRUE if it can power on, or FALSE if it cannot
+ */
+/obj/machinery/tcomms/core/proc/check_power_on()
+ // Cancel if we are already on
+ if(active)
+ return TRUE
+
+ for(var/obj/machinery/tcomms/core/C in GLOB.tcomms_machines)
+ // Make sure we dont check ourselves
+ if(C == src)
+ continue
+ // We dont care about ones on other zlevels
+ if(!atoms_share_level(C, src))
+ continue
+ // If another core is active, return FALSE
+ if(C.active)
+ return FALSE
+ // If we got here there isnt an active core on this Z-level. So return true
+ return TRUE
+
//////////////
// UI STUFF //
//////////////
@@ -198,8 +237,11 @@
if(ui_tab == UI_TAB_CONFIG)
// All the toggle on/offs go here
if(href_list["toggle_active"])
- active = !active
- update_icon()
+ if(check_power_on())
+ active = !active
+ update_icon()
+ else
+ to_chat(usr, "Error: Another core is already active in this sector. Power-up cancelled due to radio interference.")
// NTTC Toggles
if(href_list["nttc_toggle_jobs"])
nttc.toggle_jobs = !nttc.toggle_jobs
diff --git a/code/game/machinery/tcomms/relay.dm b/code/game/machinery/tcomms/relay.dm
index b2faf9ce0cd..bf5e6e21a9c 100644
--- a/code/game/machinery/tcomms/relay.dm
+++ b/code/game/machinery/tcomms/relay.dm
@@ -9,6 +9,8 @@
name = "Telecommunications Relay"
desc = "A large device with several radio antennas on it."
icon_state = "relay"
+ // This starts as off so you cant make cores as hot spares
+ active = FALSE
/// The host core for this relay
var/obj/machinery/tcomms/core/linked_core
/// ID of the hub to auto link to
@@ -26,6 +28,11 @@
/obj/machinery/tcomms/relay/Initialize(mapload)
. = ..()
component_parts += new /obj/item/circuitboard/tcomms/relay(null)
+ if(check_power_on())
+ active = TRUE
+ else
+ visible_message("Error: Another relay is already active in this sector. Power-up cancelled due to radio interference.")
+ update_icon()
if(mapload && autolink_id)
return INITIALIZE_HINT_LATELOAD
@@ -51,6 +58,40 @@
// Only ONE of these with one ID should exist per world
break
+/**
+ * Z-Level transit change helper
+ *
+ * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels on the linked core
+ */
+/obj/machinery/tcomms/relay/onTransitZ(old_z, new_z)
+ . = ..()
+ if(linked_core)
+ linked_core.refresh_zlevels()
+
+
+/**
+ * Power-on checker
+ *
+ * Checks the z-level to see if an existing relay is already powered on, and deny this one turning on if there is one. Returns TRUE if it can power on, or FALSE if it cannot
+ */
+/obj/machinery/tcomms/relay/proc/check_power_on()
+ // Cancel if we are already on
+ if(active)
+ return TRUE
+
+ for(var/obj/machinery/tcomms/relay/R in GLOB.tcomms_machines)
+ // Make sure we dont check ourselves
+ if(R == src)
+ continue
+ // We dont care about ones on other zlevels
+ if(!atoms_share_level(R, src))
+ continue
+ // If another relay is active, return FALSE
+ if(R.active)
+ return FALSE
+ // If we got here there isnt an active relay on this Z-level. So return TRUE
+ return TRUE
+
/**
* Proc to link the relay to the core.
*
@@ -83,9 +124,9 @@
* Proc which ensures the host core has its zlevels updated (icons are updated by parent call)
*/
/obj/machinery/tcomms/relay/power_change()
- ..()
- if(linked_core)
- linked_core.refresh_zlevels()
+ ..()
+ if(linked_core)
+ linked_core.refresh_zlevels()
//////////////
// UI STUFF //
@@ -127,10 +168,13 @@
// All the toggle on/offs go here
if(href_list["toggle_active"])
- active = !active
- update_icon()
- if(linked_core)
- linked_core.refresh_zlevels()
+ if(check_power_on())
+ active = !active
+ update_icon()
+ if(linked_core)
+ linked_core.refresh_zlevels()
+ else
+ to_chat(usr, "Error: Another relay is already active in this sector. Power-up cancelled due to radio interference.")
// Set network ID
if(href_list["network_id"])
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index cc9fc3e4666..18abeba7400 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -6,17 +6,45 @@
layer = MOB_LAYER+1 // Overhead
anchored = 1
density = 1
- var/transform_dead = 0
- var/transform_standing = 0
- var/cooldown_duration = 600 // 1 minute
- var/cooldown = 0
- var/robot_cell_charge = 5000
+ /// TRUE if the factory can transform dead mobs.
+ var/transform_dead = TRUE
+ /// TRUE if the mob can be standing and still be transformed.
+ var/transform_standing = TRUE
+ /// Cooldown between each transformation, in deciseconds.
+ var/cooldown_duration = 1 MINUTES
+ /// If the factory is currently on cooldown from its last transformation.
+ var/is_on_cooldown = FALSE
+ /// The type of cell that newly created borgs get.
+ var/robot_cell_type = /obj/item/stock_parts/cell/high/plus
+ /// The direction that mobs must moving in to get transformed.
var/acceptdir = EAST
+ /// The AI who placed this factory.
+ var/mob/living/silicon/ai/masterAI
-/obj/machinery/transformer/New()
- // On us
- ..()
- new /obj/machinery/conveyor/auto(loc, WEST)
+/obj/machinery/transformer/Initialize(mapload, mob/living/silicon/ai/_ai = null)
+ . = ..()
+ if(_ai)
+ masterAI = _ai
+ initialize_belts()
+
+/// Used to create all of the belts the transformer will be using. All belts should be pushing `WEST`.
+/obj/machinery/transformer/proc/initialize_belts()
+ var/turf/T = get_turf(src)
+ if(!T)
+ return
+
+ // Belt under the factory.
+ new /obj/machinery/conveyor/auto(T, WEST)
+
+ // Get the turf 1 tile to the EAST.
+ var/turf/east = locate(T.x + 1, T.y, T.z)
+ if(istype(east, /turf/simulated/floor))
+ new /obj/machinery/conveyor/auto(east, WEST)
+
+ // Get the turf 1 tile to the WEST.
+ var/turf/west = locate(T.x - 1, T.y, T.z)
+ if(istype(west, /turf/simulated/floor))
+ new /obj/machinery/conveyor/auto(west, WEST)
/obj/machinery/transformer/power_change()
..()
@@ -24,7 +52,7 @@
/obj/machinery/transformer/update_icon()
..()
- if(stat & (BROKEN|NOPOWER) || cooldown == 1)
+ if(is_on_cooldown || stat & (BROKEN|NOPOWER))
icon_state = "separator-AO0"
else
icon_state = initial(icon_state)
@@ -35,120 +63,76 @@
C.setDir(newdir)
acceptdir = turn(newdir, 180)
-/obj/machinery/transformer/Bumped(var/atom/movable/AM)
+/// Resets `is_on_cooldown` to `FALSE` and updates our icon. Used in a callback after the transformer does a transformation.
+/obj/machinery/transformer/proc/reset_cooldown()
+ is_on_cooldown = FALSE
+ update_icon()
- if(cooldown == 1)
+/obj/machinery/transformer/Bumped(atom/movable/AM)
+ // They have to be human to be transformed.
+ if(is_on_cooldown || !ishuman(AM))
return
- // Crossed didn't like people lying down.
- if(ishuman(AM))
- // Only humans can enter from the west side, while lying down.
- var/move_dir = get_dir(loc, AM.loc)
- var/mob/living/carbon/human/H = AM
- if((transform_standing || H.lying) && move_dir == acceptdir)// || move_dir == WEST)
- AM.loc = src.loc
- do_transform(AM)
+ var/mob/living/carbon/human/H = AM
+ var/move_dir = get_dir(loc, H.loc)
-/obj/machinery/transformer/proc/do_transform(var/mob/living/carbon/human/H)
- if(stat & (BROKEN|NOPOWER))
- return
- if(cooldown == 1)
+ if((transform_standing || H.lying) && move_dir == acceptdir)
+ H.forceMove(drop_location())
+ do_transform(H)
+
+/// Transforms a human mob into a cyborg, connects them to the malf AI which placed the factory.
+/obj/machinery/transformer/proc/do_transform(mob/living/carbon/human/H)
+ if(is_on_cooldown || stat & (BROKEN|NOPOWER))
return
if(!transform_dead && H.stat == DEAD)
- playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
return
- playsound(src.loc, 'sound/items/welder.ogg', 50, 1)
- H.emote("scream") // It is painful
- H.adjustBruteLoss(max(0, 80 - H.getBruteLoss())) // Hurt the human, don't try to kill them though.
-
- // Sleep for a couple of ticks to allow the human to see the pain
- sleep(5)
-
+ playsound(loc, 'sound/items/welder.ogg', 50, 1)
use_power(5000) // Use a lot of power.
- var/mob/living/silicon/robot/R = H.Robotize(1) // Delete the items or they'll all pile up in a single tile and lag
-
- R.cell.maxcharge = robot_cell_charge
- R.cell.charge = robot_cell_charge
-
- // So he can't jump out the gate right away.
- R.lockcharge = !R.lockcharge
- spawn(50)
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
- sleep(30)
- if(R)
- R.lockcharge = !R.lockcharge
- R.notify_ai(1)
// Activate the cooldown
- cooldown = 1
+ is_on_cooldown = TRUE
update_icon()
- spawn(cooldown_duration)
- cooldown = 0
- update_icon()
-
-/obj/machinery/transformer/conveyor/New()
- ..()
- var/turf/T = loc
- if(T)
- // Spawn Conveyour Belts
-
- //East
- var/turf/east = locate(T.x + 1, T.y, T.z)
- if(istype(east, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(east, WEST)
-
- // West
- var/turf/west = locate(T.x - 1, T.y, T.z)
- if(istype(west, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(west, WEST)
+ addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown_duration)
+ addtimer(CALLBACK(null, .proc/playsound, loc, 'sound/machines/ping.ogg', 50, 0), 3 SECONDS)
+ H.emote("scream")
+ if(!masterAI) // If the factory was placed via admin spawning or other means, it wont have an owner_AI.
+ H.Robotize(robot_cell_type)
+ return
+ var/mob/living/silicon/robot/R = H.Robotize(robot_cell_type, FALSE, masterAI)
+ if(R.mind && !R.client && !R.grab_ghost()) // Make sure this is an actual player first and not just a humanized monkey or something.
+ message_admins("[key_name_admin(R)] was just transformed by a borg factory, but they were SSD. Polling ghosts for a replacement.")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a malfunctioning cyborg?", ROLE_TRAITOR, poll_time = 15 SECONDS)
+ if(!length(candidates))
+ return
+ var/mob/dead/observer/O = pick(candidates)
+ R.key= O.key
/obj/machinery/transformer/mime
name = "Mimetech Greyscaler"
desc = "Turns anything placed inside black and white."
-
-/obj/machinery/transformer/mime/conveyor/New()
- ..()
- var/turf/T = loc
- if(T)
- // Spawn Conveyour Belts
-
- //East
- var/turf/east = locate(T.x + 1, T.y, T.z)
- if(istype(east, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(east, WEST)
-
- // West
- var/turf/west = locate(T.x - 1, T.y, T.z)
- if(istype(west, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(west, WEST)
-
-/obj/machinery/transformer/mime/Bumped(var/atom/movable/AM)
-
- if(cooldown == 1)
+/obj/machinery/transformer/mime/Bumped(atom/movable/AM)
+ if(is_on_cooldown)
return
// Crossed didn't like people lying down.
- if(isatom(AM))
- AM.loc = src.loc
+ if(istype(AM))
+ AM.forceMove(drop_location())
do_transform_mime(AM)
else
to_chat(AM, "Only items can be greyscaled.")
return
-/obj/machinery/transformer/proc/do_transform_mime(var/obj/item/I)
- if(stat & (BROKEN|NOPOWER))
- return
- if(cooldown == 1)
+/obj/machinery/transformer/proc/do_transform_mime(obj/item/I)
+ if(is_on_cooldown || stat & (BROKEN|NOPOWER))
return
- playsound(src.loc, 'sound/items/welder.ogg', 50, 1)
- // Sleep for a couple of ticks to allow the human to see the pain
- sleep(5)
+ playsound(loc, 'sound/items/welder.ogg', 50, 1)
use_power(5000) // Use a lot of power.
var/icon/newicon = new(I.icon, I.icon_state)
@@ -156,42 +140,27 @@
I.icon = newicon
// Activate the cooldown
- cooldown = 1
+ is_on_cooldown = TRUE
update_icon()
- spawn(cooldown_duration)
- cooldown = 0
- update_icon()
+ addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown_duration)
/obj/machinery/transformer/xray
name = "Automatic X-Ray 5000"
desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'backpack go in, backpack come out', 'human go in, irradiated human come out'."
+ acceptdir = WEST
-/obj/machinery/transformer/xray/Initialize(mapload)
- . = ..()
- // On us
- new /obj/machinery/conveyor/auto(loc, EAST)
-
-/obj/machinery/transformer/xray/conveyor/New()
- ..()
- var/turf/T = loc
+/obj/machinery/transformer/xray/initialize_belts()
+ var/turf/T = get_turf(src)
if(T)
- // Spawn Conveyour Belts
+ // This handles the belt under the transformer and 1 tile to the left and right.
+ . = ..()
- //East
- var/turf/east = locate(T.x + 1, T.y, T.z)
- if(istype(east, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(east, EAST)
- //East2
+ // Get the turf 2 tiles to the EAST.
var/turf/east2 = locate(T.x + 2, T.y, T.z)
if(istype(east2, /turf/simulated/floor))
new /obj/machinery/conveyor/auto(east2, EAST)
- // West
- var/turf/west = locate(T.x - 1, T.y, T.z)
- if(istype(west, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(west, EAST)
-
- // West2
+ // Get the turf 2 tiles to the WEST.
var/turf/west2 = locate(T.x - 2, T.y, T.z)
if(istype(west2, /turf/simulated/floor))
new /obj/machinery/conveyor/auto(west2, EAST)
@@ -207,30 +176,30 @@
else
icon_state = initial(icon_state)
-/obj/machinery/transformer/xray/Bumped(var/atom/movable/AM)
-
- if(cooldown == 1)
+/obj/machinery/transformer/xray/Bumped(atom/movable/AM)
+ if(is_on_cooldown)
return
// Crossed didn't like people lying down.
if(ishuman(AM))
// Only humans can enter from the west side, while lying down.
- var/move_dir = get_dir(loc, AM.loc)
var/mob/living/carbon/human/H = AM
- if(H.lying && move_dir == WEST)// || move_dir == WEST)
- AM.loc = src.loc
- irradiate(AM)
+ var/move_dir = get_dir(loc, H.loc)
- else if(isatom(AM))
- AM.loc = src.loc
+ if(H.lying && move_dir == acceptdir)
+ H.forceMove(drop_location())
+ irradiate(H)
+
+ else if(istype(AM))
+ AM.forceMove(drop_location())
scan(AM)
-/obj/machinery/transformer/xray/proc/irradiate(var/mob/living/carbon/human/H)
+/obj/machinery/transformer/xray/proc/irradiate(mob/living/carbon/human/H)
if(stat & (BROKEN|NOPOWER))
return
flick("separator-AO0",src)
- playsound(src.loc, 'sound/effects/alert.ogg', 50, 0)
+ playsound(loc, 'sound/effects/alert.ogg', 50, 0)
sleep(5)
H.apply_effect((rand(150,200)),IRRADIATE,0)
if(prob(5))
@@ -242,15 +211,15 @@
domutcheck(H,null,1)
-/obj/machinery/transformer/xray/proc/scan(var/obj/item/I)
+/obj/machinery/transformer/xray/proc/scan(obj/item/I)
if(scan_rec(I))
- playsound(src.loc, 'sound/effects/alert.ogg', 50, 0)
+ playsound(loc, 'sound/effects/alert.ogg', 50, 0)
flick("separator-AO0",src)
else
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
+ playsound(loc, 'sound/machines/ping.ogg', 50, 0)
sleep(30)
-/obj/machinery/transformer/xray/proc/scan_rec(var/obj/item/I)
+/obj/machinery/transformer/xray/proc/scan_rec(obj/item/I)
if(istype(I, /obj/item/gun))
return TRUE
if(istype(I, /obj/item/transfer_valve))
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index afed001f63e..fdca5c56194 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -101,6 +101,7 @@
power_change()
/obj/machinery/vending/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
QDEL_NULL(coin)
QDEL_NULL(inserted_item)
@@ -1371,7 +1372,6 @@
/obj/item/clothing/glasses/gglasses = 1,
/obj/item/clothing/shoes/jackboots = 1,
/obj/item/clothing/under/schoolgirl = 1,
- /obj/item/clothing/head/kitty = 1,
/obj/item/clothing/under/blackskirt = 1,
/obj/item/clothing/suit/toggle/owlwings = 1,
/obj/item/clothing/under/owl = 1,
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 69f70207452..9a24edb4da3 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -331,6 +331,8 @@
else
occupant.clear_alert("mechaport")
if(leg_overload_mode)
+ log_message("Leg Overload damage.")
+ take_damage(1, BRUTE, FALSE, FALSE)
if(obj_integrity < max_integrity - max_integrity / 3)
leg_overload_mode = FALSE
step_in = initial(step_in)
@@ -502,7 +504,7 @@
check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
else
check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT))
- if(. >= 5 || prob(33))
+ if((. >= 5 || prob(33)) && !(. == 1 && leg_overload_mode)) //If it takes 1 damage and leg_overload_mode is true, do not say TAKING DAMAGE! to the user several times a second.
occupant_message("Taking damage!")
log_message("Took [damage_amount] points of damage. Damage type: [damage_type]")
@@ -540,7 +542,7 @@
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1)
- user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.")
+ user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.")
log_message("Attack by hand/paw. Attacker - [user].")
diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm
index 5f5c62abd21..97e46e3abb5 100644
--- a/code/game/objects/items/blueprints.dm
+++ b/code/game/objects/items/blueprints.dm
@@ -221,6 +221,12 @@
A.contents += thing
thing.change_area(old_area, A)
+ var/area/oldA = get_area(get_turf(usr))
+ var/list/firedoors = oldA.firedoors
+ for(var/door in firedoors)
+ var/obj/machinery/door/firedoor/FD = door
+ FD.CalculateAffectingAreas()
+
interact()
area_created = TRUE
return area_created
@@ -236,6 +242,10 @@
return
set_area_machinery_title(A,str,prevname)
A.name = str
+ if(A.firedoors)
+ for(var/D in A.firedoors)
+ var/obj/machinery/door/firedoor/FD = D
+ FD.CalculateAffectingAreas()
to_chat(usr, "You rename the '[prevname]' to '[str]'.")
interact()
return 1
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index eeb484b9f50..ce52e260bca 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -11,7 +11,7 @@
slot_flags = SLOT_BELT | SLOT_EARS
attack_verb = list("attacked", "coloured")
toolspeed = 1
- var/colour = "#FF0000" //RGB
+ var/colour = COLOR_RED
var/drawtype = "rune"
var/list/graffiti = list("body","amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","up","down","left","right","heart","borgsrogue","voxpox","shitcurity","catbeast","hieroglyphs1","hieroglyphs2","hieroglyphs3","security","syndicate1","syndicate2","nanotrasen","lie","valid","arrowleft","arrowright","arrowup","arrowdown","chicken","hailcrab","brokenheart","peace","scribble","scribble2","scribble3","skrek","squish","tunnelsnake","yip","youaredead")
var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
@@ -123,54 +123,54 @@
/obj/item/toy/crayon/red
icon_state = "crayonred"
- colour = "#DA0000"
+ colour = COLOR_RED
colourName = "red"
/obj/item/toy/crayon/orange
icon_state = "crayonorange"
- colour = "#FF9300"
+ colour = COLOR_ORANGE
colourName = "orange"
/obj/item/toy/crayon/yellow
icon_state = "crayonyellow"
- colour = "#FFF200"
+ colour = COLOR_YELLOW
colourName = "yellow"
/obj/item/toy/crayon/green
icon_state = "crayongreen"
- colour = "#A8E61D"
+ colour = COLOR_GREEN
colourName = "green"
/obj/item/toy/crayon/blue
icon_state = "crayonblue"
- colour = "#00B7EF"
+ colour = COLOR_BLUE
colourName = "blue"
/obj/item/toy/crayon/purple
icon_state = "crayonpurple"
- colour = "#DA00FF"
+ colour = COLOR_PURPLE
colourName = "purple"
/obj/item/toy/crayon/random/New()
icon_state = pick(list("crayonred", "crayonorange", "crayonyellow", "crayongreen", "crayonblue", "crayonpurple"))
switch(icon_state)
if("crayonred")
- colour = "#DA0000"
+ colour = COLOR_RED
colourName = "red"
if("crayonorange")
- colour = "#FF9300"
+ colour = COLOR_ORANGE
colourName = "orange"
if("crayonyellow")
- colour = "#FFF200"
+ colour = COLOR_YELLOW
colourName = "yellow"
if("crayongreen")
- colour = "#A8E61D"
+ colour =COLOR_GREEN
colourName = "green"
if("crayonblue")
- colour = "#00B7EF"
+ colour = COLOR_BLUE
colourName = "blue"
if("crayonpurple")
- colour = "#DA00FF"
+ colour = COLOR_PURPLE
colourName = "purple"
..()
@@ -197,10 +197,10 @@
if(!Adjacent(usr) || usr.incapacitated())
return
if(href_list["color"])
- if(colour != "#FFFFFF")
- colour = "#FFFFFF"
+ if(colour != COLOR_WHITE)
+ colour = COLOR_WHITE
else
- colour = "#000000"
+ colour = COLOR_BLACK
update_window(usr)
else
..()
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index 59d2c2a99bd..c8122fc34d6 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -186,10 +186,10 @@
update_icon()
START_PROCESSING(SSobj, src)
for(var/i, i<= 5, i++)
- wires.UpdateCut(i,1)
+ wires.on_cut(i, 1)
/obj/item/radio/intercom/wirecutter_act(mob/user, obj/item/I)
- if(!(buildstage == 3 && b_stat && wires.IsAllCut()))
+ if(!(buildstage == 3 && b_stat && wires.is_all_cut()))
return
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
@@ -270,4 +270,4 @@
/obj/item/radio/intercom/locked/prison/New()
..()
- wires.CutWireIndex(RADIO_WIRE_TRANSMIT)
+ wires.cut(WIRE_RADIO_TRANSMIT)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index b497bc518b0..528be3c5f87 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -76,6 +76,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
GLOB.global_radios |= src
/obj/item/radio/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
if(SSradio)
SSradio.remove_object(src, frequency)
@@ -127,8 +128,8 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
data["freq"] = format_frequency(frequency)
data["rawfreq"] = num2text(frequency)
- data["mic_cut"] = (wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
- data["spk_cut"] = (wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
+ data["mic_cut"] = (wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL))
+ data["spk_cut"] = (wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL))
var/list/chanlist = list_channels(user)
if(islist(chanlist) && chanlist.len)
@@ -184,10 +185,10 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
return can_admin_interact()
/obj/item/radio/proc/ToggleBroadcast()
- broadcasting = !broadcasting && !(wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
+ broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL))
/obj/item/radio/proc/ToggleReception()
- listening = !listening && !(wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
+ listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL))
/obj/item/radio/Topic(href, href_list)
if(..())
@@ -325,7 +326,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
// If we were to send to a channel we don't have, drop it.
return RADIO_CONNECTION_FAIL
-/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says")
+/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, verbage = "says")
if(!on)
return 0 // the device has to be on
// Fix for permacell radios, but kinda eh about actually fixing them.
@@ -334,7 +335,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
// Uncommenting this. To the above comment:
// The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom
- if(wires.IsIndexCut(RADIO_WIRE_TRANSMIT)) // The device has to have all its wires and shit intact
+ if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact
return 0
if(!M.IsVocal())
@@ -428,6 +429,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
tcm.connection = connection
tcm.vname = M.voice_name
tcm.sender = M
+ tcm.verbage = verbage
// Now put that through the stuff
var/handled = FALSE
if(connection)
@@ -514,7 +516,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
var/is_listening = TRUE
if(!on)
is_listening = FALSE
- if(!wires || wires.IsIndexCut(RADIO_WIRE_RECEIVE))
+ if(!wires || wires.is_cut(WIRE_RADIO_RECEIVER))
is_listening = FALSE
if(!listening)
is_listening = FALSE
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index c65a9c3e50b..43697ac4776 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -72,8 +72,7 @@ REAGENT SCANNER
var/turf/U = O.loc
if(U && U.intact)
O.invisibility = 101
- if(O)
- O.alpha = 255
+ O.alpha = 255
for(var/mob/living/M in T.contents)
var/oldalpha = M.alpha
if(M.alpha < 255 && istype(M))
diff --git a/code/game/objects/items/flag.dm b/code/game/objects/items/flag.dm
index 14bb468e049..f33e504a3e6 100644
--- a/code/game/objects/items/flag.dm
+++ b/code/game/objects/items/flag.dm
@@ -251,6 +251,7 @@
message_admins("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] (JMP).")
log_game("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).")
investigate_log("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).", INVESTIGATE_BOMB)
+ burn()
else
return ..()
@@ -267,8 +268,16 @@
/obj/item/flag/chameleon/burn()
if(boobytrap)
- boobytrap.prime()
- ..()
+ fire_act()
+ addtimer(CALLBACK(src, .proc/prime_boobytrap), boobytrap.det_time)
+ else
+ ..()
+
+/obj/item/flag/chameleon/proc/prime_boobytrap()
+ boobytrap.forceMove(get_turf(loc))
+ boobytrap.prime()
+ boobytrap = null
+ burn()
/obj/item/flag/chameleon/updateFlagIcon()
icon_state = updated_icon_state
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 75e1c6a710d..4fef7b9f6d5 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -472,7 +472,8 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("cane mould", /obj/item/kitchen/mould/cane, 1, on_floor = 1), \
new /datum/stack_recipe("cash mould", /obj/item/kitchen/mould/cash, 1, on_floor = 1), \
new /datum/stack_recipe("coin mould", /obj/item/kitchen/mould/coin, 1, on_floor = 1), \
- new /datum/stack_recipe("sucker mould", /obj/item/kitchen/mould/loli, 1, on_floor = 1)))
+ new /datum/stack_recipe("sucker mould", /obj/item/kitchen/mould/loli, 1, on_floor = 1), \
+ new /datum/stack_recipe("warning cone", /obj/item/clothing/head/cone, 5, on_floor = 1)))
/obj/item/stack/sheet/plastic
name = "plastic"
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index d3d61233f97..cd1b91388d7 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -35,7 +35,7 @@
desc = "A translucent balloon. There's nothing in it."
icon = 'icons/obj/toy.dmi'
icon_state = "waterballoon-e"
- item_state = "balloon-empty"
+ item_state = "waterballoon-e"
/obj/item/toy/balloon/New()
..()
@@ -99,10 +99,10 @@
/obj/item/toy/balloon/update_icon()
if(src.reagents.total_volume >= 1)
icon_state = "waterballoon"
- item_state = "balloon"
+ item_state = "waterballoon"
else
icon_state = "waterballoon-e"
- item_state = "balloon-empty"
+ item_state = "waterballoon-e"
/obj/item/toy/syndicateballoon
name = "syndicate balloon"
diff --git a/code/game/objects/items/weapons/garrote.dm b/code/game/objects/items/weapons/garrote.dm
index 4bddaf551ea..e3c145e790b 100644
--- a/code/game/objects/items/weapons/garrote.dm
+++ b/code/game/objects/items/weapons/garrote.dm
@@ -104,7 +104,7 @@
playsound(src.loc, 'sound/weapons/cablecuff.ogg', 15, 1, -1)
M.visible_message("[U] comes from behind and begins garroting [M] with the [src]!", \
- "[U]\ begins garroting you with the [src]![improvised ? "" : " You are unable to speak!"]", \
+ "[U] begins garroting you with the [src]![improvised ? "" : " You are unable to speak!"]", \
"You hear struggling and wire strain against flesh!")
return
diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm
index 6dd07485f5b..53061e5fa61 100644
--- a/code/game/objects/items/weapons/grenades/smokebomb.dm
+++ b/code/game/objects/items/weapons/grenades/smokebomb.dm
@@ -19,7 +19,7 @@
/obj/item/grenade/smokebomb/prime()
playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
- src.smoke.set_up(10, 0, usr.loc)
+ smoke.set_up(10, 0)
spawn(0)
src.smoke.start()
sleep(10)
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 1f73e5c7062..69b4abbe10d 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -126,13 +126,15 @@
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
..()
-/obj/item/restraints/handcuffs/cable/proc/cable_color(var/colorC)
- if(colorC)
- if(colorC == "rainbow")
- colorC = color_rainbow()
- color = colorC
- else
+/obj/item/restraints/handcuffs/cable/proc/cable_color(colorC)
+ if(!colorC)
color = COLOR_RED
+ else if(colorC == "rainbow")
+ color = color_rainbow()
+ else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
+ color = COLOR_ORANGE
+ else
+ color = colorC
/obj/item/restraints/handcuffs/cable/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 26045731bd9..e7880fd0868 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -372,6 +372,15 @@
new /obj/item/ammo_box/magazine/m12g/buckshot(src)
new /obj/item/ammo_box/magazine/m12g/dragon(src)
+/obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags
+ desc = "A large duffelbag, containing three types of extended drum magazines."
+
+/obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags/New()
+ ..()
+ new /obj/item/ammo_box/magazine/m12g/XtrLrg(src)
+ new /obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot(src)
+ new /obj/item/ammo_box/magazine/m12g/XtrLrg/dragon(src)
+
/obj/item/storage/backpack/duffel/mining_conscript/
name = "mining conscription kit"
desc = "A kit containing everything a crewmember needs to support a shaft miner in the field."
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index 6d5a192c641..121782b2e8e 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -380,12 +380,13 @@
w_class = WEIGHT_CLASS_BULKY
flags = CONDUCT
materials = list(MAT_METAL=3000)
+ cant_hold = list(/obj/item/disk/nuclear) // Prevents some cheesing
-/obj/item/storage/bag/tray/attack(mob/living/M as mob, mob/living/user as mob)
+/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
- quick_empty()
+ drop_inventory(user)
// Make each item scatter a bit
for(var/obj/item/I in oldContents)
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index da0094bb2ef..9ef94a861a0 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -846,7 +846,7 @@
desc = "A small box of Almost But Not Quite Plasma Premium Matches."
icon = 'icons/obj/cigarettes.dmi'
icon_state = "matchbox"
- item_state = "zippo"
+ item_state = "matchbox"
storage_slots = 10
w_class = WEIGHT_CLASS_TINY
max_w_class = WEIGHT_CLASS_TINY
diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm
index 37f568fa51a..f86ebb2f69d 100644
--- a/code/game/objects/items/weapons/storage/fancy.dm
+++ b/code/game/objects/items/weapons/storage/fancy.dm
@@ -226,14 +226,14 @@
name = "\improper DromedaryCo packet"
desc = "A packet of six imported DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\""
icon_state = "Dpacket"
- item_state = "cigpacket"
+ item_state = "Dpacket"
/obj/item/storage/fancy/cigarettes/syndicate
name = "\improper Syndicate Cigarettes"
desc = "A packet of six evil-looking cigarettes, A label on the packaging reads, \"Donk Co\""
icon_state = "robustpacket"
- item_state = "cigpacket"
+ item_state = "robustpacket"
/obj/item/storage/fancy/cigarettes/syndicate/New()
..()
@@ -244,14 +244,14 @@
name = "cigarette packet"
desc = "An obscure brand of cigarettes."
icon_state = "syndiepacket"
- item_state = "cigpacket"
+ item_state = "syndiepacket"
cigarette_type = /obj/item/clothing/mask/cigarette/syndicate
/obj/item/storage/fancy/cigarettes/cigpack_med
name = "Medical Marijuana Packet"
desc = "A prescription packet containing six marijuana cigarettes."
icon_state = "medpacket"
- item_state = "cigpacket"
+ item_state = "medpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/medical_marijuana
@@ -259,46 +259,46 @@
name = "\improper Uplift Smooth packet"
desc = "Your favorite brand, now menthol flavored."
icon_state = "upliftpacket"
- item_state = "cigpacket"
+ item_state = "upliftpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/menthol
/obj/item/storage/fancy/cigarettes/cigpack_robust
name = "\improper Robust packet"
desc = "Smoked by the robust."
icon_state = "robustpacket"
- item_state = "cigpacket"
+ item_state = "robustpacket"
/obj/item/storage/fancy/cigarettes/cigpack_robustgold
name = "\improper Robust Gold packet"
desc = "Smoked by the truly robust."
icon_state = "robustgpacket"
- item_state = "cigpacket"
+ item_state = "robustgpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/robustgold
/obj/item/storage/fancy/cigarettes/cigpack_carp
name = "\improper Carp Classic packet"
desc = "Since 2313."
icon_state = "carppacket"
- item_state = "cigpacket"
+ item_state = "carppacket"
/obj/item/storage/fancy/cigarettes/cigpack_midori
name = "\improper Midori Tabako packet"
desc = "You can't understand the runes, but the packet smells funny."
icon_state = "midoripacket"
- item_state = "cigpacket"
+ item_state = "midoripacket"
/obj/item/storage/fancy/cigarettes/cigpack_shadyjims
name ="\improper Shady Jim's Super Slims"
desc = "Is your weight slowing you down? Having trouble running away from gravitational singularities? Can't stop stuffing your mouth? Smoke Shady Jim's Super Slims and watch all that fat burn away. Guaranteed results!"
icon_state = "shadyjimpacket"
- item_state = "cigpacket"
+ item_state = "shadyjimpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/shadyjims
/obj/item/storage/fancy/cigarettes/cigpack_random
name ="\improper Embellished Enigma packet"
desc = "For the true connoisseur of exotic flavors."
icon_state = "shadyjimpacket"
- item_state = "cigpacket"
+ item_state = "shadyjimpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/random
/obj/item/storage/fancy/rollingpapers
@@ -307,6 +307,7 @@
w_class = WEIGHT_CLASS_TINY
icon = 'icons/obj/cigarettes.dmi'
icon_state = "cig_paper_pack"
+ item_state = "cig_paper_pack"
storage_slots = 10
icon_type = "rolling paper"
can_hold = list(/obj/item/rollingpaper)
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 16bd6f4caa8..49b6c6a11ea 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -438,8 +438,11 @@
if((!ishuman(usr) && (src.loc != usr)) || usr.stat || usr.restrained())
return
+ drop_inventory(usr)
+
+/obj/item/storage/proc/drop_inventory(user)
var/turf/T = get_turf(src)
- hide_from(usr)
+ hide_from(user)
for(var/obj/item/I in contents)
remove_from_storage(I, T)
CHECK_TICK
@@ -499,10 +502,9 @@
/obj/item/storage/attack_self(mob/user)
- //Clicking on itself will empty it, if it has the verb to do that.
- if(user.is_in_active_hand(src))
- if(verbs.Find(/obj/item/storage/verb/quick_empty))
- quick_empty()
+ //Clicking on itself will empty it, if allow_quick_empty is TRUE
+ if(allow_quick_empty && user.is_in_active_hand(src))
+ drop_inventory(user)
//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area).
//Returns -1 if the atom was not found on container.
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 9ded7947202..abc267d3a34 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -43,35 +43,31 @@
/obj/item/tank/proc/toggle_internals(mob/user, silent = FALSE)
var/mob/living/carbon/C = user
if(!istype(C))
- return 0
+ return FALSE
if(C.internal == src)
to_chat(C, "You close \the [src] valve.")
C.internal = null
else
- var/can_open_valve = 0
- if(C.get_organ_slot("breathing_tube"))
- can_open_valve = 1
- else if(C.wear_mask && C.wear_mask.flags & AIRTIGHT)
- can_open_valve = 1
- else if(ishuman(C))
- var/mob/living/carbon/human/H = C
- if(H.head && H.head.flags & AIRTIGHT)
- can_open_valve = 1
+ if(!C.get_organ_slot("breathing_tube")) // Breathing tubes can always use internals, if they have one, skip ahead and turn internals on/off
+ if(!C.wear_mask) // Do we have a mask equipped?
+ return FALSE
- if(can_open_valve)
+ var/obj/item/clothing/mask/M = C.wear_mask
+ // If the "mask" isn't actually a mask OR That mask isn't internals compatible AND Their headgear isn't internals compatible
+ if(!istype(M) || (!(initial(M.flags) & AIRTIGHT) && !(C.head.flags & AIRTIGHT)))
+ if(!silent)
+ to_chat(C, "You are not wearing a suitable mask or helmet.")
+ return FALSE
+ if(M.mask_adjusted) // If the mask is equipped but pushed down
+ M.adjustmask(C) // Adjust it back
+
+ if(!silent)
if(C.internal)
- if(!silent)
- to_chat(C, "You switch your internals to [src].")
+ to_chat(C, "You switch your internals to [src].")
else
- if(!silent)
- to_chat(C, "You open \the [src] valve.")
- C.internal = src
- else
- if(!silent)
- to_chat(C, "You are not wearing a suitable mask or helmet.")
- return 0
-
+ to_chat(C, "You open \the [src] valve.")
+ C.internal = src
C.update_action_buttons_icon()
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
index fd67afcf797..311bc4a38c7 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
@@ -45,8 +45,6 @@
new /obj/item/clothing/suit/storage/labcoat(src)
new /obj/item/radio/headset/headset_sci(src)
new /obj/item/radio/headset/headset_sci(src)
- new /obj/item/reagent_containers/food/drinks/oilcan(src)
- new /obj/item/reagent_containers/food/drinks/oilcan(src)
/obj/structure/closet/secure_closet/RD
name = "research director's locker"
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
index 763717dc270..7a648aa74e4 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
@@ -69,6 +69,11 @@
/obj/structure/closet/secure_closet/closed_item_click(mob/user)
togglelock(user)
+/obj/structure/closet/secure_closet/AltClick(mob/user)
+ ..()
+ if(Adjacent(user))
+ togglelock(user)
+
/obj/structure/closet/secure_closet/emag_act(mob/user)
if(!broken)
broken = TRUE
diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm
index 4531613610e..0c0a7f5e4dd 100644
--- a/code/game/objects/structures/dresser.dm
+++ b/code/game/objects/structures/dresser.dm
@@ -56,27 +56,20 @@
/obj/structure/dresser/crowbar_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_start_check(src, user, 0))
+ if(!I.use_tool(src, user, 0))
return
TOOL_ATTEMPT_DISMANTLE_MESSAGE
if(I.use_tool(src, user, 50, volume = I.tool_volume))
TOOL_DISMANTLE_SUCCESS_MESSAGE
-
+ deconstruct(disassembled = TRUE)
/obj/structure/dresser/wrench_act(mob/user, obj/item/I)
. = TRUE
- if(!I.use_tool(src, user, 0, volume = I.tool_volume))
- return
- if(anchored)
- WRENCH_UNANCHOR_MESSAGE
- anchored = FALSE
- else
- if(!isfloorturf(loc))
- user.visible_message("A floor must be present to secure [src]!")
- return
- WRENCH_ANCHOR_MESSAGE
- anchored = TRUE
+ default_unfasten_wrench(user, I, time = 20)
-/obj/structure/dresser/deconstruct(disassembled = TRUE)
- new /obj/item/stack/sheet/wood(drop_location(), 30)
- qdel(src)
+obj/structure/dresser/deconstruct(disassembled = FALSE)
+ var/mat_drop = 15
+ if(disassembled)
+ mat_drop = 30
+ new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
+ ..()
diff --git a/code/game/objects/structures/loom.dm b/code/game/objects/structures/loom.dm
index d0bcd07f92f..4742a2c3c0c 100644
--- a/code/game/objects/structures/loom.dm
+++ b/code/game/objects/structures/loom.dm
@@ -11,12 +11,30 @@
anchored = TRUE
/obj/structure/loom/attackby(obj/item/I, mob/user)
- if(default_unfasten_wrench(user, I, 5))
- return
if(weave(I, user))
return
return ..()
+/obj/structure/loom/crowbar_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.use_tool(src, user, 0))
+ return
+ TOOL_ATTEMPT_DISMANTLE_MESSAGE
+ if(I.use_tool(src, user, 50, volume = I.tool_volume))
+ TOOL_DISMANTLE_SUCCESS_MESSAGE
+ deconstruct(disassembled = TRUE)
+
+/obj/structure/loom/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ default_unfasten_wrench(user, I, time = 20)
+
+/obj/structure/loom/deconstruct(disassembled = FALSE)
+ var/mat_drop = 5
+ if(disassembled)
+ mat_drop = 10
+ new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
+ ..()
+
///Handles the weaving.
/obj/structure/loom/proc/weave(obj/item/stack/sheet/cotton/W, mob/user)
if(!istype(W))
diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm
deleted file mode 100644
index c3d63c47862..00000000000
--- a/code/game/objects/structures/musician.dm
+++ /dev/null
@@ -1,341 +0,0 @@
-
-
-/datum/song
- var/name = "Untitled"
- var/list/lines = new()
- var/tempo = 5 // delay between notes
-
- var/playing = 0 // if we're playing
- var/help = 0 // if help is open
- var/repeat = 0 // number of times remaining to repeat
- var/max_repeat = 10 // maximum times we can repeat
-
- var/instrumentDir = "piano" // the folder with the sounds
- var/instrumentExt = "ogg" // the file extension
- var/obj/instrumentObj = null // the associated obj playing the sound
-
-/datum/song/New(dir, obj, ext = "ogg")
- tempo = sanitize_tempo(tempo)
- instrumentDir = dir
- instrumentObj = obj
- instrumentExt = ext
-
-/datum/song/Destroy()
- instrumentObj = null
- return ..()
-
-// note is a number from 1-7 for A-G
-// acc is either "b", "n", or "#"
-// oct is 1-8 (or 9 for C)
-/datum/song/proc/playnote(note, acc as text, oct)
- // handle accidental -> B<>C of E<>F
- if(acc == "b" && (note == 3 || note == 6)) // C or F
- if(note == 3)
- oct--
- note--
- acc = "n"
- else if(acc == "#" && (note == 2 || note == 5)) // B or E
- if(note == 2)
- oct++
- note++
- acc = "n"
- else if(acc == "#" && (note == 7)) //G#
- note = 1
- acc = "b"
- else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
- acc = "b"
- note++
-
- // check octave, C is allowed to go to 9
- if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
- return
-
- // now generate name
- var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
- soundfile = file(soundfile)
- // make sure the note exists
- if(!fexists(soundfile))
- return
- // and play
- var/turf/source = get_turf(instrumentObj)
- var/sound/music_played = sound(soundfile)
- for(var/A in hearers(15, source))
- var/mob/M = A
- if(!M.client || !(M.client.prefs.sound & SOUND_INSTRUMENTS))
- continue
- M.playsound_local(source, null, 100, falloff = 5, S = music_played)
-
-/datum/song/proc/shouldStopPlaying(mob/user)
- if(instrumentObj)
- //if(!user.canUseTopic(instrumentObj))
- //return 1
- return !instrumentObj.anchored // add special cases to stop in subclasses
- else
- return 1
-
-/datum/song/proc/playsong(mob/user)
- while(repeat >= 0)
- var/cur_oct[7]
- var/cur_acc[7]
- for(var/i = 1 to 7)
- cur_oct[i] = 3
- cur_acc[i] = "n"
-
- for(var/line in lines)
- for(var/beat in splittext(lowertext(line), ","))
- var/list/notes = splittext(beat, "/")
- for(var/note in splittext(notes[1], "-"))
- if(!playing || shouldStopPlaying(user)) //If the instrument is playing, or special case
- playing = 0
- return
- if(length(note) == 0)
- continue
- var/cur_note = text2ascii(note) - 96
- if(cur_note < 1 || cur_note > 7)
- continue
- for(var/i=2 to length(note))
- var/ni = copytext(note,i,i+1)
- if(!text2num(ni))
- if(ni == "#" || ni == "b" || ni == "n")
- cur_acc[cur_note] = ni
- else if(ni == "s")
- cur_acc[cur_note] = "#" // so shift is never required
- else
- cur_oct[cur_note] = text2num(ni)
- playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
- if(notes.len >= 2 && text2num(notes[2]))
- sleep(sanitize_tempo(tempo / text2num(notes[2])))
- else
- sleep(tempo)
- repeat--
- playing = 0
- repeat = 0
-
-/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(!instrumentObj)
- return
-
- ui = SSnanoui.try_update_ui(user, instrumentObj, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500)
- ui.open()
- ui.set_auto_update(1)
-
-/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
-
- data["lines"] = lines
- data["tempo"] = tempo
-
- data["playing"] = playing
- data["help"] = help
- data["repeat"] = repeat
- data["maxRepeat"] = max_repeat
- data["minTempo"] = world.tick_lag
- data["maxTempo"] = 600
-
- return data
-
-/datum/song/Topic(href, href_list)
- if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || usr.incapacitated())
- usr << browse(null, "window=instrument")
- usr.unset_machine()
- return 1
-
- instrumentObj.add_fingerprint(usr)
-
- if(href_list["newsong"])
- playing = 0
- lines = new()
- tempo = sanitize_tempo(5) // default 120 BPM
- name = ""
- SSnanoui.update_uis(src)
-
- else if(href_list["import"])
- playing = 0
- var/t = ""
- do
- t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
- if(!in_range(instrumentObj, usr))
- return
-
- if(length(t) >= 12000)
- var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
- if(cont == "no")
- break
- while(length(t) > 12000)
-
- //split into lines
- spawn()
- lines = splittext(t, "\n")
- if(lines.len == 0)
- return 1
- if(copytext(lines[1],1,6) == "BPM: ")
- tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
- lines.Cut(1,2)
- else
- tempo = sanitize_tempo(5) // default 120 BPM
- if(lines.len > 200)
- to_chat(usr, "Too many lines!")
- lines.Cut(201)
- var/linenum = 1
- for(var/l in lines)
- if(length(l) > 200)
- to_chat(usr, "Line [linenum] too long!")
- lines.Remove(l)
- else
- linenum++
- SSnanoui.update_uis(src)
-
- else if(href_list["help"])
- help = !help
- SSnanoui.update_uis(src)
-
- if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops.
- if(playing)
- return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
- repeat += round(text2num(href_list["repeat"]))
- if(repeat < 0)
- repeat = 0
- if(repeat > max_repeat)
- repeat = max_repeat
- SSnanoui.update_uis(src)
-
- else if(href_list["tempo"])
- tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag)
- SSnanoui.update_uis(src)
-
- else if(href_list["play"])
- if(playing)
- return
- playing = 1
- spawn()
- playsong(usr)
- SSnanoui.update_uis(src)
-
- else if(href_list["insertline"])
- var/num = round(text2num(href_list["insertline"]))
- if(num < 1 || num > lines.len + 1)
- return
-
- var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
- if(!newline || !in_range(instrumentObj, usr))
- return
- if(lines.len > 200)
- return
- if(length(newline) > 200)
- newline = copytext(newline, 1, 200)
-
- lines.Insert(num, newline)
- SSnanoui.update_uis(src)
-
- else if(href_list["deleteline"])
- var/num = round(text2num(href_list["deleteline"]))
- if(num > lines.len || num < 1)
- return
- lines.Cut(num, num + 1)
- SSnanoui.update_uis(src)
-
- else if(href_list["modifyline"])
- var/num = round(text2num(href_list["modifyline"]))
- var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
- if(!content || !in_range(instrumentObj, usr))
- return
- if(length(content) > 200)
- content = copytext(content, 1, 200)
- if(num > lines.len || num < 1)
- return
- lines[num] = content
- SSnanoui.update_uis(src)
-
- else if(href_list["stop"])
- playing = 0
- SSnanoui.update_uis(src)
-
-/datum/song/proc/sanitize_tempo(new_tempo)
- new_tempo = abs(new_tempo)
- return max(round(new_tempo, world.tick_lag), world.tick_lag)
-
-// subclass for handheld instruments, like violin
-/datum/song/handheld
-
-/datum/song/handheld/shouldStopPlaying()
- if(instrumentObj)
- return !isliving(instrumentObj.loc)
- else
- return 1
-
-
-//////////////////////////////////////////////////////////////////////////
-
-
-/obj/structure/piano
- name = "space minimoog"
- icon = 'icons/obj/musician.dmi'
- icon_state = "minimoog"
- anchored = 1
- density = 1
- var/datum/song/song
-
-
-/obj/structure/piano/New()
- ..()
- song = new("piano", src)
-
- if(prob(50))
- name = "space minimoog"
- desc = "This is a minimoog, like a space piano, but more spacey!"
- icon_state = "minimoog"
- else
- name = "space piano"
- desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
- icon_state = "piano"
-
-/obj/structure/piano/Destroy()
- QDEL_NULL(song)
- return ..()
-
-/obj/structure/piano/Initialize()
- if(song)
- song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
- ..()
-
-/obj/structure/piano/attack_hand(mob/user as mob)
- ui_interact(user)
-
-/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(!isliving(user) || user.incapacitated() || !anchored)
- return
-
- song.ui_interact(user, ui_key, ui, force_open)
-
-/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- return song.ui_data(user, ui_key, state)
-
-/obj/structure/piano/Topic(href, href_list)
- song.Topic(href, href_list)
-
-/obj/structure/piano/wrench_act(mob/user, obj/item/I)
- . = TRUE
- if(!I.tool_use_check(user, 0))
- return
- if(!anchored && !isinspace())
- WRENCH_ANCHOR_MESSAGE
- if(!I.use_tool(src, user, 20, volume = I.tool_volume))
- return
- user.visible_message( \
- "[user] tightens [src]'s casters.", \
- " You have tightened [src]'s casters. Now it can be played again.", \
- "You hear ratchet.")
- anchored = TRUE
- else if(anchored)
- to_chat(user, " You begin to loosen [src]'s casters...")
- if(!I.use_tool(src, user, 40, volume = I.tool_volume))
- return
- user.visible_message( \
- "[user] loosens [src]'s casters.", \
- " You have loosened [src]. Now it can be pulled somewhere else.", \
- "You hear ratchet.")
- anchored = FALSE
- else
- to_chat(user, "[src] needs to be bolted to the floor!")
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index ba5258004a0..5a8a122be21 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -292,12 +292,12 @@
/obj/structure/sign/directions/engineering
name = "\improper Engineering Department"
- desc = "A direction sign, pointing out which way the Engineering department is."
+ desc = "A direction sign, pointing out which way the Engineering Department is."
icon_state = "direction_eng"
/obj/structure/sign/directions/security
name = "\improper Security Department"
- desc = "A direction sign, pointing out which way the Security department is."
+ desc = "A direction sign, pointing out which way the Security Department is."
icon_state = "direction_sec"
/obj/structure/sign/directions/medical
@@ -307,12 +307,12 @@
/obj/structure/sign/directions/evac
name = "\improper Escape Arm"
- desc = "A direction sign, pointing out which way escape shuttle dock is."
+ desc = "A direction sign, pointing out which way Escape Shuttle Dock is."
icon_state = "direction_evac"
/obj/structure/sign/directions/cargo
name = "\improper Cargo Department"
- desc = "A direction sign, pointing out which way the Cargo department is."
+ desc = "A direction sign, pointing out which way the Cargo Department is."
icon_state = "direction_supply"
/obj/structure/sign/explosives
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 870ac5e6966..6882c27df3d 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -4,12 +4,13 @@
return
var/turf/turf_source = get_turf(source)
-
if(!turf_source)
return
+ if(!SSsounds.channel_list) // Not ready yet
+ return
//allocate a channel if necessary now so its the same for everyone
- channel = channel || open_sound_channel()
+ channel = channel || SSsounds.random_available_channel()
// Looping through the player list has the added bonus of working for mobs inside containers
var/sound/S = sound(get_sfx(soundin))
@@ -33,7 +34,7 @@
if(distance <= maxdistance)
M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S)
-/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S)
+/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, distance_multiplier = 1)
if(!client || !can_hear())
return
@@ -41,7 +42,7 @@
S = sound(get_sfx(soundin))
S.wait = 0 //No queue
- S.channel = channel || open_sound_channel()
+ S.channel = channel || SSsounds.random_available_channel()
S.volume = vol
if(vary)
@@ -55,6 +56,7 @@
//sound volume falloff with distance
var/distance = get_dist(T, turf_source)
+ distance *= distance_multiplier
S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff.
@@ -81,9 +83,9 @@
return //No sound
var/dx = turf_source.x - T.x // Hearing from the right/left
- S.x = dx
+ S.x = dx * distance_multiplier
var/dz = turf_source.y - T.y // Hearing from infront/behind
- S.z = dz
+ S.z = dz * distance_multiplier
// The y value is for above your head, but there is no ceiling in 2d spessmens.
S.y = 1
S.falloff = (falloff ? falloff : FALLOFF_SOUNDS)
@@ -98,15 +100,14 @@
var/mob/M = m
M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S)
-/proc/open_sound_channel()
- var/static/next_channel = 1 //loop through the available 1024 - (the ones we reserve) channels and pray that its not still being used
- . = ++next_channel
- if(next_channel > CHANNEL_HIGHEST_AVAILABLE)
- next_channel = 1
-
/mob/proc/stop_sound_channel(chan)
SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan))
+/mob/proc/set_sound_channel_volume(channel, volume)
+ var/sound/S = sound(null, FALSE, FALSE, channel, volume)
+ S.status = SOUND_UPDATE
+ SEND_SOUND(src, S)
+
/client/proc/playtitlemusic()
if(!SSticker || !SSticker.login_music || config.disable_lobby_music)
return
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 8d1f3920c0c..2f90e4d7cc4 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -263,6 +263,13 @@
if(SSair && !ignore_air)
SSair.add_to_active(src)
+ //update firedoor adjacency
+ var/list/turfs_to_check = get_adjacent_open_turfs(src) | src
+ for(var/I in turfs_to_check)
+ var/turf/T = I
+ for(var/obj/machinery/door/firedoor/FD in T)
+ FD.CalculateAffectingAreas()
+
if(!keep_cabling && !can_have_cabling())
for(var/obj/structure/cable/C in contents)
qdel(C)
diff --git a/code/game/world.dm b/code/game/world.dm
index 02c613e9ac8..c279fa24b54 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -340,6 +340,8 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
#endif
for(var/client/C in GLOB.clients)
+ var/secs_before_auto_reconnect = 10 // TODO: make it higher if server is due for an update @AffectedArc07
+ C << output(list2params(list(secs_before_auto_reconnect)), "browseroutput:reboot")
if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[config.server]")
diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm
deleted file mode 100644
index 752d3232d23..00000000000
--- a/code/modules/alarm/alarm.dm
+++ /dev/null
@@ -1,136 +0,0 @@
-#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone?
-
-/datum/alarm_source
- var/source = null // The source trigger
- var/source_name = "" // The name of the source should it be lost (for example a destroyed camera)
- var/duration = 0 // How long this source will be alarming, 0 for indefinetely.
- var/severity = 1 // How severe the alarm from this source is.
- var/start_time = 0 // When this source began alarming.
- var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost.
-
-/datum/alarm_source/New(var/atom/source)
- src.source = source
- start_time = world.time
- source_name = source.get_source_name()
-
-/datum/alarm
- var/atom/origin //Used to identify the alarm area.
- var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared.
- var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source.
- var/list/cameras //List of cameras that can be switched to, if the player has that capability.
- var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera).
- var/area/last_name //The last acquired name, used should origin be lost
- var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated.
- var/end_time //Used to set when this alarm should clear, in case the origin is lost.
-
-/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity)
- src.origin = origin
-
- cameras() // Sets up both cameras and last alarm area.
- set_source_data(source, duration, severity)
-
-/datum/alarm/process()
- // Has origin gone missing?
- if(!origin && !end_time)
- end_time = world.time + ALARM_RESET_DELAY
- for(var/datum/alarm_source/AS in sources)
- // Has the alarm passed its best before date?
- if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration)))
- sources -= AS
- // Has the source gone missing? Then reset the normal duration and set end_time
- if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely.
- AS.duration = 0
- AS.end_time = world.time + ALARM_RESET_DELAY
-
-/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity)
- var/datum/alarm_source/AS = sources_assoc[source]
- if(!AS)
- AS = new/datum/alarm_source(source)
- sources += AS
- sources_assoc[source] = AS
- // Currently only non-0 durations can be altered (normal alarms VS EMP blasts)
- if(AS.duration)
- duration = duration SECONDS
- AS.duration = duration
- AS.severity = severity
-
-/datum/alarm/proc/clear(var/source)
- var/datum/alarm_source/AS = sources_assoc[source]
- sources -= AS
- sources_assoc -= source
-
-/datum/alarm/proc/alarm_area()
- if(!origin)
- return last_area
-
- last_area = origin.get_alarm_area()
- return last_area
-
-/datum/alarm/proc/alarm_name()
- if(!origin)
- return last_name
-
- last_name = origin.get_alarm_name()
- return last_name
-
-/datum/alarm/proc/cameras()
- // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras
- if(cameras && (last_camera_area != alarm_area()))
- cameras = null
-
- // The list of cameras is also reset by /proc/invalidateCameraCache()
- if(!cameras)
- cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
-
- last_camera_area = last_area
- return cameras
-
-/datum/alarm/proc/max_severity()
- var/max_severity = 0
- for(var/datum/alarm_source/AS in sources)
- max_severity = max(AS.severity, max_severity)
-
- return max_severity
-
-/******************
-* Assisting procs *
-******************/
-/atom/proc/get_alarm_area()
- var/area/A = get_area(src)
- return A
-
-/area/get_alarm_area()
- return src
-
-/atom/proc/get_alarm_name()
- var/area/A = get_area(src)
- return A.name
-
-/area/get_alarm_name()
- return name
-
-/mob/get_alarm_name()
- return name
-
-/atom/proc/get_source_name()
- return name
-
-/obj/machinery/camera/get_source_name()
- return c_tag
-
-/atom/proc/get_alarm_cameras()
- var/area/A = get_area(src)
- return A.get_cameras()
-
-/area/get_alarm_cameras()
- return get_cameras()
-
-/mob/living/silicon/robot/get_alarm_cameras()
- var/list/cameras = ..()
- if(camera)
- cameras += camera
-
- return cameras
-
-/mob/living/silicon/robot/syndicate/get_alarm_cameras()
- return list()
diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm
deleted file mode 100644
index 56a673bb5b4..00000000000
--- a/code/modules/alarm/alarm_handler.dm
+++ /dev/null
@@ -1,103 +0,0 @@
-#define ALARM_RAISED 1
-#define ALARM_CLEARED 0
-
-/datum/alarm_handler
- var/category = ""
- var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms
- var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin.
- var/list/listeners = new // A list of all objects interested in alarm changes.
-
-/datum/alarm_handler/process()
- for(var/datum/alarm/A in alarms)
- A.process()
- check_alarm_cleared(A)
-
-/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
- var/new_alarm
- //Proper origin and source mandatory
- if(!(origin && source))
- return
- origin = origin.get_alarm_origin()
-
- new_alarm = 0
- //see if there is already an alarm of this origin
- var/datum/alarm/existing = alarms_assoc[origin]
- if(existing)
- existing.set_source_data(source, duration, severity)
- else
- existing = new/datum/alarm(origin, source, duration, severity)
- new_alarm = 1
-
- alarms |= existing
- alarms_assoc[origin] = existing
- if(new_alarm)
- alarms = dd_sortedObjectList(alarms)
- on_alarm_change(existing, ALARM_RAISED)
-
- return new_alarm
-
-/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source)
- //Proper origin and source mandatory
- if(!(origin && source))
- return
- origin = origin.get_alarm_origin()
-
- var/datum/alarm/existing = alarms_assoc[origin]
- if(existing)
- existing.clear(source)
- return check_alarm_cleared(existing)
-
-/datum/alarm_handler/proc/has_major_alarms()
- if(alarms && alarms.len)
- return 1
- return 0
-
-/datum/alarm_handler/proc/major_alarms()
- return alarms
-
-/datum/alarm_handler/proc/minor_alarms()
- return alarms
-
-/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm)
- if((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len)
- alarms -= alarm
- alarms_assoc -= alarm.origin
- on_alarm_change(alarm, ALARM_CLEARED)
- return 1
- return 0
-
-/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
- for(var/obj/machinery/camera/C in alarm.cameras())
- if(was_raised)
- C.network.Add(category)
- else
- C.network.Remove(category)
- notify_listeners(alarm, was_raised)
-
-/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin)
- if(!origin)
- return
-
- origin = origin.get_alarm_origin()
- var/datum/alarm/existing = alarms_assoc[origin]
- if(!existing)
- return
-
- return existing.max_severity()
-
-/atom/proc/get_alarm_origin()
- return src
-
-/turf/get_alarm_origin()
- var/area/area = get_area(src)
- return area // Very important to get area.master, as dynamic lightning can and will split areas.
-
-/datum/alarm_handler/proc/register(var/object, var/procName)
- listeners[object] = procName
-
-/datum/alarm_handler/proc/unregister(var/object)
- listeners -= object
-
-/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised)
- for(var/listener in listeners)
- call(listener, listeners[listener])(src, alarm, was_raised)
diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm
deleted file mode 100644
index cc29b40ac44..00000000000
--- a/code/modules/alarm/atmosphere_alarm.dm
+++ /dev/null
@@ -1,19 +0,0 @@
-/datum/alarm_handler/atmosphere
- category = "Atmosphere Alarms"
-
-/datum/alarm_handler/atmosphere/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
- ..()
-
-/datum/alarm_handler/atmosphere/major_alarms()
- var/list/major_alarms = new()
- for(var/datum/alarm/A in alarms)
- if(A.max_severity() > 1)
- major_alarms.Add(A)
- return major_alarms
-
-/datum/alarm_handler/atmosphere/minor_alarms()
- var/list/minor_alarms = new()
- for(var/datum/alarm/A in alarms)
- if(A.max_severity() == 1)
- minor_alarms.Add(A)
- return minor_alarms
diff --git a/code/modules/alarm/burglar_alarm.dm b/code/modules/alarm/burglar_alarm.dm
deleted file mode 100644
index c55cb12deef..00000000000
--- a/code/modules/alarm/burglar_alarm.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/datum/alarm_handler/burglar
- category = "Burglar Alarms"
diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm
deleted file mode 100644
index bef53ad466f..00000000000
--- a/code/modules/alarm/camera_alarm.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/datum/alarm_handler/camera
- category = "Camera Alarms"
diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm
deleted file mode 100644
index dfae3cc8177..00000000000
--- a/code/modules/alarm/fire_alarm.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/datum/alarm_handler/fire
- category = "Fire Alarms"
-
-/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
- var/area/A = alarm.origin
- if(istype(A))
- if(was_raised)
- A.fire_alert()
- else
- A.fire_reset()
- ..()
diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm
deleted file mode 100644
index fd7e6febe48..00000000000
--- a/code/modules/alarm/motion_alarm.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/datum/alarm_handler/motion
- category = "Motion Alarms"
diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm
deleted file mode 100644
index 4a0947a8f94..00000000000
--- a/code/modules/alarm/power_alarm.dm
+++ /dev/null
@@ -1,10 +0,0 @@
-/datum/alarm_handler/power
- category = "Power Alarms"
-
-/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
- var/area/A = alarm.origin
- if(istype(A))
- A.power_alert(was_raised)
- ..()
-
-/area/proc/power_alert(var/alarming)
diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm
index 5fea7baa019..6567dd62ede 100644
--- a/code/modules/assembly/assembly.dm
+++ b/code/modules/assembly/assembly.dm
@@ -1,3 +1,9 @@
+#define WIRE_RECEIVE (1<<0) //Allows pulse(0) to call Activate()
+#define WIRE_PULSE (1<<1) //Allows pulse(0) to act on the holder
+#define WIRE_PULSE_SPECIAL (1<<2) //Allows pulse(0) to act on the holders special assembly
+#define WIRE_RADIO_RECEIVE (1<<3) //Allows pulse(1) to call Activate()
+#define WIRE_RADIO_PULSE (1<<4) //Allows pulse(1) to send a radio message
+
/obj/item/assembly
name = "assembly"
desc = "A small electronic device that should never exist."
@@ -22,21 +28,12 @@
var/wires = WIRE_RECEIVE | WIRE_PULSE
var/datum/wires/connected = null // currently only used by timer/signaler
- var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate()
- var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder
- var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly
- var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate()
- var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message
-
/obj/item/assembly/proc/activate() //What the device does when turned on
return
/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
return
-/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
- return
-
/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here
return
@@ -79,7 +76,11 @@
activate()
return TRUE
-/obj/item/assembly/pulse(radio = FALSE)
+//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
+/obj/item/assembly/proc/pulse(radio = FALSE)
+ if(connected && wires)
+ connected.pulse_assembly(src)
+ return TRUE
if(holder && (wires & WIRE_PULSE))
holder.process_activation(src, 1, 0)
if(holder && (wires & WIRE_PULSE_SPECIAL))
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 2759d12c21a..0c3fcfec3ac 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -128,12 +128,6 @@
if(usr)
GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")
-/obj/item/assembly/signaler/pulse(var/radio = FALSE)
- if(connected && wires)
- connected.Pulse(src)
- else
- return ..(radio)
-
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
if(!receiving || !signal)
return FALSE
diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm
index 4993c8502ea..7e277677f94 100644
--- a/code/modules/client/preference/loadout/loadout_hat.dm
+++ b/code/modules/client/preference/loadout/loadout_hat.dm
@@ -166,7 +166,3 @@
/datum/gear/hat/flowerpin
display_name = "hair flower"
path = /obj/item/clothing/head/hairflower
-
-/datum/gear/hat/kitty
- display_name = "kitty headband"
- path = /obj/item/clothing/head/kitty
diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm
index ea8f4198f50..d00751e2cfb 100644
--- a/code/modules/clothing/ears/ears.dm
+++ b/code/modules/clothing/ears/ears.dm
@@ -8,21 +8,3 @@
strip_delay = 15
put_on_delay = 25
resistance_flags = FLAMMABLE
-
-/obj/item/clothing/ears/headphones
- name = "headphones"
- desc = "Unce unce unce unce."
- var/on = 0
- icon_state = "headphones0"
- item_state = null
- actions_types = list(/datum/action/item_action/toggle_headphones)
-
-/obj/item/clothing/ears/headphones/attack_self(mob/user)
- on = !on
- icon_state = "headphones[on]"
-
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
-
- user.update_inv_ears()
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 24bef5398a8..bdcd27c24e3 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -11,7 +11,8 @@
resistance_flags = NONE
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
sprite_sheets = list(
- "Vox" = 'icons/mob/species/vox/suit.dmi'
+ "Vox" = 'icons/mob/species/vox/suit.dmi',
+ "Grey" = 'icons/mob/species/grey/suit.dmi'
)
w_class = WEIGHT_CLASS_NORMAL
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index a7bd3fb2627..6ffa40fe727 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -162,7 +162,7 @@
desc = "A slick, authoritative cloak designed for the Chief Engineer."
icon_state = "cemantle"
item_state = "cemantle"
- allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd)
+ allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd)
//Chief Medical Officer
/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer
@@ -231,7 +231,7 @@
icon_state = "hazard"
item_state = "hazard"
blood_overlay_type = "armor"
- allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen)
+ allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen, /obj/item/rcd, /obj/item/rpd)
resistance_flags = NONE
sprite_sheets = list(
diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm
index a0d6823fe80..aae3b84a3c5 100644
--- a/code/modules/crafting/craft.dm
+++ b/code/modules/crafting/craft.dm
@@ -357,7 +357,7 @@
if(!fail_msg)
to_chat(usr, "[TR.name] constructed.")
if(TR.alert_admins_on_craft)
- message_admins("[usr.ckey] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
+ message_admins("[key_name_admin(usr)] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
else
to_chat(usr, "Construction failed[fail_msg]")
busy = FALSE
diff --git a/code/modules/events/apc_short.dm b/code/modules/events/apc_short.dm
index c19b6cbe8b7..fcb31972204 100644
--- a/code/modules/events/apc_short.dm
+++ b/code/modules/events/apc_short.dm
@@ -46,10 +46,10 @@
if(prob(APC_BREAK_PROBABILITY))
// if it has internal wires, cut the power wires
if(C.wires)
- if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER1))
- C.wires.CutWireIndex(APC_WIRE_MAIN_POWER1)
- if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER2))
- C.wires.CutWireIndex(APC_WIRE_MAIN_POWER2)
+ if(!C.wires.is_cut(WIRE_MAIN_POWER1))
+ C.wires.cut(WIRE_MAIN_POWER1)
+ if(!C.wires.is_cut(WIRE_MAIN_POWER2))
+ C.wires.cut(WIRE_MAIN_POWER2)
// if it was operating, toggle off the breaker
if(C.operating)
C.toggle_breaker()
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 8d91635c458..35cc199c3ff 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -32,7 +32,7 @@
log_debug("Original brand intelligence machine: [originMachine] ([ADMIN_VV(originMachine,"VV")]) [ADMIN_JMP(originMachine)]")
/datum/event/brand_intelligence/tick()
- if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.IsAllCut()) //if the original vending machine is missing or has it's voice switch flipped
+ if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped
for(var/obj/machinery/vending/saved in infectedMachines)
saved.shoot_inventory = 0
if(originMachine)
diff --git a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
index d5d114fb43f..7b3349e4c2d 100644
--- a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
@@ -7,6 +7,7 @@
materials = list(MAT_GLASS=100)
var/light_intensity = 2
light_color = LIGHT_COLOR_LIGHTBLUE
+ resistance_flags = FLAMMABLE
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change()
if(!isShotFlammable() && (resistance_flags & ON_FIRE))
@@ -30,6 +31,7 @@
overlays += filling
name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
if(resistance_flags & ON_FIRE)
+ cut_overlay(GLOB.fire_overlay, TRUE)
overlays += "shotglass_fire"
name = "flaming [name]"
else
diff --git a/code/modules/food_and_drinks/food/foods/meat.dm b/code/modules/food_and_drinks/food/foods/meat.dm
index 382e2f39022..a9f0ab3d684 100644
--- a/code/modules/food_and_drinks/food/foods/meat.dm
+++ b/code/modules/food_and_drinks/food/foods/meat.dm
@@ -30,7 +30,7 @@
name = "-meat"
var/subjectname = ""
var/subjectjob = null
- tastes = list("tender meat" = 1)
+ tastes = list("salty meat" = 1)
/obj/item/reagent_containers/food/snacks/meat/slab/meatproduct
name = "meat product"
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index f179de1459a..f6bb3c8e1c4 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -70,6 +70,7 @@
max_n_of_items = 1500 * B.rating
/obj/machinery/smartfridge/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
for(var/atom/movable/A in contents)
A.forceMove(loc)
diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm
index 612146e579a..5a73313601a 100644
--- a/code/modules/hydroponics/beekeeping/beebox.dm
+++ b/code/modules/hydroponics/beekeeping/beebox.dm
@@ -198,10 +198,17 @@
return
return ..()
+/obj/structure/beebox/crowbar_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.use_tool(src, user, 0))
+ return
+ TOOL_ATTEMPT_DISMANTLE_MESSAGE
+ if(I.use_tool(src, user, 50, volume = I.tool_volume))
+ TOOL_DISMANTLE_SUCCESS_MESSAGE
+ deconstruct(disassembled = TRUE)
+
/obj/structure/beebox/wrench_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_use_check(user, 0))
- return
default_unfasten_wrench(user, I, time = 20)
/obj/structure/beebox/attack_hand(mob/user)
@@ -261,15 +268,18 @@
visible_message("[user] removes the queen from the apiary.")
queen_bee = null
-/obj/structure/beebox/deconstruct(disassembled = TRUE)
- new /obj/item/stack/sheet/wood(loc, 20)
+/obj/structure/beebox/deconstruct(disassembled = FALSE)
+ var/mat_drop = 20
+ if(disassembled)
+ mat_drop = 40
+ new /obj/item/stack/sheet/wood(loc, mat_drop)
for(var/mob/living/simple_animal/hostile/poison/bees/B in bees)
if(B.loc == src)
B.forceMove(drop_location())
for(var/obj/item/honey_frame/HF in honey_frames)
HF.forceMove(drop_location())
honey_frames -= HF
- qdel(src)
+ ..()
/obj/structure/beebox/unwrenched
anchored = FALSE
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
index f194c48e019..5031ac71373 100644
--- a/code/modules/hydroponics/fermenting_barrel.dm
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "barrel"
density = TRUE
- anchored = FALSE
+ anchored = TRUE
container_type = DRAINABLE | AMOUNT_VISIBLE
pressure_resistance = 2 * ONE_ATMOSPHERE
max_integrity = 300
@@ -65,6 +65,26 @@
to_chat(user, "You close [src], letting you draw from its tap.")
update_icon()
+/obj/structure/fermenting_barrel/crowbar_act(mob/living/user, obj/item/I)
+ . = TRUE
+ if(!I.use_tool(src, user, 0))
+ return
+ TOOL_ATTEMPT_DISMANTLE_MESSAGE
+ if(I.use_tool(src, user, 50, volume = I.tool_volume))
+ TOOL_DISMANTLE_SUCCESS_MESSAGE
+ deconstruct(disassembled = TRUE)
+
+/obj/structure/fermenting_barrel/wrench_act(mob/living/user, obj/item/I)
+ . = TRUE
+ default_unfasten_wrench(user, I, time = 20)
+
+/obj/structure/fermenting_barrel/deconstruct(disassembled = FALSE)
+ var/mat_drop = 15
+ if(disassembled)
+ mat_drop = 30
+ new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
+ ..()
+
/obj/structure/fermenting_barrel/update_icon()
if(open)
icon_state = "barrel_open"
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index d6f8cae7519..0f2b54ab971 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -29,7 +29,8 @@
if(istype(W,/obj/item/reagent_containers/syringe))
if(!contains_sample)
for(var/datum/reagent/blood/bloodSample in W.reagents.reagent_list)
- if(bloodSample.data["mind"] && bloodSample.data["cloneable"] == 1)
+ var/datum/dna/dna = bloodSample.data["dna"]
+ if(bloodSample.data["mind"] && bloodSample.data["cloneable"] && !(NO_SCAN in dna.species.species_traits))
var/datum/mind/tempmind = bloodSample.data["mind"]
if(tempmind.is_revivable())
mind = bloodSample.data["mind"]
diff --git a/code/modules/instruments/_instrument_data.dm b/code/modules/instruments/_instrument_data.dm
new file mode 100644
index 00000000000..2fde3de59fb
--- /dev/null
+++ b/code/modules/instruments/_instrument_data.dm
@@ -0,0 +1,113 @@
+/**
+ * Get all non admin_only instruments as a list of text ids.
+ */
+/proc/get_allowed_instrument_ids()
+ . = list()
+ for(var/id in SSinstruments.instrument_data)
+ var/datum/instrument/I = SSinstruments.instrument_data[id]
+ if(!I.admin_only)
+ . += I.id
+
+/**
+ * # Instrument Datums
+ *
+ * Instrument datums hold the data for any given instrument, as well as data on how to play it and what bounds there are to playing it.
+ *
+ * The datums themselves are kept in SSinstruments in a list by their unique ID. The reason it uses ID instead of typepath is to support the runtime creation of instruments.
+ * Since songs cache them while playing, there isn't realistic issues regarding performance from accessing.
+ */
+/datum/instrument
+ /// Name of the instrument
+ var/name = "Generic instrument"
+ /// Uniquely identifies this instrument so runtime changes are possible as opposed to paths. If this is unset, things will use path instead.
+ var/id
+ /// Category
+ var/category = "Unsorted"
+ /// Used for categorization subtypes
+ var/abstract_type = /datum/instrument
+ /// Write here however many samples, follow this syntax: "%note num%"='%sample file%' eg. "27"='synthesizer/e2.ogg'. Key must never be lower than 0 and higher than 127
+ var/list/real_samples
+ /// assoc list key = /datum/instrument_key. do not fill this yourself!
+ var/list/samples
+ /// See __DEFINES/flags/instruments.dm
+ var/instrument_flags = NONE
+ /// For legacy instruments, the path to our notes
+ var/legacy_instrument_path
+ /// For legacy instruments, our file extension
+ var/legacy_instrument_ext
+ /// What songs are using us
+ var/list/datum/song/songs_using = list()
+ /// Don't touch this
+ var/static/HIGHEST_KEY = 127
+ /// Don't touch this x2
+ var/static/LOWEST_KEY = 0
+ /// Oh no - For truly troll instruments.
+ var/admin_only = FALSE
+ /// Volume multiplier. Synthesized instruments are quite loud and I don't like to cut off potential detail via editing. (someone correct me if this isn't a thing)
+ var/volume_multiplier = 0.33
+
+/datum/instrument/New()
+ if(isnull(id))
+ id = "[type]"
+
+/datum/instrument/Destroy()
+ SSinstruments.instrument_data -= id
+ for(var/i in songs_using)
+ var/datum/song/S = i
+ S.set_instrument(null)
+ real_samples = null
+ samples = null
+ songs_using = null
+ return ..()
+
+/**
+ * Initializes the instrument, calculating its samples if necessary.
+ */
+/datum/instrument/proc/Initialize()
+ if(instrument_flags & (INSTRUMENT_LEGACY | INSTRUMENT_DO_NOT_AUTOSAMPLE))
+ return
+ calculate_samples()
+
+/**
+ * Checks if this instrument is ready to play.
+ */
+/datum/instrument/proc/is_ready()
+ if(instrument_flags & INSTRUMENT_LEGACY)
+ return legacy_instrument_path && legacy_instrument_ext
+ else if(instrument_flags & INSTRUMENT_DO_NOT_AUTOSAMPLE)
+ return length(samples)
+ return length(samples) >= 128
+
+/**
+ * For synthesized instruments, this is how the instrument generates the "keys" that a [/datum/song] uses to play notes.
+ * Calculating them on the fly would be unperformant, so we do it during init and keep it all cached in a list.
+ */
+/datum/instrument/proc/calculate_samples()
+ if(!length(real_samples))
+ CRASH("No real samples defined for [id] [type] on calculate_samples() call.")
+ var/list/real_keys = list()
+ samples = list()
+ for(var/key in real_samples)
+ real_keys += text2num(key)
+ sortTim(real_keys, /proc/cmp_numeric_asc, associative = FALSE)
+
+ for(var/i in 1 to (length(real_keys) - 1))
+ var/from_key = real_keys[i]
+ var/to_key = real_keys[i + 1]
+ var/sample1 = real_samples[num2text(from_key)]
+ var/sample2 = real_samples[num2text(to_key)]
+ var/pivot = FLOOR((from_key + to_key) / 2, 1) //original code was a round but I replaced it because that's effectively a floor, thanks Baystation! who knows what was intended.
+ for(var/key in from_key to pivot)
+ samples[num2text(key)] = new /datum/instrument_key(sample1, key, key - from_key)
+ for(var/key in (pivot + 1) to to_key)
+ samples[num2text(key)] = new /datum/instrument_key(sample2, key, key - to_key)
+
+ // Fill in 0 to first key and last key to 127
+ var/first_key = real_keys[1]
+ var/last_key = real_keys[length(real_keys)]
+ var/first_sample = real_samples[num2text(first_key)]
+ var/last_sample = real_samples[num2text(last_key)]
+ for(var/key in LOWEST_KEY to (first_key - 1))
+ samples[num2text(key)] = new /datum/instrument_key(first_sample, key, key - first_key)
+ for(var/key in last_key to HIGHEST_KEY)
+ samples[num2text(key)] = new /datum/instrument_key(last_sample, key, key - last_key)
diff --git a/code/modules/instruments/_instrument_key.dm b/code/modules/instruments/_instrument_key.dm
new file mode 100644
index 00000000000..5c7cc0ce372
--- /dev/null
+++ b/code/modules/instruments/_instrument_key.dm
@@ -0,0 +1,33 @@
+#define KEY_TWELTH (1/12)
+
+/**
+ * Instrument key datums contain everything needed to know how to play a specific
+ * note of an instrument.*
+ */
+/datum/instrument_key
+ /// The numerical key of what this is, from 1 to 127 on a standard piano keyboard.
+ var/key
+ /// The actual sample file that will be loaded when playing.
+ var/sample
+ /// The frequency to play the sample to get our desired note.
+ var/frequency
+ /// Deviation up/down from the pivot point that uses its sample. Used to calculate frequency.
+ var/deviation
+
+/datum/instrument_key/New(sample, key, deviation, frequency)
+ src.sample = sample
+ src.key = key
+ src.deviation = deviation
+ src.frequency = frequency
+ if(!frequency && deviation)
+ calculate()
+
+/**
+ * Calculates and stores our deviation.
+ */
+/datum/instrument_key/proc/calculate()
+ if(!deviation)
+ CRASH("Invalid calculate call: No deviation or sample in instrument_key")
+ frequency = 2 ** (KEY_TWELTH * deviation)
+
+#undef KEY_TWELTH
diff --git a/code/modules/instruments/brass.dm b/code/modules/instruments/brass.dm
new file mode 100644
index 00000000000..7f8f103831f
--- /dev/null
+++ b/code/modules/instruments/brass.dm
@@ -0,0 +1,26 @@
+/datum/instrument/brass
+ name = "Generic brass instrument"
+ category = "Brass"
+ abstract_type = /datum/instrument/brass
+
+/datum/instrument/brass/crisis_section
+ name = "Crisis Brass Section"
+ id = "crbrass"
+ real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg')
+
+/datum/instrument/brass/crisis_trombone
+ name = "Crisis Trombone"
+ id = "crtrombone"
+ real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg')
+
+/datum/instrument/brass/crisis_trumpet
+ name = "Crisis Trumpet"
+ id = "crtrumpet"
+ real_samples = list("60"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg')
diff --git a/code/modules/instruments/chromatic_percussion.dm b/code/modules/instruments/chromatic_percussion.dm
new file mode 100644
index 00000000000..cafa9e31edb
--- /dev/null
+++ b/code/modules/instruments/chromatic_percussion.dm
@@ -0,0 +1,31 @@
+/datum/instrument/chromatic
+ name = "Generic chromatic percussion instrument"
+ category = "Chromatic percussion"
+ abstract_type = /datum/instrument/chromatic
+
+/datum/instrument/chromatic/vibraphone1
+ name = "Crisis Vibraphone"
+ id = "crvibr"
+ real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg')
+
+/datum/instrument/chromatic/musicbox1
+ name = "SGM Music Box"
+ id = "sgmmbox"
+ real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg')
+
+/datum/instrument/chromatic/fluid_celeste
+ name = "FluidR3 Celeste"
+ id = "r3celeste"
+ real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg',
+ "84"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg',
+ "96"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg',
+ "108"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg')
diff --git a/code/modules/instruments/fun.dm b/code/modules/instruments/fun.dm
new file mode 100644
index 00000000000..5a9b1292c42
--- /dev/null
+++ b/code/modules/instruments/fun.dm
@@ -0,0 +1,19 @@
+/datum/instrument/fun
+ name = "Generic Fun Instrument"
+ category = "Fun"
+ abstract_type = /datum/instrument/fun
+
+/datum/instrument/fun/honk
+ name = "!!HONK!!"
+ id = "honk"
+ real_samples = list("74"='sound/items/bikehorn.ogg') // Cluwne Heaven
+
+/datum/instrument/fun/signal
+ name = "Ping"
+ id = "ping"
+ real_samples = list("79"='sound/machines/ping.ogg')
+
+/datum/instrument/fun/chime
+ name = "Chime"
+ id = "chime"
+ real_samples = list("79"='sound/machines/chime.ogg')
diff --git a/code/modules/instruments/guitar.dm b/code/modules/instruments/guitar.dm
new file mode 100644
index 00000000000..be7cfbe467b
--- /dev/null
+++ b/code/modules/instruments/guitar.dm
@@ -0,0 +1,36 @@
+/datum/instrument/guitar
+ name = "Generic guitar-like instrument"
+ category = "Guitar"
+ abstract_type = /datum/instrument/guitar
+
+/datum/instrument/guitar/steel_crisis
+ name = "Crisis Steel String Guitar"
+ id = "csteelgt"
+ real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg')
+
+/datum/instrument/guitar/nylon_crisis
+ name = "Crisis Nylon String Guitar"
+ id = "cnylongt"
+ real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg')
+
+/datum/instrument/guitar/clean_crisis
+ name = "Crisis Clean Guitar"
+ id = "ccleangt"
+ real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg')
+
+/datum/instrument/guitar/muted_crisis
+ name = "Crisis Muted Guitar"
+ id = "cmutedgt"
+ real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg')
diff --git a/code/modules/instruments/hardcoded.dm b/code/modules/instruments/hardcoded.dm
new file mode 100644
index 00000000000..5db7e8b3bb4
--- /dev/null
+++ b/code/modules/instruments/hardcoded.dm
@@ -0,0 +1,86 @@
+//THESE ARE HARDCODED INSTRUMENT SAMPLES.
+//SONGS WILL BE AUTOMATICALLY SWITCHED TO LEGACY MODE IF THEY USE THIS KIND OF INSTRUMENT!
+//I'd prefer these stayed. They sound different from the mechanical synthesis of synthed instruments, and I quite like them that way. It's not legacy, it's hardcoded, old style. - kevinz000
+/datum/instrument/hardcoded
+ abstract_type = /datum/instrument/hardcoded
+ category = "Non-Synthesized"
+ instrument_flags = INSTRUMENT_LEGACY
+ volume_multiplier = 1 //not as loud as synth'd
+
+/datum/instrument/hardcoded/accordion
+ name = "Accordion"
+ id = "accordion"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "accordion"
+
+/datum/instrument/hardcoded/bikehorn
+ name = "Bike Horn"
+ id = "bikehorn"
+ legacy_instrument_ext = "ogg"
+ legacy_instrument_path = "bikehorn"
+
+/datum/instrument/hardcoded/eguitar
+ name = "Electric Guitar"
+ id = "eguitar"
+ legacy_instrument_ext = "ogg"
+ legacy_instrument_path = "eguitar"
+
+/datum/instrument/hardcoded/glockenspiel
+ name = "Glockenspiel"
+ id = "glockenspiel"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "glockenspiel"
+
+/datum/instrument/hardcoded/guitar
+ name = "Guitar"
+ id = "guitar"
+ legacy_instrument_ext = "ogg"
+ legacy_instrument_path = "guitar"
+
+/datum/instrument/hardcoded/harmonica
+ name = "Harmonica"
+ id = "harmonica"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "harmonica"
+
+/datum/instrument/hardcoded/piano
+ name = "Piano"
+ id = "piano"
+ legacy_instrument_ext = "ogg"
+ legacy_instrument_path = "piano"
+
+/datum/instrument/hardcoded/recorder
+ name = "Recorder"
+ id = "recorder"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "recorder"
+
+/datum/instrument/hardcoded/saxophone
+ name = "Saxophone"
+ id = "saxophone"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "saxophone"
+
+/datum/instrument/hardcoded/trombone
+ name = "Trombone"
+ id = "trombone"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "trombone"
+
+/datum/instrument/hardcoded/violin
+ name = "Violin"
+ id = "violin"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "violin"
+
+/datum/instrument/hardcoded/xylophone
+ name = "Xylophone"
+ id = "xylophone"
+ legacy_instrument_ext = "mid"
+ legacy_instrument_path = "xylophone"
+
+/datum/instrument/hardcoded/banjo
+ name = "Banjo"
+ id = "banjo"
+ legacy_instrument_ext = "ogg"
+ legacy_instrument_path = "banjo"
diff --git a/code/modules/instruments/objs/items/_instrument.dm b/code/modules/instruments/objs/items/_instrument.dm
new file mode 100644
index 00000000000..936a9b4c85d
--- /dev/null
+++ b/code/modules/instruments/objs/items/_instrument.dm
@@ -0,0 +1,53 @@
+//copy pasta of the space piano, don't hurt me -Pete
+/obj/item/instrument
+ name = "generic instrument"
+ force = 10
+ max_integrity = 100
+ resistance_flags = FLAMMABLE
+ icon = 'icons/obj/musician.dmi'
+ lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi'
+ /// Our song datum.
+ var/datum/song/handheld/song
+ /// Our allowed list of instrument ids. This is nulled on initialize.
+ var/list/allowed_instrument_ids
+ /// How far away our song datum can be heard.
+ var/instrument_range = 15
+
+/obj/item/instrument/Initialize(mapload)
+ . = ..()
+ song = new(src, allowed_instrument_ids, instrument_range)
+ allowed_instrument_ids = null //We don't need this clogging memory after it's used.
+
+/obj/item/instrument/Destroy()
+ QDEL_NULL(song)
+ return ..()
+
+/obj/item/instrument/suicide_act(mob/user)
+ user.visible_message("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!")
+ return BRUTELOSS
+
+/obj/item/instrument/attack_self(mob/user)
+ tgui_interact(user)
+
+/obj/item/instrument/tgui_data(mob/user)
+ return song.tgui_data(user)
+
+/obj/item/instrument/tgui_interact(mob/user)
+ if(!isliving(user) || user.incapacitated())
+ return
+ song.tgui_interact(user)
+
+/obj/item/instrument/tgui_act(action, params)
+ if(..())
+ return
+ return song.tgui_act(action, params)
+
+/**
+ * Whether the instrument should stop playing
+ *
+ * Arguments:
+ * * user - The user
+ */
+/obj/item/instrument/proc/should_stop_playing(mob/user)
+ return !(src in user) || !isliving(user) || user.incapacitated()
diff --git a/code/modules/instruments/objs/items/headphones.dm b/code/modules/instruments/objs/items/headphones.dm
new file mode 100644
index 00000000000..ef1a76bdc3b
--- /dev/null
+++ b/code/modules/instruments/objs/items/headphones.dm
@@ -0,0 +1,80 @@
+/obj/item/clothing/ears/headphones
+ name = "headphones"
+ desc = "Unce unce unce unce."
+ icon_state = "headphones0"
+ item_state = "headphones0"
+ actions_types = list(/datum/action/item_action/change_headphones_song)
+ var/datum/song/headphones/song
+
+/obj/item/clothing/ears/headphones/Initialize(mapload)
+ . = ..()
+ song = new(src, "piano") // Piano is the default instrument but all instruments are allowed
+ song.instrument_range = 0
+ song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
+ // To update the icon
+ RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing)
+ RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing)
+
+/obj/item/clothing/ears/headphones/Destroy()
+ QDEL_NULL(song)
+ return ..()
+
+/obj/item/clothing/ears/headphones/attack_self(mob/user)
+ tgui_interact(user)
+
+/obj/item/clothing/ears/headphones/tgui_data(mob/user)
+ return song.tgui_data(user)
+
+/obj/item/clothing/ears/headphones/tgui_interact(mob/user)
+ if(should_stop_playing(user) || user.incapacitated())
+ return
+ song.tgui_interact(user)
+
+/obj/item/clothing/ears/headphones/tgui_act(action, params)
+ if(..())
+ return
+ return song.tgui_act(action, params)
+
+/obj/item/clothing/ears/headphones/update_icon()
+ var/mob/living/carbon/human/user = loc
+ if(istype(user))
+ user.update_action_buttons_icon()
+ user.update_inv_ears()
+ ..()
+
+/obj/item/clothing/ears/headphones/item_action_slot_check(slot)
+ if(slot == slot_l_ear || slot == slot_r_ear)
+ return TRUE
+
+/**
+ * Called by a component signal when our song starts playing.
+ */
+/obj/item/clothing/ears/headphones/proc/start_playing()
+ icon_state = item_state = "headphones1"
+ update_icon()
+
+/**
+ * Called by a component signal when our song stops playing.
+ */
+/obj/item/clothing/ears/headphones/proc/stop_playing()
+ icon_state = item_state = "headphones0"
+ update_icon()
+
+/**
+ * Whether the headphone's song should stop playing
+ *
+ * Arguments:
+ * * user - The user
+ */
+/obj/item/clothing/ears/headphones/proc/should_stop_playing(mob/living/carbon/human/user)
+ return !(src in user) || !istype(user) || !((src == user.l_ear) || (src == user.r_ear))
+
+// special subtype so it uses the correct item type
+/datum/song/headphones
+
+/datum/song/headphones/should_stop_playing(mob/user)
+ . = ..()
+ if(.)
+ return TRUE
+ var/obj/item/clothing/ears/headphones/I = parent
+ return I.should_stop_playing(user)
diff --git a/code/game/objects/items/devices/instruments.dm b/code/modules/instruments/objs/items/instruments.dm
similarity index 54%
rename from code/game/objects/items/devices/instruments.dm
rename to code/modules/instruments/objs/items/instruments.dm
index 53ead129836..2426de9a5c9 100644
--- a/code/game/objects/items/devices/instruments.dm
+++ b/code/modules/instruments/objs/items/instruments.dm
@@ -1,55 +1,10 @@
-//copy pasta of the space piano, don't hurt me -Pete
-/obj/item/instrument
- name = "generic instrument"
- icon = 'icons/obj/musician.dmi'
- lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi'
- resistance_flags = FLAMMABLE
- max_integrity = 100
- var/datum/song/handheld/song
- var/instrumentId = "generic"
- var/instrumentExt = "mid"
-
-/obj/item/instrument/New()
- song = new(instrumentId, src, instrumentExt)
- ..()
-
-/obj/item/instrument/Destroy()
- QDEL_NULL(song)
- return ..()
-
-/obj/item/instrument/suicide_act(mob/user)
- user.visible_message("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!")
- return BRUTELOSS
-
-/obj/item/instrument/Initialize(mapload)
- song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
- ..()
-
-/obj/item/instrument/attack_self(mob/user)
- ui_interact(user)
-
-/obj/item/instrument/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- if(!isliving(user) || user.incapacitated())
- return
-
- song.ui_interact(user, ui_key, ui, force_open)
-
-/obj/item/instrument/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- return song.ui_data(user, ui_key, state)
-
-/obj/item/instrument/Topic(href, href_list)
- song.Topic(href, href_list)
-
/obj/item/instrument/violin
name = "space violin"
desc = "A wooden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
icon_state = "violin"
item_state = "violin"
- instrumentExt = "ogg"
- force = 10
hitsound = "swing_hit"
- instrumentId = "violin"
+ allowed_instrument_ids = "violin"
/obj/item/instrument/violin/golden
name = "golden violin"
@@ -63,87 +18,146 @@
desc = "An advanced electronic synthesizer that can be used as various instruments."
icon_state = "synth"
item_state = "synth"
- instrumentId = "piano"
- instrumentExt = "ogg"
- var/static/list/insTypes = list("accordion" = "mid", "glockenspiel" = "mid", "guitar" = "ogg", "eguitar" = "ogg", "harmonica" = "mid", "piano" = "ogg", "recorder" = "mid", "saxophone" = "mid", "trombone" = "mid", "violin" = "ogg", "xylophone" = "mid")
- actions_types = list(/datum/action/item_action/synthswitch)
+ allowed_instrument_ids = "piano"
-/obj/item/instrument/piano_synth/proc/changeInstrument(name = "piano")
- song.instrumentDir = name
- song.instrumentExt = insTypes[name]
+/obj/item/instrument/piano_synth/Initialize(mapload)
+ . = ..()
+ song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
+
+/obj/item/instrument/banjo
+ name = "banjo"
+ desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings."
+ icon_state = "banjo"
+ item_state = "banjo"
+ attack_verb = list("scruggs-styled", "hum-diggitied", "shin-digged", "clawhammered")
+ hitsound = 'sound/weapons/banjoslap.ogg'
+ allowed_instrument_ids = "banjo"
/obj/item/instrument/guitar
name = "guitar"
desc = "It's made of wood and has bronze strings."
icon_state = "guitar"
item_state = "guitar"
- instrumentExt = "ogg"
- force = 10
attack_verb = list("played metal on", "serenaded", "crashed", "smashed")
- hitsound = 'sound/effects/guitarsmash.ogg'
- instrumentId = "guitar"
+ hitsound = 'sound/weapons/guitarslam.ogg'
+ allowed_instrument_ids = "guitar"
/obj/item/instrument/eguitar
name = "electric guitar"
desc = "Makes all your shredding needs possible."
icon_state = "eguitar"
item_state = "eguitar"
- instrumentExt = "ogg"
force = 12
attack_verb = list("played metal on", "shredded", "crashed", "smashed")
hitsound = 'sound/weapons/stringsmash.ogg'
- instrumentId = "eguitar"
+ allowed_instrument_ids = "eguitar"
/obj/item/instrument/glockenspiel
name = "glockenspiel"
desc = "Smooth metal bars perfect for any marching band."
icon_state = "glockenspiel"
item_state = "glockenspiel"
- instrumentId = "glockenspiel"
+ allowed_instrument_ids = "glockenspiel"
/obj/item/instrument/accordion
name = "accordion"
desc = "Pun-Pun not included."
icon_state = "accordion"
item_state = "accordion"
- instrumentId = "accordion"
+ allowed_instrument_ids = "accordion"
+
+/obj/item/instrument/trumpet
+ name = "trumpet"
+ desc = "To announce the arrival of the king!"
+ icon_state = "trumpet"
+ item_state = "trumpet"
+ allowed_instrument_ids = "trombone"
+
+/obj/item/instrument/trumpet/spectral
+ name = "spectral trumpet"
+ desc = "Things are about to get spooky!"
+ icon_state = "spectral_trumpet"
+ item_state = "spectral_trumpet"
+ force = 0
+ attack_verb = list("played", "jazzed", "trumpeted", "mourned", "dooted", "spooked")
+
+/obj/item/instrument/trumpet/spectral/Initialize()
+ . = ..()
+ AddComponent(/datum/component/spooky)
+
+/obj/item/instrument/trumpet/spectral/attack(mob/living/carbon/C, mob/user)
+ playsound(src, 'sound/instruments/trombone/En4.mid', 100, 1, -1)
+ ..()
/obj/item/instrument/saxophone
name = "saxophone"
desc = "This soothing sound will be sure to leave your audience in tears."
icon_state = "saxophone"
item_state = "saxophone"
- instrumentId = "saxophone"
+ allowed_instrument_ids = "saxophone"
+
+/obj/item/instrument/saxophone/spectral
+ name = "spectral saxophone"
+ desc = "This spooky sound will be sure to leave mortals in bones."
+ icon_state = "saxophone"
+ item_state = "saxophone"
+ force = 0
+ attack_verb = list("played", "jazzed", "saxxed", "mourned", "dooted", "spooked")
+
+/obj/item/instrument/saxophone/spectral/Initialize()
+ . = ..()
+ AddComponent(/datum/component/spooky)
+
+/obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user)
+ playsound(src, 'sound/instruments/saxophone/En4.mid', 100,1,-1)
+ ..()
/obj/item/instrument/trombone
name = "trombone"
desc = "How can any pool table ever hope to compete?"
icon_state = "trombone"
+ allowed_instrument_ids = "trombone"
item_state = "trombone"
- instrumentId = "trombone"
+
+/obj/item/instrument/trombone/spectral
+ name = "spectral trombone"
+ desc = "A skeleton's favorite instrument. Apply directly on the mortals."
+ icon_state = "trombone"
+ item_state = "trombone"
+ force = 0
+ attack_verb = list("played", "jazzed", "tromboned", "mourned", "dooted", "spooked")
+
+/obj/item/instrument/trombone/spectral/Initialize()
+ . = ..()
+ AddComponent(/datum/component/spooky)
+
+/obj/item/instrument/trombone/spectral/attack(mob/living/carbon/C, mob/user)
+ playsound (src, 'sound/instruments/trombone/Cn4.mid', 100,1,-1)
+ ..()
/obj/item/instrument/recorder
name = "recorder"
desc = "Just like in school, playing ability and all."
+ force = 5
icon_state = "recorder"
item_state = "recorder"
- instrumentId = "recorder"
+ allowed_instrument_ids = "recorder"
/obj/item/instrument/harmonica
name = "harmonica"
desc = "For when you get a bad case of the space blues."
icon_state = "harmonica"
item_state = "harmonica"
- instrumentId = "harmonica"
force = 5
w_class = WEIGHT_CLASS_SMALL
+ allowed_instrument_ids = "harmonica"
/obj/item/instrument/xylophone
name = "xylophone"
- desc = "a percussion instrument with a bright tone."
+ desc = "A percussion instrument with a bright tone."
icon_state = "xylophone"
item_state = "xylophone"
- instrumentId = "xylophone"
+ allowed_instrument_ids = "bikehorn"
/obj/item/instrument/bikehorn
name = "gilded bike horn"
@@ -153,14 +167,14 @@
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
attack_verb = list("beautifully honks")
- instrumentId = "bikehorn"
- instrumentExt = "ogg"
w_class = WEIGHT_CLASS_TINY
force = 0
throw_speed = 3
throw_range = 7
hitsound = 'sound/items/bikehorn.ogg'
+ allowed_instrument_ids = "bikehorn"
+// Crafting recipes
/datum/crafting_recipe/violin
name = "Violin"
result = /obj/item/instrument/violin
@@ -168,7 +182,7 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
- time = 80
+ time = 8 SECONDS
category = CAT_MISC
/datum/crafting_recipe/guitar
@@ -178,7 +192,7 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
- time = 80
+ time = 8 SECONDS
category = CAT_MISC
/datum/crafting_recipe/eguitar
@@ -188,5 +202,15 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
- time = 80
+ time = 8 SECONDS
+ category = CAT_MISC
+
+/datum/crafting_recipe/banjo
+ name = "Banjo"
+ result = /obj/item/instrument/banjo
+ reqs = list(/obj/item/stack/sheet/wood = 5,
+ /obj/item/stack/cable_coil = 6,
+ /obj/item/stack/tape_roll = 5)
+ tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
+ time = 8 SECONDS
category = CAT_MISC
diff --git a/code/modules/instruments/objs/structures/_musician.dm b/code/modules/instruments/objs/structures/_musician.dm
new file mode 100644
index 00000000000..542859b4aef
--- /dev/null
+++ b/code/modules/instruments/objs/structures/_musician.dm
@@ -0,0 +1,47 @@
+/obj/structure/musician
+ name = "Not A Piano"
+ desc = "Something broke!"
+ var/can_play_unanchored = FALSE
+ var/list/allowed_instrument_ids
+ var/datum/song/song
+
+/obj/structure/musician/Initialize(mapload)
+ . = ..()
+ song = new(src, allowed_instrument_ids)
+ allowed_instrument_ids = null
+
+/obj/structure/musician/Destroy()
+ QDEL_NULL(song)
+ return ..()
+
+/obj/structure/musician/attack_hand(mob/user)
+ add_fingerprint(user)
+ tgui_interact(user)
+
+/obj/structure/musician/tgui_data(mob/user)
+ return song.tgui_data(user)
+
+/obj/structure/musician/tgui_interact(mob/user)
+ song.tgui_interact(user)
+
+/obj/structure/musician/tgui_act(action, params)
+ if(..())
+ return
+ return song.tgui_act(action, params)
+
+/obj/structure/musician/wrench_act(mob/living/user, obj/item/I)
+ default_unfasten_wrench(user, I, 40)
+ return TRUE
+
+/**
+ * Whether the instrument should stop playing
+ *
+ * Arguments:
+ * * user - The user
+ */
+/obj/structure/musician/proc/should_stop_playing(mob/user)
+ if(!(anchored || can_play_unanchored))
+ return TRUE
+ if(!user)
+ return FALSE
+ return !CanUseTopic(user, GLOB.physical_state)
diff --git a/code/modules/instruments/objs/structures/piano.dm b/code/modules/instruments/objs/structures/piano.dm
new file mode 100644
index 00000000000..a68f07e53a3
--- /dev/null
+++ b/code/modules/instruments/objs/structures/piano.dm
@@ -0,0 +1,22 @@
+/obj/structure/piano
+ parent_type = /obj/structure/musician // TODO: Can't edit maps right now due to a freeze, remove and update path when it's done
+ name = "space minimoog"
+ icon = 'icons/obj/musician.dmi'
+ icon_state = "minimoog"
+ anchored = TRUE
+ density = TRUE
+ allowed_instrument_ids = "piano"
+
+/obj/structure/piano/unanchored
+ anchored = FALSE
+
+/obj/structure/piano/Initialize(mapload)
+ . = ..()
+ if(prob(50) && icon_state == initial(icon_state))
+ name = "space minimoog"
+ desc = "This is a minimoog, like a space piano, but more spacey!"
+ icon_state = "minimoog"
+ else
+ name = "space piano"
+ desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
+ icon_state = "piano"
diff --git a/code/modules/instruments/organ.dm b/code/modules/instruments/organ.dm
new file mode 100644
index 00000000000..424f63d7104
--- /dev/null
+++ b/code/modules/instruments/organ.dm
@@ -0,0 +1,43 @@
+/datum/instrument/organ
+ name = "Generic organ"
+ category = "Organ"
+ abstract_type = /datum/instrument/organ
+
+/datum/instrument/organ/crisis_church
+ name = "Crisis Church Organ"
+ id = "crichugan"
+ real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg')
+
+/datum/instrument/organ/crisis_hammond
+ name = "Crisis Hammond Organ"
+ id = "crihamgan"
+ real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg')
+
+/datum/instrument/organ/crisis_accordian
+ name = "Crisis Accordion"
+ id = "crack"
+ real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg')
+
+/datum/instrument/organ/crisis_harmonica
+ name = "Crisis Harmonica"
+ id = "crharmony"
+ real_samples = list("48"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg')
+
+/datum/instrument/organ/crisis_tango_accordian
+ name = "Crisis Tango Accordion"
+ id = "crtango"
+ real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg')
diff --git a/code/modules/instruments/piano.dm b/code/modules/instruments/piano.dm
new file mode 100644
index 00000000000..fdd2f6e9382
--- /dev/null
+++ b/code/modules/instruments/piano.dm
@@ -0,0 +1,56 @@
+/datum/instrument/piano
+ name = "Generic piano"
+ category = "Piano"
+ abstract_type = /datum/instrument/piano
+
+/datum/instrument/piano/fluid_piano
+ name = "FluidR3 Grand Piano"
+ id = "r3grand"
+ real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg',
+ "84"='sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg',
+ "96"='sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg',
+ "108"='sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg')
+
+/datum/instrument/piano/fluid_harpsichord
+ name = "FluidR3 Harpsichord"
+ id = "r3harpsi"
+ real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg',
+ "84"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg',
+ "96"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg',
+ "108"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg')
+
+/datum/instrument/piano/crisis_harpsichord
+ name = "Crisis Harpsichord"
+ id = "crharpsi"
+ real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg')
+
+/datum/instrument/piano/crisis_grandpiano_uni
+ name = "Crisis Grand Piano One"
+ id = "crgrand1"
+ real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg',
+ "84"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg',
+ "96"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg',
+ "108"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg')
+
+/datum/instrument/piano/crisis_brightpiano_uni
+ name = "Crisis Bright Piano One"
+ id = "crbright1"
+ real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg',
+ "48"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg',
+ "60"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg',
+ "72"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg',
+ "84"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg',
+ "96"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg',
+ "108"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg')
diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm
new file mode 100644
index 00000000000..142eab1fd2d
--- /dev/null
+++ b/code/modules/instruments/songs/_song.dm
@@ -0,0 +1,410 @@
+#define MUSICIAN_HEARCHECK_MINDELAY 4
+#define MUSIC_MAXLINES 1000
+#define MUSIC_MAXLINECHARS 300
+
+/**
+ * # Song datum
+ *
+ * These are the actual backend behind instruments.
+ * They attach to an atom and provide the editor + playback functionality.
+ */
+/datum/song
+ /// Name of the song
+ var/name = "Untitled"
+
+ /// The atom we're attached to/playing from
+ var/atom/parent
+
+ /// Our song lines
+ var/list/lines
+
+ /// delay between notes in deciseconds
+ var/tempo = 5
+
+ /// How far we can be heard
+ var/instrument_range = 15
+
+ /// Are we currently playing?
+ var/playing = FALSE
+
+ /// Are we currently editing?
+ var/editing = TRUE
+ /// Is the help screen open?
+ var/help = FALSE
+
+ /// Repeats left
+ var/repeat = 0
+ /// Maximum times we can repeat
+ var/max_repeats = 10
+
+ /// Our volume
+ var/volume = 75
+ /// Max volume
+ var/max_volume = 75
+ /// Min volume - This is so someone doesn't decide it's funny to set it to 0 and play invisible songs.
+ var/min_volume = 1
+
+ /// What instruments our built in picker can use. The picker won't show unless this is longer than one.
+ var/list/allowed_instrument_ids
+
+ //////////// Cached instrument variables /////////////
+ /// Instrument we are currently using
+ var/datum/instrument/using_instrument
+ /// Cached legacy ext for legacy instruments
+ var/cached_legacy_ext
+ /// Cached legacy dir for legacy instruments
+ var/cached_legacy_dir
+ /// Cached list of samples, referenced directly from the instrument for synthesized instruments
+ var/list/cached_samples
+ /// Are we operating in legacy mode (so if the instrument is a legacy instrument)
+ var/legacy = FALSE
+ //////////////////////////////////////////////////////
+
+ /////////////////// Playing variables ////////////////
+ /**
+ * Build by compile_chords()
+ * Must be rebuilt on instrument switch.
+ * Compilation happens when we start playing and is cleared after we finish playing.
+ * Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv)
+ */
+ var/list/compiled_chords
+ /// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag.
+ var/elapsed_delay
+ /// Amount of delay to wait before playing the next chord
+ var/delay_by
+ /// Current chord we're on.
+ var/current_chord
+ /// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1.
+ var/list/channels_playing
+ /// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments.
+ var/list/channels_idle
+ /// Person playing us
+ var/mob/user_playing
+ //////////////////////////////////////////////////////
+
+ /// Last world.time we checked for who can hear us
+ var/last_hearcheck = 0
+ /// The list of mobs that can hear us
+ var/list/hearing_mobs
+ /// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop)
+ var/debug_mode = FALSE
+ /// Max sound channels to occupy
+ var/max_sound_channels = CHANNELS_PER_INSTRUMENT
+ /// Current channels, so we can save a length() call.
+ var/using_sound_channels = 0
+ /// Last channel to play. text.
+ var/last_channel_played
+ /// Should we not decay our last played note?
+ var/full_sustain_held_note = TRUE
+
+ /////////////////////// DO NOT TOUCH THESE ///////////////////
+ var/octave_min = INSTRUMENT_MIN_OCTAVE
+ var/octave_max = INSTRUMENT_MAX_OCTAVE
+ var/key_min = INSTRUMENT_MIN_KEY
+ var/key_max = INSTRUMENT_MAX_KEY
+ var/static/list/note_offset_lookup
+ var/static/list/accent_lookup
+ //////////////////////////////////////////////////////////////
+
+ ///////////// !!FUN!! - Only works in synthesized mode! /////////////////
+ /// Note numbers to shift.
+ var/note_shift = 0
+ var/note_shift_min = -100
+ var/note_shift_max = 100
+ var/can_noteshift = TRUE
+ /// The kind of sustain we're using
+ var/sustain_mode = SUSTAIN_LINEAR
+ /// When a note is considered dead if it is below this in volume
+ var/sustain_dropoff_volume = INSTRUMENT_MIN_SUSTAIN_DROPOFF
+ /// Total duration of linear sustain for 100 volume note to get to SUSTAIN_DROPOFF
+ var/sustain_linear_duration = 5
+ /// Exponential sustain dropoff rate per decisecond
+ var/sustain_exponential_dropoff = 1.4
+ ////////// DO NOT DIRECTLY SET THESE!
+ /// Do not directly set, use update_sustain()
+ var/cached_linear_dropoff = 10
+ /// Do not directly set, use update_sustain()
+ var/cached_exponential_dropoff = 1.045
+ /////////////////////////////////////////////////////////////////////////
+
+ var/static/list/valid_files[0] // Cache to avoid running fexists() every time
+
+/datum/song/New(atom/parent, list/instrument_ids, new_range)
+ SSinstruments.on_song_new(src)
+ lines = list()
+ tempo = sanitize_tempo(tempo)
+ src.parent = parent
+ if(instrument_ids)
+ allowed_instrument_ids = islist(instrument_ids) ? instrument_ids : list(instrument_ids)
+ if(length(allowed_instrument_ids))
+ set_instrument(allowed_instrument_ids[1])
+ hearing_mobs = list()
+ volume = clamp(volume, min_volume, max_volume)
+ channels_playing = list()
+ channels_idle = list()
+ if(!note_offset_lookup)
+ note_offset_lookup = list(9, 11, 0, 2, 4, 5, 7)
+ accent_lookup = list("b" = -1, "s" = 1, "#" = 1, "n" = 0)
+ update_sustain()
+ if(new_range)
+ instrument_range = new_range
+
+/datum/song/Destroy()
+ stop_playing()
+ SSinstruments.on_song_del(src)
+ lines = null
+ using_instrument = null
+ allowed_instrument_ids = null
+ parent = null
+ return ..()
+
+/**
+ * Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range.
+ */
+/datum/song/proc/do_hearcheck()
+ last_hearcheck = world.time
+ var/list/old = hearing_mobs.Copy()
+ hearing_mobs.len = 0
+ var/turf/source = get_turf(parent)
+ for(var/mob/M in GLOB.player_list)
+ var/dist = get_dist(M, source)
+ if(dist > instrument_range) // Distance check
+ continue
+ if(!isInSight(M, source)) // Visibility check (direct line of sight)
+ continue
+ hearing_mobs[M] = dist
+ var/list/exited = old - hearing_mobs
+ for(var/i in exited)
+ terminate_sound_mob(i)
+
+/**
+ * Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum.
+ */
+/datum/song/proc/set_instrument(datum/instrument/I)
+ terminate_all_sounds()
+ var/old_legacy
+ if(using_instrument)
+ using_instrument.songs_using -= src
+ old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY)
+ using_instrument = null
+ cached_samples = null
+ cached_legacy_ext = null
+ cached_legacy_dir = null
+ legacy = null
+ if(istext(I) || ispath(I))
+ I = SSinstruments.instrument_data[I]
+ if(istype(I))
+ using_instrument = I
+ I.songs_using += src
+ var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY)
+ if(instrument_legacy)
+ cached_legacy_ext = I.legacy_instrument_ext
+ cached_legacy_dir = I.legacy_instrument_path
+ legacy = TRUE
+ else
+ cached_samples = I.samples
+ legacy = FALSE
+ if(isnull(old_legacy) || (old_legacy != instrument_legacy))
+ if(playing)
+ compile_chords()
+
+/**
+ * Attempts to start playing our song.
+ */
+/datum/song/proc/start_playing(mob/user)
+ if(playing)
+ return
+ if(!using_instrument?.is_ready())
+ to_chat(user, "An error has occured with [src]. Please reset the instrument.")
+ return
+ compile_chords()
+ if(!length(compiled_chords))
+ to_chat(user, "Song is empty.")
+ return
+ playing = TRUE
+ SStgui.update_uis(parent)
+ //we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
+ //wrap the rest of the stuff to ensure stop_playing() is called.
+ do_hearcheck()
+ SEND_SIGNAL(parent, COMSIG_SONG_START)
+ elapsed_delay = 0
+ delay_by = 0
+ current_chord = 1
+ user_playing = user
+ START_PROCESSING(SSinstruments, src)
+
+/**
+ * Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
+ */
+/datum/song/proc/stop_playing()
+ if(!playing)
+ return
+ playing = FALSE
+ if(!debug_mode)
+ compiled_chords = null
+ STOP_PROCESSING(SSinstruments, src)
+ SEND_SIGNAL(parent, COMSIG_SONG_END)
+ terminate_all_sounds(TRUE)
+ hearing_mobs.len = 0
+ SStgui.update_uis(parent)
+ user_playing = null
+
+/**
+ * Processes our song.
+ */
+/datum/song/proc/process_song(wait)
+ if(!length(compiled_chords) || current_chord > length(compiled_chords) || should_stop_playing(user_playing))
+ stop_playing()
+ return
+ var/list/chord = compiled_chords[current_chord]
+ if(++elapsed_delay >= delay_by)
+ play_chord(chord)
+ elapsed_delay = 0
+ delay_by = tempodiv_to_delay(chord[length(chord)])
+ current_chord++
+ if(current_chord > length(compiled_chords))
+ if(repeat)
+ repeat--
+ current_chord = 1
+ SStgui.update_uis(parent)
+ return
+ else
+ stop_playing()
+ return
+
+/**
+ * Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
+ */
+/datum/song/proc/tempodiv_to_delay(tempodiv)
+ tempodiv = tempodiv || world.tick_lag // Default to world.tick_lag in case someone's trying to be smart
+ return max(1, round((tempo / tempodiv) / world.tick_lag, 1))
+
+/**
+ * Compiles chords.
+ */
+/datum/song/proc/compile_chords()
+ legacy ? compile_legacy() : compile_synthesized()
+
+/**
+ * Plays a chord.
+ */
+/datum/song/proc/play_chord(list/chord)
+ // last value is timing information
+ for(var/i in 1 to (length(chord) - 1))
+ legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing)
+
+/**
+ * Checks if we should halt playback.
+ */
+/datum/song/proc/should_stop_playing(mob/user)
+ return QDELETED(parent) || !using_instrument || !playing
+
+/**
+ * Sanitizes tempo to a value that makes sense and fits the current world.tick_lag.
+ */
+/datum/song/proc/sanitize_tempo(new_tempo)
+ new_tempo = abs(new_tempo)
+ return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
+
+/**
+ * Gets our beats per minute based on our tempo.
+ */
+/datum/song/proc/get_bpm()
+ return 600 / tempo
+
+/**
+ * Sets our tempo from a beats-per-minute, sanitizing it to a valid number first.
+ */
+/datum/song/proc/set_bpm(bpm)
+ tempo = sanitize_tempo(600 / bpm)
+
+/datum/song/process(wait)
+ if(!playing)
+ return PROCESS_KILL
+ // it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up.
+ process_song(world.tick_lag)
+ process_decay(world.tick_lag)
+
+/**
+ * Updates our cached linear/exponential falloff stuff, saving calculations down the line.
+ */
+/datum/song/proc/update_sustain()
+ // Exponential is easy
+ cached_exponential_dropoff = sustain_exponential_dropoff
+ // Linear, not so much, since it's a target duration from 100 volume rather than an exponential rate.
+ var/target_duration = sustain_linear_duration
+ var/volume_diff = max(0, 100 - sustain_dropoff_volume)
+ var/volume_decrease_per_decisecond = volume_diff / target_duration
+ cached_linear_dropoff = volume_decrease_per_decisecond
+
+/**
+ * Setter for setting output volume.
+ */
+/datum/song/proc/set_volume(volume)
+ src.volume = clamp(volume, max(0, min_volume), min(100, max_volume))
+ update_sustain()
+ // We don't want to send the whole payload (song included) just for volume
+ var/datum/tgui/ui = SStgui.get_open_ui(usr, parent, "main")
+ if(ui)
+ ui.push_data(list("volume" = volume), force = TRUE)
+
+/**
+ * Setter for setting how low the volume has to get before a note is considered "dead" and dropped
+ */
+/datum/song/proc/set_dropoff_volume(volume, no_refresh = FALSE)
+ sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
+ update_sustain()
+ if(!no_refresh)
+ SStgui.update_uis(parent)
+
+/**
+ * Setter for setting exponential falloff factor.
+ */
+/datum/song/proc/set_exponential_drop_rate(drop, no_refresh = FALSE)
+ sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
+ update_sustain()
+ if(!no_refresh)
+ SStgui.update_uis(parent)
+
+/**
+ * Setter for setting linear falloff duration.
+ */
+/datum/song/proc/set_linear_falloff_duration(duration, no_refresh = FALSE)
+ sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
+ update_sustain()
+ if(!no_refresh)
+ SStgui.update_uis(parent)
+
+/datum/song/vv_edit_var(var_name, var_value)
+ . = ..()
+ if(.)
+ switch(var_name)
+ if(NAMEOF(src, volume))
+ set_volume(var_value)
+ if(NAMEOF(src, sustain_dropoff_volume))
+ set_dropoff_volume(var_value)
+ if(NAMEOF(src, sustain_exponential_dropoff))
+ set_exponential_drop_rate(var_value)
+ if(NAMEOF(src, sustain_linear_duration))
+ set_linear_falloff_duration(var_value)
+
+// subtype for handheld instruments, like violin
+/datum/song/handheld
+
+/datum/song/handheld/should_stop_playing(mob/user)
+ . = ..()
+ if(.)
+ return TRUE
+ var/obj/item/instrument/I = parent
+ return I.should_stop_playing(user)
+
+// subtype for stationary structures, like pianos
+/datum/song/stationary
+
+/datum/song/stationary/should_stop_playing(mob/user)
+ . = ..()
+ if(.)
+ return TRUE
+ var/obj/structure/musician/M = parent
+ return M.should_stop_playing(user)
+
diff --git a/code/modules/instruments/songs/_song_ui.dm b/code/modules/instruments/songs/_song_ui.dm
new file mode 100644
index 00000000000..cbd0f646f5c
--- /dev/null
+++ b/code/modules/instruments/songs/_song_ui.dm
@@ -0,0 +1,179 @@
+/datum/song/tgui_data(mob/user)
+ var/data[0]
+
+ // General
+ data["playing"] = playing
+ data["repeat"] = repeat
+ data["maxRepeats"] = max_repeats
+ data["editing"] = editing
+ data["lines"] = lines
+ data["tempo"] = tempo
+ data["minTempo"] = world.tick_lag
+ data["maxTempo"] = 5 SECONDS
+ data["tickLag"] = world.tick_lag
+ data["help"] = help
+
+ // Status
+ var/list/allowed_instrument_names = list()
+ for(var/i in allowed_instrument_ids)
+ var/datum/instrument/I = SSinstruments.get_instrument(i)
+ if(I)
+ allowed_instrument_names += I.name
+ data["allowedInstrumentNames"] = allowed_instrument_names
+ data["instrumentLoaded"] = !isnull(using_instrument)
+ if(using_instrument)
+ data["instrument"] = using_instrument.name
+ data["canNoteShift"] = can_noteshift
+ if(can_noteshift)
+ data["noteShift"] = note_shift
+ data["noteShiftMin"] = note_shift_min
+ data["noteShiftMax"] = note_shift_max
+ data["sustainMode"] = sustain_mode
+ switch(sustain_mode)
+ if(SUSTAIN_LINEAR)
+ data["sustainLinearDuration"] = sustain_linear_duration
+ if(SUSTAIN_EXPONENTIAL)
+ data["sustainExponentialDropoff"] = sustain_exponential_dropoff
+ data["ready"] = using_instrument?.is_ready()
+ data["legacy"] = legacy
+ data["volume"] = volume
+ data["minVolume"] = min_volume
+ data["maxVolume"] = max_volume
+ data["sustainDropoffVolume"] = sustain_dropoff_volume
+ data["sustainHeldNote"] = full_sustain_held_note
+
+ return data
+
+/datum/song/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, parent, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, parent, ui_key, "Instrument", parent?.name || "Instrument", 700, 500)
+ ui.open()
+ ui.set_autoupdate(FALSE) // NO!!! Don't auto-update this!!
+
+/datum/song/tgui_act(action, params)
+ . = TRUE
+ switch(action)
+ if("newsong")
+ lines = new()
+ tempo = sanitize_tempo(5) // default 120 BPM
+ name = ""
+ if("import")
+ var/t = ""
+ do
+ t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
+ if(!in_range(parent, usr))
+ return
+
+ if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
+ var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
+ if(cont == "no")
+ break
+ while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
+ parse_song(t)
+ return FALSE
+ if("help")
+ help = !help
+ if("edit")
+ editing = !editing
+ if("repeat") //Changing this from a toggle to a number of repeats to avoid infinite loops.
+ if(playing)
+ return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
+ repeat = clamp(round(text2num(params["new"])), 0, max_repeats)
+ if("tempo")
+ tempo = sanitize_tempo(text2num(params["new"]))
+ if("play")
+ INVOKE_ASYNC(src, .proc/start_playing, usr)
+ if("newline")
+ var/newline = html_encode(input("Enter your line: ", parent.name) as text|null)
+ if(!newline || !in_range(parent, usr))
+ return
+ if(length(lines) > MUSIC_MAXLINES)
+ return
+ if(length(newline) > MUSIC_MAXLINECHARS)
+ newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
+ lines.Add(newline)
+ if("deleteline")
+ var/num = round(text2num(params["line"]))
+ if(num > length(lines) || num < 1)
+ return
+ lines.Cut(num, num + 1)
+ if("modifyline")
+ var/num = round(text2num(params["line"]))
+ var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS)
+ if(!content || !in_range(parent, usr))
+ return
+ if(num > length(lines) || num < 1)
+ return
+ lines[num] = content
+ if("stop")
+ stop_playing()
+ if("setlinearfalloff")
+ set_linear_falloff_duration(round(text2num(params["new"]) * 10, world.tick_lag), TRUE)
+ if("setexpfalloff")
+ set_exponential_drop_rate(round(text2num(params["new"]), 0.00001), TRUE)
+ if("setvolume")
+ set_volume(round(text2num(params["new"]), 1))
+ if("setdropoffvolume")
+ set_dropoff_volume(round(text2num(params["new"]), 0.01), TRUE)
+ if("switchinstrument")
+ if(!length(allowed_instrument_ids))
+ return
+ else if(length(allowed_instrument_ids) == 1)
+ set_instrument(allowed_instrument_ids[1])
+ return
+ var/choice = params["name"]
+ for(var/i in allowed_instrument_ids)
+ var/datum/instrument/I = SSinstruments.get_instrument(i)
+ if(I && I.name == choice)
+ set_instrument(I)
+ if("setnoteshift")
+ note_shift = clamp(round(text2num(params["new"])), note_shift_min, note_shift_max)
+ if("setsustainmode")
+ var/static/list/sustain_modes
+ if(!length(sustain_modes))
+ sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL)
+ var/choice = params["new"]
+ sustain_mode = sustain_modes[choice] || sustain_mode
+ if("togglesustainhold")
+ full_sustain_held_note = !full_sustain_held_note
+ if("reset")
+ var/default_instrument = allowed_instrument_ids[1]
+ if(using_instrument != SSinstruments.instrument_data[default_instrument])
+ set_instrument(default_instrument)
+ note_shift = initial(note_shift)
+ sustain_mode = initial(sustain_mode)
+ set_linear_falloff_duration(initial(sustain_linear_duration), TRUE)
+ set_exponential_drop_rate(initial(sustain_exponential_dropoff), TRUE)
+ set_dropoff_volume(initial(sustain_dropoff_volume), TRUE)
+ else
+ return FALSE
+ parent.add_fingerprint(usr)
+
+/**
+ * Parses a song the user has input into lines and stores them.
+ */
+/datum/song/proc/parse_song(text)
+ set waitfor = FALSE
+ //split into lines
+ stop_playing()
+ lines = splittext(text, "\n")
+ if(length(lines))
+ var/bpm_string = "BPM: "
+ if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1))
+ var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default
+ tempo = sanitize_tempo(600 / round(divisor, 1))
+ lines.Cut(1, 2)
+ else
+ tempo = sanitize_tempo(5) // default 120 BPM
+ if(length(lines) > MUSIC_MAXLINES)
+ to_chat(usr, "Too many lines!")
+ lines.Cut(MUSIC_MAXLINES + 1)
+ var/linenum = 1
+ for(var/l in lines)
+ if(length_char(l) > MUSIC_MAXLINECHARS)
+ to_chat(usr, "Line [linenum] too long!")
+ lines.Remove(l)
+ else
+ linenum++
+ SStgui.update_uis(parent)
diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm
new file mode 100644
index 00000000000..61d7964caf4
--- /dev/null
+++ b/code/modules/instruments/songs/play_legacy.dm
@@ -0,0 +1,95 @@
+/**
+ * Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
+ */
+/datum/song/proc/compile_legacy()
+ if(!length(src.lines))
+ return
+ var/list/lines = src.lines //cache for hyepr speed!
+ compiled_chords = list()
+ var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
+ var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
+ for(var/line in lines)
+ var/list/chords = splittext(lowertext(line), ",")
+ for(var/chord in chords)
+ var/list/compiled_chord = list()
+ var/tempodiv = 1
+ var/list/notes_tempodiv = splittext(chord, "/")
+ var/len = length(notes_tempodiv)
+ if(len >= 2)
+ tempodiv = text2num(notes_tempodiv[2])
+ if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
+ var/list/notes = splittext(notes_tempodiv[1], "-")
+ for(var/note in notes)
+ if(length(note) == 0)
+ continue
+ // 1-7, A-G
+ var/key = text2ascii(note) - 96
+ if((key < 1) || (key > 7))
+ continue
+ for(var/i in 2 to length(note))
+ var/oct_acc = copytext(note, i, i + 1)
+ var/num = text2num(oct_acc)
+ if(!num) //it's an accidental
+ accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
+ else //octave
+ octaves[key] = clamp(num, octave_min, octave_max)
+ compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key])
+ compiled_chord += tempodiv //this goes last
+ if(length(compiled_chord))
+ compiled_chords[++compiled_chords.len] = compiled_chord
+
+/**
+ * Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management.
+ *
+ * Arguments:
+ * * note - number from 1-7 for A-G
+ * * acc - either "b", "n", or "#"
+ * * oct - 1-8 (or 9 for C)
+ */
+/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user)
+ // handle accidental -> B<>C of E<>F
+ if(acc == "b" && (note == 3 || note == 6)) // C or F
+ if(note == 3)
+ oct--
+ note--
+ acc = "n"
+ else if(acc == "#" && (note == 2 || note == 5)) // B or E
+ if(note == 2)
+ oct++
+ note++
+ acc = "n"
+ else if(acc == "#" && (note == 7)) //G#
+ note = 1
+ acc = "b"
+ else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
+ acc = "b"
+ note++
+
+ // check octave, C is allowed to go to 9
+ if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
+ return
+
+ // now generate name
+ var/filename = "sound/instruments/[cached_legacy_dir]/[ascii2text(note + 64)][acc][oct].[cached_legacy_ext]"
+ var/soundfile = file(filename)
+ // make sure the note exists
+ var/cached_fexists = valid_files[filename]
+ if(!isnull(cached_fexists))
+ if(!cached_fexists)
+ return
+ else if(!fexists(soundfile))
+ valid_files[filename] = FALSE
+ return
+ else
+ valid_files[filename] = TRUE
+ // and play
+ var/turf/source = get_turf(parent)
+ if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
+ do_hearcheck()
+ var/sound/music_played = sound(soundfile)
+ for(var/i in hearing_mobs)
+ var/mob/M = i
+ if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS))
+ continue
+ M.playsound_local(source, null, volume * using_instrument.volume_multiplier, falloff = 5, S = music_played)
+ // Could do environment and echo later but not for now
diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm
new file mode 100644
index 00000000000..b2f16f6ab91
--- /dev/null
+++ b/code/modules/instruments/songs/play_synthesized.dm
@@ -0,0 +1,134 @@
+/**
+ * Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
+ */
+/datum/song/proc/compile_synthesized()
+ if(!length(src.lines))
+ return
+ var/list/lines = src.lines //cache for hyepr speed!
+ compiled_chords = list()
+ var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
+ var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
+ for(var/line in lines)
+ var/list/chords = splittext(lowertext(line), ",")
+ for(var/chord in chords)
+ var/list/compiled_chord = list()
+ var/tempodiv = 1
+ var/list/notes_tempodiv = splittext(chord, "/")
+ var/len = length(notes_tempodiv)
+ if(len >= 2)
+ tempodiv = text2num(notes_tempodiv[2])
+ if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
+ var/list/notes = splittext(notes_tempodiv[1], "-")
+ for(var/note in notes)
+ if(length(note) == 0)
+ continue
+ // 1-7, A-G
+ var/key = text2ascii(note) - 96
+ if((key < 1) || (key > 7))
+ continue
+ for(var/i in 2 to length(note))
+ var/oct_acc = copytext(note, i, i + 1)
+ var/num = text2num(oct_acc)
+ if(!num) //it's an accidental
+ accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
+ else //octave
+ octaves[key] = clamp(num, octave_min, octave_max)
+ compiled_chord += clamp((note_offset_lookup[key] + octaves[key] * 12 + accent_lookup[accents[key]]), key_min, key_max)
+ compiled_chord += tempodiv //this goes last
+ if(length(compiled_chord))
+ compiled_chords[++compiled_chords.len] = compiled_chord
+
+/**
+ * Plays a specific numerical key from our instrument to anyone who can hear us.
+ * Does a hearing check if enough time has passed.
+ */
+/datum/song/proc/playkey_synth(key, mob/user)
+ if(can_noteshift)
+ key = clamp(key + note_shift, key_min, key_max)
+ if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
+ do_hearcheck()
+ var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc!
+ //Should probably add channel limiters here at some point but I don't care right now.
+ var/channel = pop_channel()
+ if(isnull(channel))
+ return FALSE
+ . = TRUE
+ var/sound/copy = sound(K.sample)
+ var/volume = src.volume * using_instrument.volume_multiplier
+ copy.frequency = K.frequency
+ copy.volume = volume
+ var/channel_text = num2text(channel)
+ channels_playing[channel_text] = 100
+ last_channel_played = channel_text
+ for(var/i in hearing_mobs)
+ var/mob/M = i
+ if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS))
+ continue
+ M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF)
+ // Could do environment and echo later but not for now
+
+/**
+ * Stops all sounds we are "responsible" for. Only works in synthesized mode.
+ */
+/datum/song/proc/terminate_all_sounds(clear_channels = TRUE)
+ for(var/i in hearing_mobs)
+ terminate_sound_mob(i)
+ if(clear_channels && channels_playing)
+ channels_playing.len = 0
+ channels_idle.len = 0
+ SSinstruments.current_instrument_channels -= using_sound_channels
+ using_sound_channels = 0
+ SSsounds.free_datum_channels(src)
+
+/**
+ * Stops all sounds we are responsible for in a given person. Only works in synthesized mode.
+ */
+/datum/song/proc/terminate_sound_mob(mob/M)
+ for(var/channel in channels_playing)
+ M.stop_sound_channel(text2num(channel))
+
+/**
+ * Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster.
+ */
+/datum/song/proc/pop_channel()
+ if(length(channels_idle)) //just pop one off of here if we have one available
+ . = text2num(channels_idle[1])
+ channels_idle.Cut(1, 2)
+ return
+ if(using_sound_channels >= max_sound_channels)
+ return
+ . = SSinstruments.reserve_instrument_channel(src)
+ if(!isnull(.))
+ using_sound_channels++
+
+/**
+ * Decays our channels and updates their volumes to mobs who can hear us.
+ *
+ * Arguments:
+ * * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation.
+ */
+/datum/song/proc/process_decay(wait_ds)
+ var/linear_dropoff = cached_linear_dropoff * wait_ds
+ var/exponential_dropoff = cached_exponential_dropoff ** wait_ds
+ for(var/channel in channels_playing)
+ if(full_sustain_held_note && (channel == last_channel_played))
+ continue
+ var/current_volume = channels_playing[channel]
+ switch(sustain_mode)
+ if(SUSTAIN_LINEAR)
+ current_volume -= linear_dropoff
+ if(SUSTAIN_EXPONENTIAL)
+ current_volume /= exponential_dropoff
+ channels_playing[channel] = current_volume
+ var/dead = current_volume <= sustain_dropoff_volume
+ var/channelnumber = text2num(channel)
+ if(dead)
+ channels_playing -= channel
+ channels_idle += channel
+ for(var/i in hearing_mobs)
+ var/mob/M = i
+ M.stop_sound_channel(channelnumber)
+ else
+ for(var/i in hearing_mobs)
+ var/mob/M = i
+ M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier)
diff --git a/code/modules/instruments/synth_tones.dm b/code/modules/instruments/synth_tones.dm
new file mode 100644
index 00000000000..9ad9250f40d
--- /dev/null
+++ b/code/modules/instruments/synth_tones.dm
@@ -0,0 +1,19 @@
+/datum/instrument/tones
+ name = "Ideal tone"
+ category = "Tones"
+ abstract_type = /datum/instrument/tones
+
+/datum/instrument/tones/square_wave
+ name = "Ideal square wave"
+ id = "square"
+ real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg')
+
+/datum/instrument/tones/sine_wave
+ name = "Ideal sine wave"
+ id = "sine"
+ real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg')
+
+/datum/instrument/tones/saw_wave
+ name = "Ideal sawtooth wave"
+ id = "saw"
+ real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg')
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index fd675bac32c..058b64d3c18 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -1,104 +1,21 @@
-/**********************Mining Equipment Locker**************************/
+// Use this define to register something as a purchasable!
+// * n — The proper name of the purchasable
+// * o — The object type path of the purchasable to spawn
+// * p — The price of the purchasable in mining points
+#define EQUIPMENT(n, o, p) n = new /datum/data/mining_equipment(n, o, p)
+
+/**********************Mining Equipment Vendor**************************/
/obj/machinery/mineral/equipment_vendor
name = "mining equipment vendor"
desc = "An equipment vendor for miners, points collected at an ore redemption machine can be spent here."
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "mining"
- density = 1
- anchored = 1.0
+ density = TRUE
+ anchored = TRUE
var/obj/item/card/id/inserted_id
- var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces.
- new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 10),
- new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100),
- new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 300),
- new /datum/data/mining_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100),
- new /datum/data/mining_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100),
- new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150),
- new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200),
- new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300),
- new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
- new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
- new /datum/data/mining_equipment("Space Cash", /obj/item/stack/spacecash/c1000, 2000),
- new /datum/data/mining_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500),
- new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400),
- new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400),
- new /datum/data/mining_equipment("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
- new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
- new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
- new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
- new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
- new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750),
- new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
- new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
- new /datum/data/mining_equipment("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
- new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 800),
- new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000),
- new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000),
- new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
- new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript/full, 1500),
- new /datum/data/mining_equipment("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
- new /datum/data/mining_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
- new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
- new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500),
- new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
- new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
- new /datum/data/mining_equipment("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800),
- new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
- new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
- new /datum/data/mining_equipment("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600),
- new /datum/data/mining_equipment("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
- new /datum/data/mining_equipment("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100),
- new /datum/data/mining_equipment("Lazarus Capsule", /obj/item/mobcapsule, 800),
- new /datum/data/mining_equipment("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
- new /datum/data/mining_equipment("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100),
- new /datum/data/mining_equipment("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150),
- new /datum/data/mining_equipment("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250),
- new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300),
- new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
- new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
- new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
- new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000)
- )
-
-/obj/machinery/mineral/equipment_vendor/golem
- name = "golem ship equipment vendor"
-
-/obj/machinery/mineral/equipment_vendor/golem/New()
- ..()
- component_parts = list()
- component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null)
- component_parts += new /obj/item/stock_parts/matter_bin(null)
- component_parts += new /obj/item/stock_parts/matter_bin(null)
- component_parts += new /obj/item/stock_parts/matter_bin(null)
- component_parts += new /obj/item/stack/sheet/glass(null)
- RefreshParts()
-
-/obj/machinery/mineral/equipment_vendor/golem/Initialize()
- . = ..()
- desc += "\nIt seems a few selections have been added."
- prize_list += list(
- new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/golem, 250),
- new /datum/data/mining_equipment("Science Backpack", /obj/item/storage/backpack/science, 250),
- new /datum/data/mining_equipment("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250),
- new /datum/data/mining_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250),
- new /datum/data/mining_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500),
- new /datum/data/mining_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000),
- new /datum/data/mining_equipment("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000),
- new /datum/data/mining_equipment("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000),
- new /datum/data/mining_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000)
-
- )
-
-/datum/data/mining_equipment
- var/equipment_name = "generic"
- var/equipment_path = null
- var/cost = 0
-
-/datum/data/mining_equipment/New(name, path, equipment_cost)
- equipment_name = name
- equipment_path = path
- cost = equipment_cost
+ var/list/prize_list // Initialized just below! (if you're wondering why - check CONTRIBUTING.md, look for: "hidden" init proc)
+ var/dirty_items = FALSE // Used to refresh the static/redundant data in case the machine gets VV'd
/obj/machinery/mineral/equipment_vendor/New()
..()
@@ -110,6 +27,72 @@
component_parts += new /obj/item/stack/sheet/glass(null)
RefreshParts()
+/obj/machinery/mineral/equipment_vendor/Initialize(mapload)
+ . = ..()
+ prize_list = list()
+ prize_list["Gear"] = list(
+ EQUIPMENT("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
+ EQUIPMENT("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
+ EQUIPMENT("Fulton Beacon", /obj/item/fulton_core, 400),
+ EQUIPMENT("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript, 1500),
+ EQUIPMENT("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
+ EQUIPMENT("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
+ EQUIPMENT("Lazarus Capsule", /obj/item/mobcapsule, 800),
+ EQUIPMENT("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
+ EQUIPMENT("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
+ EQUIPMENT("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
+ )
+ prize_list["Consumables"] = list(
+ EQUIPMENT("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100),
+ EQUIPMENT("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
+ EQUIPMENT("Fulton Pack", /obj/item/extraction_pack, 1000),
+ EQUIPMENT("Jaunter", /obj/item/wormhole_jaunter, 750),
+ EQUIPMENT("Lazarus Injector", /obj/item/lazarus_injector, 1000),
+ EQUIPMENT("Point Transfer Card", /obj/item/card/mining_point_card, 500),
+ EQUIPMENT("Shelter Capsule", /obj/item/survivalcapsule, 400),
+ EQUIPMENT("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
+ EQUIPMENT("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
+ )
+ prize_list["Kinetic Accelerator"] = list(
+ EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
+ EQUIPMENT("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150),
+ EQUIPMENT("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000),
+ EQUIPMENT("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
+ EQUIPMENT("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
+ EQUIPMENT("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300),
+ EQUIPMENT("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100),
+ EQUIPMENT("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
+ EQUIPMENT("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250),
+ EQUIPMENT("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100),
+ )
+ prize_list["Digging Tools"] = list(
+ EQUIPMENT("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
+ EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
+ EQUIPMENT("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
+ EQUIPMENT("Resonator", /obj/item/resonator, 800),
+ EQUIPMENT("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
+ EQUIPMENT("Super Resonator", /obj/item/resonator/upgraded, 2500),
+ )
+ prize_list["Minebot"] = list(
+ EQUIPMENT("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800),
+ EQUIPMENT("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
+ EQUIPMENT("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
+ EQUIPMENT("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600),
+ EQUIPMENT("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
+ )
+ prize_list["Miscellaneous"] = list(
+ EQUIPMENT("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100),
+ EQUIPMENT("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
+ EQUIPMENT("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150),
+ EQUIPMENT("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
+ EQUIPMENT("Laser Pointer", /obj/item/laser_pointer, 300),
+ EQUIPMENT("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
+ EQUIPMENT("Soap", /obj/item/soap/nanotrasen, 200),
+ EQUIPMENT("Space Cash", /obj/item/stack/spacecash/c1000, 2000),
+ EQUIPMENT("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100),
+ )
+ prize_list["Extra"] = list() // Used in child vendors
+
/obj/machinery/mineral/equipment_vendor/power_change()
..()
update_icon()
@@ -126,90 +109,126 @@
/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user)
if(..())
return
- interact(user)
+ tgui_interact(user)
/obj/machinery/mineral/equipment_vendor/attack_ghost(mob/user)
- interact(user)
+ tgui_interact(user)
-/obj/machinery/mineral/equipment_vendor/interact(mob/user)
- user.set_machine(src)
+/obj/machinery/mineral/equipment_vendor/tgui_data(mob/user)
+ var/list/data[0]
- var/dat
- dat +=""
- if(istype(inserted_id))
- dat += "You have [inserted_id.mining_points] mining points collected. Eject ID."
+ // ID
+ if(inserted_id)
+ data["has_id"] = TRUE
+ data["id"] = list(
+ "name" = inserted_id.registered_name,
+ "points" = inserted_id.mining_points,
+ )
else
- dat += "No ID inserted. Insert ID."
- dat += " "
- dat += " Equipment point cost list:
"
- for(var/datum/data/mining_equipment/prize in prize_list)
- dat += "| [prize.equipment_name] | [prize.cost] | Purchase | "
- dat += " "
- var/datum/browser/popup = new(user, "miningvendor", "Mining Equipment Vendor", 400, 350)
- popup.set_content(dat)
- popup.open()
+ data["has_id"] = FALSE
-/obj/machinery/mineral/equipment_vendor/Topic(href, href_list)
+ return data
+
+/obj/machinery/mineral/equipment_vendor/tgui_static_data(mob/user)
+ var/list/static_data[0]
+
+ // Available items - in static data because we don't wanna compute this list every time! It hardly changes.
+ static_data["items"] = list()
+ for(var/cat in prize_list)
+ var/list/cat_items = list()
+ for(var/prize_name in prize_list[cat])
+ var/datum/data/mining_equipment/prize = prize_list[cat][prize_name]
+ cat_items[prize_name] = list("name" = prize_name, "price" = prize.cost)
+ static_data["items"][cat] = cat_items
+
+ return static_data
+
+/obj/machinery/mineral/equipment_vendor/vv_edit_var(var_name, var_value)
+ // Gotta update the static data in case an admin VV's the items for some reason..!
+ if(var_name == "prize_list")
+ dirty_items = TRUE
+ return ..()
+
+/obj/machinery/mineral/equipment_vendor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ // Update static data if need be
+ if(dirty_items)
+ if(!ui)
+ ui = SStgui.get_open_ui(user, src, ui_key)
+ if(ui) // OK so ui?. somehow breaks the implied src so this is needed
+ ui.initial_static_data = tgui_static_data(user)
+ dirty_items = FALSE
+
+ // Open the window
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "MiningVendor", name, 400, 450)
+ ui.open()
+ ui.set_autoupdate(FALSE)
+
+/obj/machinery/mineral/equipment_vendor/tgui_act(action, params)
if(..())
- return 1
+ return
- if(href_list["choice"])
- if(istype(inserted_id))
- if(href_list["choice"] == "eject")
- inserted_id.loc = loc
- inserted_id.verb_pickup()
- inserted_id = null
- else if(href_list["choice"] == "insert")
- var/obj/item/card/id/I = usr.get_active_hand()
- if(istype(I))
- if(!usr.drop_item())
- return
- I.loc = src
- inserted_id = I
- else
- to_chat(usr, "No valid ID.")
-
- if(href_list["purchase"])
- if(istype(inserted_id))
- var/datum/data/mining_equipment/prize = locate(href_list["purchase"])
- if(!prize || !(prize in prize_list) || prize.cost > inserted_id.mining_points)
+ . = TRUE
+ switch(action)
+ if("logoff")
+ if(!inserted_id)
+ return
+ usr.put_in_hands(inserted_id)
+ inserted_id = null
+ if("purchase")
+ if(!inserted_id)
+ return
+ var/category = params["cat"] // meow
+ var/name = params["name"]
+ if(!(category in prize_list) || !(name in prize_list[category])) // Not trying something that's not in the list, are you?
+ return
+ var/datum/data/mining_equipment/prize = prize_list[category][name]
+ if(prize.cost > inserted_id.mining_points) // shouldn't be able to access this since the button is greyed out, but..
+ to_chat(usr, "You have insufficient points.")
return
inserted_id.mining_points -= prize.cost
- new prize.equipment_path(src.loc)
- updateUsrDialog()
+ new prize.equipment_path(loc)
+ else
+ return FALSE
+ add_fingerprint()
/obj/machinery/mineral/equipment_vendor/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, "mining-open", "mining", I))
- updateUsrDialog()
return
if(panel_open)
if(istype(I, /obj/item/crowbar))
if(inserted_id)
inserted_id.forceMove(loc) //Prevents deconstructing the ORM from deleting whatever ID was inside it.
default_deconstruction_crowbar(user, I)
- return 1
+ return TRUE
if(istype(I, /obj/item/mining_voucher))
if(!powered())
return
- else
- RedeemVoucher(I, user)
+ redeem_voucher(I, user)
return
- if(istype(I,/obj/item/card/id))
+ if(istype(I, /obj/item/card/id))
if(!powered())
return
- else
- var/obj/item/card/id/C = usr.get_active_hand()
- if(istype(C) && !istype(inserted_id))
- if(!usr.drop_item())
- return
- C.forceMove(src)
- inserted_id = C
- interact(user)
+ var/obj/item/card/id/C = user.get_active_hand()
+ if(istype(C) && !istype(inserted_id))
+ if(!user.drop_item())
+ return
+ C.forceMove(src)
+ inserted_id = C
+ tgui_interact(user)
return
return ..()
-/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/mining_voucher/voucher, mob/redeemer)
+/**
+ * Called when someone slaps the machine with a mining voucher
+ *
+ * Arguments:
+ * * voucher - The voucher card item
+ * * redeemer - The person holding it
+ */
+/obj/machinery/mineral/equipment_vendor/proc/redeem_voucher(obj/item/mining_voucher/voucher, mob/redeemer)
var/items = list("Survival Capsule and Explorer's Webbing", "Resonator Kit", "Minebot Kit", "Extraction and Rescue Kit", "Crusher Kit", "Mining Conscription Kit")
var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in items
@@ -240,11 +259,51 @@
qdel(voucher)
/obj/machinery/mineral/equipment_vendor/ex_act(severity, target)
- do_sparks(5, 1, src)
+ do_sparks(5, TRUE, src)
if(prob(50 / severity) && severity < 3)
qdel(src)
-/**********************Mining Equipment Locker Items**************************/
+/**********************Mining Equiment Vendor (Golem)**************************/
+
+/obj/machinery/mineral/equipment_vendor/golem
+ name = "golem ship equipment vendor"
+
+/obj/machinery/mineral/equipment_vendor/golem/New()
+ ..()
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null)
+ component_parts += new /obj/item/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ RefreshParts()
+
+/obj/machinery/mineral/equipment_vendor/golem/Initialize()
+ . = ..()
+ desc += "\nIt seems a few selections have been added."
+ prize_list["Extra"] += list(
+ EQUIPMENT("Extra ID", /obj/item/card/id/golem, 250),
+ EQUIPMENT("Science Backpack", /obj/item/storage/backpack/science, 250),
+ EQUIPMENT("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250),
+ EQUIPMENT("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250),
+ EQUIPMENT("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500),
+ EQUIPMENT("Grey Slime Extract", /obj/item/slime_extract/grey, 1000),
+ EQUIPMENT("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000),
+ EQUIPMENT("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000),
+ EQUIPMENT("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000),
+ )
+
+/**********************Mining Equipment Datum**************************/
+
+/datum/data/mining_equipment
+ var/equipment_name = "generic"
+ var/equipment_path = null
+ var/cost = 0
+
+/datum/data/mining_equipment/New(name, path, equipment_cost)
+ equipment_name = name
+ equipment_path = path
+ cost = equipment_cost
/**********************Mining Equipment Voucher**********************/
@@ -300,3 +359,5 @@
/obj/item/storage/backpack/duffel/mining_conscript/full/New()
..()
new /obj/item/card/id/mining_access_card(src)
+
+#undef EQUIPMENT
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index c01d04beba8..54c23c89b5d 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -220,6 +220,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
var/datum/wires/explosive/gibtonite/wires
/obj/item/twohanded/required/gibtonite/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
return ..()
diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm
index 2f5627e8413..4d003753876 100644
--- a/code/modules/mob/dead/observer/say.dm
+++ b/code/modules/mob/dead/observer/say.dm
@@ -1,20 +1,10 @@
-/mob/dead/observer/say(var/message)
+/mob/dead/observer/say(message)
message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN))
if(!message)
return
- log_ghostsay(message, src)
-
- if(src.client)
- if(src.client.prefs.muted & MUTE_DEADCHAT)
- to_chat(src, "You cannot talk in deadchat (muted).")
- return
-
- if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
- return
-
- . = src.say_dead(message)
+ return say_dead(message)
/mob/dead/observer/emote(act, type, message, force)
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 410c485f15e..2514ce68b06 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -171,7 +171,14 @@
to_chat(src, heard)
-/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
+/mob/proc/hear_holopad_talk(list/message_pieces, verb = "says", mob/speaker = null)
+ if(sleeping || stat == UNCONSCIOUS)
+ hear_sleep(multilingual_to_message(message_pieces))
+ return
+
+ if(!can_hear())
+ return
+
var/message = combine_message(message_pieces, verb, speaker)
var/name = speaker.name
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index a46268daf50..50d9b6d5fd5 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -186,7 +186,7 @@
if(prob(80))
new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]"
else
- new_name += ..(gender,1)
+ new_name += " [..(gender,1)]"
return new_name
/datum/language/vulpkanin
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 60891563ad2..3739e337542 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -301,14 +301,13 @@
else if(nutrition >= NUTRITION_LEVEL_FAT)
msg += "[p_they(TRUE)] [p_are()] quite chubby.\n"
- if(!ismachineperson(src) && blood_volume < BLOOD_VOLUME_SAFE)
+ if(blood_volume < BLOOD_VOLUME_SAFE)
msg += "[p_they(TRUE)] [p_have()] pale skin.\n"
if(bleedsuppress)
msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n"
else if(bleed_rate)
- var/bleed_message = !ismachineperson(src) ? "bleeding" : "leaking"
- msg += "[p_they(TRUE)] [p_are()] [bleed_message]!\n"
+ msg += "[p_they(TRUE)] [p_are()] bleeding!\n"
if(reagents.has_reagent("teslium"))
msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n"
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 6141ad02f9f..c7798a51a08 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -572,41 +572,34 @@ emp_act
if(M.a_intent == INTENT_HARM)
if(w_uniform)
w_uniform.add_fingerprint(M)
- var/damage = rand(15, 30)
+ var/damage = prob(90) ? 20 : 0
if(!damage)
- playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
+ playsound(loc, 'sound/weapons/slashmiss.ogg', 50, TRUE, -1)
visible_message("[M] has lunged at [src]!")
return 0
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected))
- var/armor_block = run_armor_check(affecting, "melee")
+ var/armor_block = run_armor_check(affecting, "melee", armour_penetration = 10)
- playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
+ playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
visible_message("[M] has slashed at [src]!", \
"[M] has slashed at [src]!")
apply_damage(damage, BRUTE, affecting, armor_block)
- if(damage >= 25)
- visible_message("[M] has wounded [src]!", \
- "[M] has wounded [src]!")
- apply_effect(4, WEAKEN, armor_block)
- add_attack_logs(M, src, "Alien attacked")
+ add_attack_logs(M, src, "Alien attacked")
updatehealth("alien attack")
- if(M.a_intent == INTENT_DISARM)
- if(prob(80))
+ if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead.
+ var/obj/item/I = get_active_hand()
+ if(I && unEquip(I))
+ playsound(loc, 'sound/weapons/slash.ogg', 25, TRUE, -1)
+ visible_message("[M] disarms [src]!", "[M] disarms you!", "You hear aggressive shuffling!")
+ to_chat(M, "You disarm [src]!")
+ else
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected))
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
apply_effect(5, WEAKEN, run_armor_check(affecting, "melee"))
add_attack_logs(M, src, "Alien tackled")
visible_message("[M] has tackled down [src]!")
- else
- if(prob(99)) //this looks fucking stupid but it was previously 'var/randn = rand(1, 100); if(randn <= 99)'
- playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
- drop_item()
- visible_message("[M] disarmed [src]!")
- else
- playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
- visible_message("[M] has tried to disarm [src]!")
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species/diona.dm b/code/modules/mob/living/carbon/human/species/diona.dm
index a8639a1a948..2ce011c7159 100644
--- a/code/modules/mob/living/carbon/human/species/diona.dm
+++ b/code/modules/mob/living/carbon/human/species/diona.dm
@@ -74,6 +74,10 @@
..()
H.gender = NEUTER
+/datum/species/diona/on_species_loss(mob/living/carbon/human/H)
+ . = ..()
+ H.clear_alert("nolight")
+
/datum/species/diona/handle_reagents(mob/living/carbon/human/H, datum/reagent/R)
if(R.id == "glyphosate" || R.id == "atrazine")
H.adjustToxLoss(3) //Deal aditional damage
diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm
index ec37d3c6c9f..a1cab2ea466 100644
--- a/code/modules/mob/living/carbon/human/species/machine.dm
+++ b/code/modules/mob/living/carbon/human/species/machine.dm
@@ -22,7 +22,7 @@
death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional."
death_sounds = list('sound/voice/borg_deathsound.ogg') //I've made this a list in the event we add more sounds for dead robots.
- species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie!
+ species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_BLOOD, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie!
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS
dietflags = 0 //IPCs can't eat, so no diet
@@ -30,10 +30,6 @@
blood_color = "#1F181F"
flesh_color = "#AAAAAA"
- blood_color = "#3C3C3C"
- exotic_blood = "oil"
- blood_damage_type = STAMINA
-
//Default styles for created mobs.
default_hair = "Blue IPC Screen"
dies_at_threshold = TRUE
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index d4546f4f6bc..e944e692887 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -351,8 +351,6 @@ proc/get_radio_key_from_channel(var/channel)
return
if(stat)
- if(stat == DEAD)
- return say_dead(message_pieces)
return
if(is_muzzled())
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 7f6ed094dce..a5a9269d63b 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
var/list/connected_robots = list()
var/aiRestorePowerRoutine = 0
//var/list/laws = list()
- var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list())
+ alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera", "Burglar")
var/viewalerts = 0
var/icon/holo_icon//Default is assigned when AI is created.
var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
@@ -173,19 +173,19 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
add_language("Galactic Common", 1)
add_language("Sol Common", 1)
add_language("Tradeband", 1)
- add_language("Neo-Russkiya", 0)
- add_language("Gutter", 0)
- add_language("Sinta'unathi", 0)
- add_language("Siik'tajr", 0)
- add_language("Canilunzt", 0)
- add_language("Skrellian", 0)
- add_language("Vox-pidgin", 0)
- add_language("Orluum", 0)
- add_language("Rootspeak", 0)
+ add_language("Neo-Russkiya", 1)
+ add_language("Gutter", 1)
+ add_language("Sinta'unathi", 1)
+ add_language("Siik'tajr", 1)
+ add_language("Canilunzt", 1)
+ add_language("Skrellian", 1)
+ add_language("Vox-pidgin", 1)
+ add_language("Orluum", 1)
+ add_language("Rootspeak", 1)
add_language("Trinary", 1)
- add_language("Chittin", 0)
- add_language("Bubblish", 0)
- add_language("Clownish", 0)
+ add_language("Chittin", 1)
+ add_language("Bubblish", 1)
+ add_language("Clownish", 1)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if(!B)//If there is no player/brain inside.
@@ -242,6 +242,46 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
return
show_borg_info()
+/mob/living/silicon/ai/proc/ai_alerts()
+ var/list/dat = list("Current Station Alerts\n")
+ dat += "Close
"
+ var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
+ for(var/cat in temp_alarm_list)
+ if(!(cat in alarms_listend_for))
+ continue
+ dat += text("[] \n", cat)
+ var/list/list/L = temp_alarm_list[cat].Copy()
+ for(var/alarm in L)
+ var/list/list/alm = L[alarm].Copy()
+ var/area_name = alm[1]
+ var/C = alm[2]
+ var/list/list/sources = alm[3].Copy()
+ for(var/thing in sources)
+ var/atom/A = locateUID(thing)
+ if(A && A.z != z)
+ L -= alarm
+ continue
+ dat += ""
+ if(C && islist(C))
+ var/dat2 = ""
+ for(var/cam in C)
+ var/obj/machinery/camera/I = locateUID(cam)
+ if(!QDELETED(I))
+ dat2 += text("[][]", (dat2 == "") ? "" : " | ", I.c_tag)
+ dat += text("-- [] ([])", area_name, (dat2 != "") ? dat2 : "No Camera")
+ else
+ dat += text("-- [] (No Camera)", area_name)
+ if(sources.len > 1)
+ dat += text("- [] sources", sources.len)
+ dat += " \n"
+ if(!L.len)
+ dat += "-- All Systems Nominal \n"
+ dat += " \n"
+
+ viewalerts = TRUE
+ var/dat_text = dat.Join("")
+ src << browse(dat_text, "window=aialerts&can_close=0")
+
/mob/living/silicon/ai/proc/show_borg_info()
stat(null, text("Connected cyborgs: [connected_robots.len]"))
for(var/thing in connected_robots)
@@ -612,7 +652,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
if(href_list["switchcamera"])
switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras
if(href_list["showalerts"])
- subsystem_alarm_monitor()
+ ai_alerts()
if(href_list["show_paper"])
if(last_paper_seen)
src << browse(last_paper_seen, "window=show_paper")
@@ -787,12 +827,49 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
Bot.call_bot(src, waypoint)
+/mob/living/silicon/ai/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
+ if(!(class in alarms_listend_for))
+ return
+ if(alarmsource.z != z)
+ return
+ if(stat == DEAD)
+ return TRUE
+ if(O)
+ var/obj/machinery/camera/C = locateUID(O[1])
+ if(O.len == 1 && !QDELETED(C) && C.can_use())
+ queueAlarm("--- [class] alarm detected in [A.name]! ([C.c_tag])", class)
+ else if(O && O.len)
+ var/foo = 0
+ var/dat2 = ""
+ for(var/thing in O)
+ var/obj/machinery/camera/I = locateUID(thing)
+ if(!QDELETED(I))
+ dat2 += text("[][]", (!foo) ? "" : " | ", I.c_tag) //I'm not fixing this shit...
+ foo = 1
+ queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
+ else
+ queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
+ else
+ queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
+ if(viewalerts)
+ ai_alerts()
+
+/mob/living/silicon/ai/alarm_cancelled(src, class, area/A, obj/origin, cleared)
+ if(cleared)
+ if(!(class in alarms_listend_for))
+ return
+ if(origin.z != z)
+ return
+ queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
+ if(viewalerts)
+ ai_alerts()
+
/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C)
if(!tracking)
cameraFollow = null
- if(!C || stat == DEAD) //C.can_use())
+ if(QDELETED(C) || stat == DEAD) //C.can_use())
return FALSE
if(!eyeobj)
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index 61151e35c92..106eaf86d37 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -132,8 +132,6 @@
sleep(50)
theAPC = null
- process_queued_alarms()
-
/mob/living/silicon/ai/updatehealth(reason = "none given")
if(status_flags & GODMODE)
health = 100
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index db95f18c210..0a4e4cee898 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -563,6 +563,10 @@
var/obj/item/holder/H = ..()
if(!istype(H))
return
+ if(stat == DEAD)
+ H.icon = 'icons/mob/pai.dmi'
+ H.icon_state = "[chassis]_dead"
+ return
if(resting)
icon_state = "[chassis]"
resting = 0
diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm
index 2a1662b064d..52d58ad659d 100644
--- a/code/modules/mob/living/silicon/robot/component.dm
+++ b/code/modules/mob/living/silicon/robot/component.dm
@@ -312,8 +312,3 @@ proc/robot_healthscan(mob/user, mob/living/M)
to_chat(user, "[capitalize(O.name)]: [O.damage]")
if(!organ_found)
to_chat(user, "No prosthetics located.")
-
- if(ismachineperson(H))
- to_chat(user, "Internal Fluid Level:[H.blood_volume]/[H.max_blood]")
- if(H.bleed_rate)
- to_chat(user, "Warning:External component leak detected!")
diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm
index 72d9b210712..65847c3a9b1 100644
--- a/code/modules/mob/living/silicon/robot/death.dm
+++ b/code/modules/mob/living/silicon/robot/death.dm
@@ -53,7 +53,7 @@
emote("deathgasp", force = TRUE)
if(module)
- module.handle_death(gibbed)
+ module.handle_death(src, gibbed)
// Only execute the below if we successfully died
. = ..(gibbed)
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 5cc6e50fd06..270606361cb 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -59,7 +59,7 @@
// Disable the microphone wire on Drones
if(radio)
- radio.wires.CutWireIndex(RADIO_WIRE_TRANSMIT)
+ radio.wires.cut(WIRE_RADIO_TRANSMIT)
if(camera && ("Robots" in camera.network))
camera.network.Add("Engineering")
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index 07080e694bf..a183a8fc455 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -13,7 +13,6 @@
handle_robot_cell()
process_locks()
update_items()
- process_queued_alarms()
/mob/living/silicon/robot/proc/handle_robot_cell()
@@ -46,7 +45,7 @@
/mob/living/silicon/robot/proc/handle_equipment()
if(camera && !scrambledcodes)
- if(stat == DEAD || wires.IsCameraCut())
+ if(stat == DEAD || wires.is_cut(WIRE_BORG_CAMERA))
camera.status = 0
else
camera.status = 1
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 7f1a18ac22d..af1feef3ee7 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -59,7 +59,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/ear_protection = 0
var/damage_protection = 0
var/emp_protection = FALSE
- var/xeno_disarm_chance = 85
var/list/force_modules = list()
var/allow_rename = TRUE
@@ -72,11 +71,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/list/req_access
var/ident = 0
//var/list/laws = list()
- var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
var/viewalerts = 0
var/modtype = "Default"
var/lower_mod = 0
- var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N
+ var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever/N
var/jeton = 0
var/low_power_mode = 0 //whether the robot has no charge left.
var/weapon_lock = 0
@@ -112,7 +110,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/get_cell()
return cell
-/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
+/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
@@ -134,13 +132,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
radio = new /obj/item/radio/borg(src)
common_radio = radio
- init(ai_to_sync_to = ai_to_sync_to)
+ init(alien, connect_to_AI, ai_to_sync_to)
if(has_camera && !camera)
camera = new /obj/machinery/camera(src)
camera.c_tag = real_name
camera.network = list("SS13","Robots")
- if(wires.IsCameraCut()) // 5 = BORG CAMERA
+ if(wires.is_cut(WIRE_BORG_CAMERA)) // 5 = BORG CAMERA
camera.status = 0
if(mmi == null)
@@ -172,18 +170,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
scanner = new(src)
scanner.Grant(src)
-/mob/living/silicon/robot/proc/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
+/mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
make_laws()
additional_law_channels["Binary"] = ":b "
+ if(!connect_to_AI)
+ return
var/found_ai = ai_to_sync_to
if(!found_ai)
found_ai = select_active_ai_with_fewest_borgs()
if(found_ai)
- lawupdate = 1
+ lawupdate = TRUE
connect_to_ai(found_ai)
else
- lawupdate = 0
+ lawupdate = FALSE
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
@@ -270,6 +270,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
//Improved /N
/mob/living/silicon/robot/Destroy()
+ SStgui.close_uis(wires)
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
var/turf/T = get_turf(loc)//To hopefully prevent run time errors.
if(T) mmi.loc = T
@@ -543,6 +544,43 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
src.verbs -= GLOB.robot_verbs_default
src.verbs -= silicon_subsystems
+/mob/living/silicon/robot/verb/cmd_robot_alerts()
+ set category = "Robot Commands"
+ set name = "Show Alerts"
+ if(usr.stat == DEAD)
+ to_chat(src, "Alert: You are dead.")
+ return //won't work if dead
+ robot_alerts()
+
+/mob/living/silicon/robot/proc/robot_alerts()
+ var/list/dat = list()
+ var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
+ for(var/cat in temp_alarm_list)
+ if(!(cat in alarms_listend_for))
+ continue
+ dat += text("[cat] \n")
+ var/list/list/L = temp_alarm_list[cat].Copy()
+ for(var/alarm in L)
+ var/list/list/alm = L[alarm].Copy()
+ var/list/list/sources = alm[3].Copy()
+ var/area_name = alm[1]
+ for(var/thing in sources)
+ var/atom/A = locateUID(thing)
+ if(A && A.z != z)
+ L -= alarm
+ continue
+ dat += ""
+ dat += text("-- [area_name]")
+ dat += " \n"
+ if(!L.len)
+ dat += "-- All Systems Nominal \n"
+ dat += " \n"
+
+ var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410)
+ var/dat_text = dat.Join("")
+ alerts.set_content(dat_text)
+ alerts.open()
+
/mob/living/silicon/robot/proc/ionpulse()
if(!ionpulse_on)
return
@@ -604,6 +642,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/InCritical()
return low_power_mode
+/mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
+ if(!(class in alarms_listend_for))
+ return
+ if(alarmsource.z != z)
+ return
+ if(stat == DEAD)
+ return
+ queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
+
+/mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared)
+ if(cleared)
+ if(!(class in alarms_listend_for))
+ return
+ if(origin.z != z)
+ return
+ queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
+
/mob/living/silicon/robot/ex_act(severity)
switch(severity)
if(1.0)
@@ -795,7 +850,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
opened = FALSE
update_icons()
return
- else if(wiresexposed && wires.IsAllCut())
+ else if(wiresexposed && wires.is_all_cut())
//Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob.
if(!mmi)
to_chat(user, "[src] has no brain to remove.")
@@ -1023,10 +1078,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
src << browse(null, t1)
return 1
- if(href_list["showalerts"])
- subsystem_alarm_monitor()
- return 1
-
if(href_list["mod"])
var/obj/item/O = locate(href_list["mod"])
if(istype(O) && (O.loc == src))
@@ -1041,6 +1092,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
activate_module(O)
installed_modules()
+ //Show alerts window if user clicked on "Show alerts" in chat
+ if(href_list["showalerts"])
+ robot_alerts()
+ return TRUE
+
if(href_list["deact"])
var/obj/item/O = locate(href_list["deact"])
if(activated(O))
@@ -1233,7 +1289,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/proc/SetLockdown(var/state = 1)
// They stay locked down if their wire is cut.
- if(wires.LockedCut())
+ if(wires.is_cut(WIRE_BORG_LOCKED))
state = 1
if(state)
throw_alert("locked", /obj/screen/alert/locked)
@@ -1343,14 +1399,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
ear_protection = 1 // Immunity to the audio part of flashbangs
damage_protection = 10 // Reduce all incoming damage by this number
- xeno_disarm_chance = 20
allow_rename = FALSE
modtype = "Commando"
faction = list("nanotrasen")
is_emaggable = FALSE
default_cell_type = /obj/item/stock_parts/cell/bluespace
-/mob/living/silicon/robot/deathsquad/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
+/mob/living/silicon/robot/deathsquad/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
laws = new /datum/ai_laws/deathsquad
module = new /obj/item/robot_module/deathsquad(src)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
@@ -1380,7 +1435,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/eprefix = "Amber"
-/mob/living/silicon/robot/ert/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
+/mob/living/silicon/robot/ert/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
laws = new /datum/ai_laws/ert_override
radio = new /obj/item/radio/borg/ert(src)
radio.recalculateChannels()
@@ -1413,7 +1468,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
damage_protection = 5 // Reduce all incoming damage by this number
eprefix = "Gamma"
magpulse = 1
- xeno_disarm_chance = 40
/mob/living/silicon/robot/destroyer
@@ -1433,10 +1487,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
ear_protection = 1 // Immunity to the audio part of flashbangs
emp_protection = TRUE // Immunity to EMP, due to heavy shielding
damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg.
- xeno_disarm_chance = 10
default_cell_type = /obj/item/stock_parts/cell/bluespace
-/mob/living/silicon/robot/destroyer/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
+/mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
additional_law_channels["Binary"] = ":b "
laws = new /datum/ai_laws/deathsquad
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 8445a399582..84eb782833b 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -2,19 +2,17 @@
if(M.a_intent == INTENT_DISARM)
if(!lying)
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
- if(prob(xeno_disarm_chance))
- Stun(7)
- step(src, get_dir(M,src))
- spawn(5)
- step(src, get_dir(M,src))
- add_attack_logs(M, src, "Alien pushed over")
- playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1)
- visible_message("[M] has forced back [src]!",\
- "[M] has forced back [src]!")
+ var/obj/item/I = get_active_hand()
+ if(I)
+ uneq_active()
+ visible_message("[M] disarmed [src]!", "[M] has disabled [src]'s active module!")
+ add_attack_logs(M, src, "alien disarmed")
else
- playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1)
- visible_message("[M] took a swipe at [src]!",\
- "[M] took a swipe at [src]!")
+ Stun(2)
+ step(src, get_dir(M,src))
+ add_attack_logs(M, src, "Alien pushed over")
+ visible_message("[M] forces back [src]!", "[M] forces back [src]!")
+ playsound(loc, 'sound/weapons/pierce.ogg', 50, TRUE, -1)
else
..()
return
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 8e8c67693c5..ff967354131 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -114,7 +114,7 @@
/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W)
return FALSE
-/obj/item/robot_module/proc/handle_death(gibbed)
+/obj/item/robot_module/proc/handle_death(mob/living/silicon/robot/R, gibbed)
return
/obj/item/robot_module/standard
@@ -255,7 +255,7 @@
fix_modules()
-/obj/item/robot_module/engineering/handle_death()
+/obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed)
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
if(G)
G.drop_gripped_item(silent = TRUE)
@@ -368,6 +368,11 @@
R.add_language("Clownish",1)
R.add_language("Neo-Russkiya", 1)
+/obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed)
+ var/obj/item/storage/bag/tray/cyborg/T = locate(/obj/item/storage/bag/tray/cyborg) in modules
+ if(istype(T))
+ T.drop_inventory(R)
+
/obj/item/robot_module/miner
name = "miner robot module"
@@ -623,7 +628,7 @@
..()
-/obj/item/robot_module/drone/handle_death()
+/obj/item/robot_module/drone/handle_death(mob/living/silicon/robot/R, gibbed)
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
if(G)
G.drop_gripped_item(silent = TRUE)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index c923e0912b7..7f9c0849d9c 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -11,9 +11,11 @@
var/list/stating_laws = list()// Channels laws are currently being stated on
var/list/alarms_to_show = list()
var/list/alarms_to_clear = list()
+ var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
+ var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
+ var/list/alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera")
//var/list/hud_list[10]
var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer
- var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to
var/designation = ""
var/obj/item/camera/siliconcam/aiCamera = null //photography
//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented.
@@ -25,9 +27,6 @@
//var/sensor_mode = 0 //Determines the current HUD.
- var/next_alarm_notice
- var/list/datum/alarm/queued_alarms = new()
-
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD)
@@ -46,6 +45,8 @@
diag_hud_set_health()
add_language("Galactic Common")
init_subsystems()
+ RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
+ RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
/mob/living/silicon/med_hud_set_health()
return //we use a different hud
@@ -55,10 +56,93 @@
/mob/living/silicon/Destroy()
GLOB.silicon_mob_list -= src
- for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.unregister(src)
return ..()
+/mob/living/silicon/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
+ return
+
+/mob/living/silicon/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
+ return
+
+/mob/living/silicon/proc/queueAlarm(message, type, incoming = TRUE)
+ var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
+ if(incoming)
+ alarms_to_show += message
+ alarm_types_show[type] += 1
+ else
+ alarms_to_clear += message
+ alarm_types_clear[type] += 1
+
+ if(in_cooldown)
+ return
+
+ addtimer(CALLBACK(src, .proc/show_alarms), 3 SECONDS)
+
+/mob/living/silicon/proc/show_alarms()
+ if(alarms_to_show.len < 5)
+ for(var/msg in alarms_to_show)
+ to_chat(src, msg)
+ else if(length(alarms_to_show))
+
+ var/list/msg = list("--- ")
+
+ if(alarm_types_show["Burglar"])
+ msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
+
+ if(alarm_types_show["Motion"])
+ msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
+
+ if(alarm_types_show["Fire"])
+ msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
+
+ if(alarm_types_show["Atmosphere"])
+ msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
+
+ if(alarm_types_show["Power"])
+ msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
+
+ if(alarm_types_show["Camera"])
+ msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
+
+ msg += "\[Show Alerts\]"
+ var/msg_text = msg.Join("")
+ to_chat(src, msg_text)
+
+ if(alarms_to_clear.len < 3)
+ for(var/msg in alarms_to_clear)
+ to_chat(src, msg)
+
+ else if(alarms_to_clear.len)
+ var/list/msg = list("--- ")
+
+ if(alarm_types_clear["Motion"])
+ msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
+
+ if(alarm_types_clear["Fire"])
+ msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
+
+ if(alarm_types_clear["Atmosphere"])
+ msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
+
+ if(alarm_types_clear["Power"])
+ msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
+
+ if(alarm_types_show["Camera"])
+ msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
+
+ msg += "\[Show Alerts\]"
+
+ var/msg_text = msg.Join("")
+ to_chat(src, msg_text)
+
+
+ alarms_to_show.Cut()
+ alarms_to_clear.Cut()
+ for(var/key in alarm_types_show)
+ alarm_types_show[key] = 0
+ for(var/key in alarm_types_clear)
+ alarm_types_clear[key] = 0
+
/mob/living/silicon/rename_character(oldname, newname)
// we actually don't want it changing minds and stuff
if(!newname)
@@ -283,63 +367,6 @@
if("Disable")
to_chat(src, "Sensor augmentations disabled.")
-/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised)
- if(!next_alarm_notice)
- next_alarm_notice = world.time + 10 SECONDS
-
- var/list/alarms = queued_alarms[alarm_handler]
- if(was_raised)
- // Raised alarms are always set
- alarms[alarm] = 1
- else
- // Alarms that were raised but then cleared before the next notice are instead removed
- if(alarm in alarms)
- alarms -= alarm
- // And alarms that have only been cleared thus far are set as such
- else
- alarms[alarm] = -1
-
-/mob/living/silicon/proc/process_queued_alarms()
- if(next_alarm_notice && (world.time > next_alarm_notice))
- next_alarm_notice = 0
-
- var/alarm_raised = 0
- for(var/datum/alarm_handler/AH in queued_alarms)
- var/list/alarms = queued_alarms[AH]
- var/reported = 0
- for(var/datum/alarm/A in alarms)
- if(alarms[A] == 1)
- if(!reported)
- reported = 1
- to_chat(src, "--- [AH.category] Detected ---")
- raised_alarm(A)
-
- for(var/datum/alarm_handler/AH in queued_alarms)
- var/list/alarms = queued_alarms[AH]
- var/reported = 0
- for(var/datum/alarm/A in alarms)
- if(alarms[A] == -1)
- if(!reported)
- reported = 1
- to_chat(src, "--- [AH.category] Cleared ---")
- to_chat(src, "\The [A.alarm_name()].")
-
- if(alarm_raised)
- to_chat(src, "\[Show Alerts\]")
-
- for(var/datum/alarm_handler/AH in queued_alarms)
- var/list/alarms = queued_alarms[AH]
- alarms.Cut()
-
-/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A)
- to_chat(src, "[A.alarm_name()]!")
-
-/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A)
- var/cameratext = ""
- for(var/obj/machinery/camera/C in A.cameras())
- cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]"
- to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])")
-
/mob/living/silicon/adjustToxLoss(var/amount)
return STATUS_UPDATE_NONE
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index e720400abd5..6ac5f8cded2 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -3,11 +3,10 @@
/mob/living/silicon/attack_alien(mob/living/carbon/alien/humanoid/M)
if(..()) //if harm or disarm intent
- var/damage = rand(10, 20)
+ var/damage = 20
if(prob(90))
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
- visible_message("[M] has slashed at [src]!", \
- "[M] has slashed at [src]!")
+ visible_message("[M] has slashed at [src]!", "[M] has slashed at [src]!")
if(prob(8))
flash_eyes(affect_silicon = 1)
add_attack_logs(M, src, "Alien attacked")
@@ -64,6 +63,6 @@
else
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
playsound(loc, 'sound/effects/bang.ogg', 10, 1)
- visible_message("[M] punches [src], but doesn't leave a dent.", \
- "[M] punches [src], but doesn't leave a dent.!")
+ visible_message("[M] punches [src], but doesn't leave a dent.", \
+ "[M] punches [src], but doesn't leave a dent.")
return FALSE
diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm
index d18d41a40cf..65a92d07008 100644
--- a/code/modules/mob/living/silicon/subsystems.dm
+++ b/code/modules/mob/living/silicon/subsystems.dm
@@ -8,13 +8,11 @@
/mob/living/silicon
var/list/silicon_subsystems = list(
- /mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_law_manager
)
/mob/living/silicon/ai
silicon_subsystems = list(
- /mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_atmos_control,
/mob/living/silicon/proc/subsystem_crew_monitor,
/mob/living/silicon/proc/subsystem_law_manager,
@@ -23,7 +21,6 @@
/mob/living/silicon/robot/drone
silicon_subsystems = list(
- /mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_law_manager,
/mob/living/silicon/proc/subsystem_power_monitor
)
@@ -32,30 +29,11 @@
register_alarms = 0
/mob/living/silicon/proc/init_subsystems()
- alarm_monitor = new(src)
atmos_control = new(src)
crew_monitor = new(src)
law_manager = new(src)
power_monitor = new(src)
- if(!register_alarms)
- return
-
- var/list/register_to = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
- for(var/datum/alarm_handler/AH in register_to)
- AH.register(src, /mob/living/silicon/proc/receive_alarm)
- queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
- alarm_handlers |= AH
-
-/********************
-* Alarm Monitor *
-********************/
-/mob/living/silicon/proc/subsystem_alarm_monitor()
- set name = "Alarm Monitor"
- set category = "Subsystems"
-
- alarm_monitor.ui_interact(usr, state = GLOB.self_state)
-
/********************
* Atmos Control *
********************/
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index b42f3275997..28fff50a124 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -42,13 +42,18 @@
/mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M)
if(..()) //if harm or disarm intent.
- var/damage = rand(15, 30)
- visible_message("[M] has slashed at [src]!", \
- "[M] has slashed at [src]!")
- playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
- add_attack_logs(M, src, "Alien attacked")
- attack_threshold_check(damage)
- return
+ if(M.a_intent == INTENT_DISARM)
+ playsound(loc, 'sound/weapons/pierce.ogg', 25, TRUE, -1)
+ visible_message("[M] [response_disarm] [name]!", "[M] [response_disarm] you!")
+ add_attack_logs(M, src, "Alien disarmed")
+ else
+ var/damage = rand(15, 30)
+ visible_message("[M] has slashed at [src]!", \
+ "[M] has slashed at [src]!")
+ playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
+ add_attack_logs(M, src, "Alien attacked")
+ attack_threshold_check(damage)
+ return TRUE
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)
if(..()) //successful larva bite
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index f1278918898..dbd8ae6033a 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -66,6 +66,7 @@
RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/human_squish_check)
/mob/living/simple_animal/bot/mulebot/Destroy()
+ SStgui.close_uis(wires)
unload(0)
QDEL_NULL(wires)
QDEL_NULL(cell)
@@ -142,7 +143,7 @@
if(open)
icon_state="mulebot-hatch"
else
- icon_state = "mulebot[!wires.MobAvoid()]"
+ icon_state = "mulebot[wires.is_cut(WIRE_MOB_AVOIDANCE)]"
overlays.Cut()
if(load && !ismob(load))//buckling handles the mob offsets
load.pixel_y = initial(load.pixel_y) + 9
@@ -158,9 +159,9 @@
qdel(src)
if(2)
for(var/i = 1; i < 3; i++)
- wires.RandomCut()
+ wires.cut_random()
if(3)
- wires.RandomCut()
+ wires.cut_random()
return
/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
@@ -169,7 +170,7 @@
unload(0)
if(prob(25))
visible_message("Something shorts out inside [src]!")
- wires.RandomCut()
+ wires.cut_random()
/mob/living/simple_animal/bot/mulebot/Topic(href, list/href_list)
if(..())
@@ -318,7 +319,7 @@
// returns true if the bot has power
/mob/living/simple_animal/bot/mulebot/proc/has_power()
- return !open && cell && cell.charge > 0 && wires.HasPower()
+ return !open && cell && cell.charge > 0 && !wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2)
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
switch(type)
@@ -362,7 +363,7 @@
if(istype(AM,/obj/structure/closet/crate))
CRATE = AM
else
- if(wires.LoadCheck())
+ if(!wires.is_cut(WIRE_LOADCHECK))
buzz(SIGH)
return // if not hacked, only allow crates to be loaded
@@ -459,8 +460,7 @@
on = 0
return
if(on)
- var/speed = (wires.Motor1() ? 1 : 0) + (wires.Motor2() ? 2 : 0)
-// to_chat(world, "speed: [speed]")
+ var/speed = (wires.is_cut(WIRE_MOTOR1) ? 1 : 0) + (wires.is_cut(WIRE_MOTOR2) ? 2 : 0)
var/num_steps = 0
switch(speed)
if(0)
@@ -624,7 +624,7 @@
// not loaded
if(auto_pickup) // find a crate
var/atom/movable/AM
- if(wires.LoadCheck()) // if hacked, load first unanchored thing we find
+ if(wires.is_cut(WIRE_LOADCHECK)) // if hacked, load first unanchored thing we find
for(var/atom/movable/A in get_step(loc, loaddir))
if(!A.anchored)
AM = A
@@ -672,7 +672,7 @@
// called when bot bumps into anything
/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
- if(!wires.MobAvoid()) // usually just bumps, but if avoidance disabled knock over mobs
+ if(wires.is_cut(WIRE_MOB_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs
var/mob/M = obs
if(ismob(M))
if(istype(M,/mob/living/silicon/robot))
@@ -736,8 +736,8 @@
..()
/mob/living/simple_animal/bot/mulebot/receive_signal(datum/signal/signal)
- if(!wires.RemoteRX() || ..())
- return 1
+ if(wires.is_cut(WIRE_REMOTE_RX) || ..())
+ return TRUE
var/recv = signal.data["command"]
@@ -772,7 +772,7 @@
// send a radio signal with multiple data key/values
/mob/living/simple_animal/bot/mulebot/post_signal_multiple(var/freq, var/list/keyval)
- if(!wires.RemoteTX())
+ if(wires.is_cut(WIRE_REMOTE_TX))
return
..()
@@ -803,7 +803,7 @@
//Update navigation data. Called when commanded to deliver, return home, or a route update is needed...
/mob/living/simple_animal/bot/mulebot/proc/get_nav()
- if(!on || !wires.BeaconRX())
+ if(!on || wires.is_cut(WIRE_BEACON_RX))
return
for(var/obj/machinery/navbeacon/NB in GLOB.deliverybeacons)
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 41032185c3b..7a26de718b6 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -44,18 +44,27 @@
usr.emote(message)
-/mob/proc/say_dead(var/message)
- if(!(client && client.holder))
- if(!config.dsay_allowed)
- to_chat(src, "Deadchat is globally muted.")
+/mob/proc/say_dead(message)
+ if(client)
+ if(!client.holder)
+ if(!config.dsay_allowed)
+ to_chat(src, "Deadchat is globally muted.")
+ return
+
+ if(client.prefs.muted & MUTE_DEADCHAT)
+ to_chat(src, "You cannot talk in deadchat (muted).")
return
- if(client && !(client.prefs.toggles & CHAT_DEAD))
- to_chat(usr, "You have deadchat muted.")
- return
+ if(!(client.prefs.toggles & CHAT_DEAD))
+ to_chat(src, "You have deadchat muted.")
+ return
+
+ if(client.handle_spam_prevention(message, MUTE_DEADCHAT))
+ return
say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src)
create_log(DEADCHAT_LOG, message)
+ log_ghostsay(message, src)
/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null)
if(stat == DEAD)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 8e7adea666b..d151a98309f 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -44,26 +44,37 @@
-//human -> robot
-/mob/living/carbon/human/proc/Robotize()
+/**
+ For transforming humans into robots (cyborgs).
+
+ Arguments:
+ * cell_type: A type path of the cell the new borg should receive.
+ * connect_to_default_AI: TRUE if you want /robot/New() to handle connecting the borg to the AI with the least borgs.
+ * AI: A reference to the AI we want to connect to.
+*/
+/mob/living/carbon/human/proc/Robotize(cell_type = null, connect_to_default_AI = TRUE, mob/living/silicon/ai/AI = null)
if(notransform)
return
for(var/obj/item/W in src)
unEquip(W)
- regenerate_icons()
+
notransform = 1
canmove = 0
icon = null
invisibility = 101
- for(var/t in bodyparts)
- qdel(t)
- for(var/i in internal_organs)
- qdel(i)
- var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc )
+ // Creating a new borg here will connect them to a default AI and notify that AI, if `connect_to_default_AI` is TRUE.
+ var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(loc, connect_to_AI = connect_to_default_AI)
- // cyborgs produced by Robotize get an automatic power cell
- O.cell = new /obj/item/stock_parts/cell/high(O)
+ // If `AI` is passed in, we want to connect to that AI specifically.
+ if(AI)
+ O.lawupdate = TRUE
+ O.connect_to_ai(AI)
+
+ if(!cell_type)
+ O.cell = new /obj/item/stock_parts/cell/high(O)
+ else
+ O.cell = new cell_type(O)
O.gender = gender
O.invisibility = 0
@@ -77,9 +88,8 @@
else
O.key = key
- O.loc = loc
+ O.forceMove(loc)
O.job = "Cyborg"
- O.notify_ai(1)
if(O.mind && O.mind.assigned_role == "Cyborg")
if(O.mind.role_alt_title == "Robot")
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 4f1bd269337..a2357fd9ebe 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -35,7 +35,7 @@
/obj/machinery/modular_computer/console/preset/engineering/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/power_monitor())
- hard_drive.store_file(new/datum/computer_file/program/alarm_monitor())
+// hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) //TO-DO:TGUI--Uncomment Modular computers
hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor())
// ===== RESEARCH CONSOLE =====
diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm
index 8cb47337a6d..2f2f4a08232 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm
@@ -7,65 +7,71 @@
requires_ntnet = 1
network_destination = "alarm monitoring network"
size = 5
- var/list/datum/alarm_handler/alarm_handlers
+ var/tgui_id = "NtosStationAlertConsole"
+ var/ui_x = 315
+ var/ui_y = 500
+ var/has_alert = 0
+ var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power")
-/datum/computer_file/program/alarm_monitor/New()
+/datum/computer_file/program/alarm_monitor/process_tick()
..()
- alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
- for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon)
-/datum/computer_file/program/alarm_monitor/Destroy()
- for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.unregister(src)
- QDEL_NULL(alarm_handlers)
- return ..()
+ if(has_alert)
+ program_icon_state = "alert-red"
+ ui_header = "alarm_red.gif"
+ update_computer_icon()
+ else
+ program_icon_state = "alert-green"
+ ui_header = "alarm_green.gif"
+ update_computer_icon()
+ return TRUE
-/datum/computer_file/program/alarm_monitor/proc/update_icon()
- for(var/datum/alarm_handler/AH in alarm_handlers)
- if(AH.has_major_alarms())
- program_icon_state = "alert-red"
- ui_header = "alarm_red.gif"
- update_computer_icon()
- return 1
- program_icon_state = "alert-green"
- ui_header = "alarm_green.gif"
- update_computer_icon()
- return 0
-
-/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
- assets.send(user)
- ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700)
- ui.set_auto_update(1)
- ui.set_layout_key("program")
- ui.open()
-
-/datum/computer_file/program/alarm_monitor/ui_data(mob/user)
+/datum/computer_file/program/alarm_monitor/tgui_data(mob/user)
var/list/data = get_header_data()
- var/categories[0]
- for(var/datum/alarm_handler/AH in alarm_handlers)
- categories[++categories.len] = list("category" = AH.category, "alarms" = list())
- for(var/datum/alarm/A in AH.major_alarms())
- var/cameras[0]
- var/lost_sources[0]
-
- if(isAI(user))
- for(var/obj/machinery/camera/C in A.cameras())
- cameras[++cameras.len] = C.nano_structure()
- for(var/datum/alarm_source/AS in A.sources)
- if(!AS.source)
- lost_sources[++lost_sources.len] = AS.source_name
-
- categories[categories.len]["alarms"] += list(list(
- "name" = sanitize(A.alarm_name()),
- "origin_lost" = A.origin == null,
- "has_cameras" = cameras.len,
- "cameras" = cameras,
- "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
- data["categories"] = categories
+ data["alarms"] = list()
+ for(var/class in SSalarm.alarms)
+ if(!(class in alarms_listend_for))
+ continue
+ data["alarms"][class] = list()
+ for(var/area in alarms[class])
+ data["alarms"][class] += area
return data
+
+/datum/computer_file/program/alarm_monitor/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
+ if(is_station_level(alarmsource.z))
+ if(!(A.type in GLOB.the_station_areas))
+ return
+ else if(!is_mining_level(alarmsource.z) || istype(A, /area/ruin))
+ return
+ update_alarm_display()
+
+/datum/computer_file/program/alarm_monitor/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
+ if(is_station_level(origin.z))
+ if(!(A.type in GLOB.the_station_areas))
+ return
+ else if(!is_mining_level(origin.z) || istype(A, /area/ruin))
+ return
+ update_alarm_display()
+
+/datum/computer_file/program/alarm_monitor/proc/update_alarm_display()
+ has_alert = FALSE
+ for(var/cat in alarms)
+ if(!(cat in alarms_listend_for))
+ continue
+ var/list/L = alarms[cat]
+ if(length(L))
+ has_alert = TRUE
+
+/datum/computer_file/program/alarm_monitor/run_program(mob/user)
+ . = ..(user)
+ GLOB.alarmdisplay += src
+ RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
+ RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
+
+/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE)
+ GLOB.alarmdisplay -= src
+ UnregisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM)
+ UnregisterSignal(SSalarm, COMSIG_CANCELLED_ALARM)
+ ..()
diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm
deleted file mode 100644
index cee3820b102..00000000000
--- a/code/modules/nano/modules/alarm_monitor.dm
+++ /dev/null
@@ -1,90 +0,0 @@
-/datum/nano_module/alarm_monitor
- name = "Alarm monitor"
- var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use.
- var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
-
-/datum/nano_module/alarm_monitor/all/New()
- ..()
- alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
-
-/datum/nano_module/alarm_monitor/engineering/New()
- ..()
- alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
-
-/datum/nano_module/alarm_monitor/security/New()
- ..()
- alarm_handlers = list(SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.motion_alarm)
-
-/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName)
- for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.register(object, procName)
-
-/datum/nano_module/alarm_monitor/proc/unregister(var/object)
- for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.unregister(object)
-
-/datum/nano_module/alarm_monitor/proc/all_alarms()
- var/list/all_alarms = new()
- for(var/datum/alarm_handler/AH in alarm_handlers)
- all_alarms += AH.alarms
-
- return all_alarms
-
-/datum/nano_module/alarm_monitor/proc/major_alarms()
- var/list/all_alarms = new()
- for(var/datum/alarm_handler/AH in alarm_handlers)
- all_alarms += AH.major_alarms()
-
- return all_alarms
-
-/datum/nano_module/alarm_monitor/proc/minor_alarms()
- var/list/all_alarms = new()
- for(var/datum/alarm_handler/AH in alarm_handlers)
- all_alarms += AH.minor_alarms()
-
- return all_alarms
-
-/datum/nano_module/alarm_monitor/Topic(ref, href_list)
- if(..())
- return 1
- if(href_list["switchTo"])
- var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras
- if(!C || !isAI(usr))
- return
-
- usr.switch_to_camera(C)
- return 1
-
-/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state)
- ui.open()
- ui.set_auto_update(1)
-
-/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
-
- var/categories[0]
- for(var/datum/alarm_handler/AH in alarm_handlers)
- categories[++categories.len] = list("category" = AH.category, "alarms" = list())
- for(var/datum/alarm/A in AH.major_alarms())
- var/cameras[0]
- var/lost_sources[0]
-
- if(isAI(user))
- for(var/obj/machinery/camera/C in A.cameras())
- cameras[++cameras.len] = C.nano_structure()
- for(var/datum/alarm_source/AS in A.sources)
- if(!AS.source)
- lost_sources[++lost_sources.len] = AS.source_name
-
- categories[categories.len]["alarms"] += list(list(
- "name" = sanitize(A.alarm_name()),
- "origin_lost" = A.origin == null,
- "has_cameras" = cameras.len,
- "cameras" = cameras,
- "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
- data["categories"] = categories
-
- return data
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 409abe3d724..4b64266f2b7 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -25,6 +25,11 @@
#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds
+// main_status var
+#define APC_EXTERNAL_POWER_NOTCONNECTED 0
+#define APC_EXTERNAL_POWER_NOENERGY 1
+#define APC_EXTERNAL_POWER_GOOD 2
+
// APC malf status
#define APC_MALF_NOT_HACKED 1
#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core.
@@ -75,7 +80,7 @@
var/lastused_equip = 0
var/lastused_environ = 0
var/lastused_total = 0
- var/main_status = 0
+ var/main_status = APC_EXTERNAL_POWER_NOTCONNECTED
powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
var/malfhack = 0 //New var for my changes to AI malf. --NeoFite
var/mob/living/silicon/ai/malfai = null //See above --NeoFite
@@ -170,6 +175,7 @@
addtimer(CALLBACK(src, .proc/update), 5)
/obj/machinery/power/apc/Destroy()
+ SStgui.close_uis(wires)
GLOB.apcs -= src
if(malfai && operating)
malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10,0,1000)
@@ -212,12 +218,11 @@
if(isarea(A))
area = A
// no-op, keep the name
- else if(isarea(A) && src.areastring == null)
+ else if(isarea(A) && !areastring)
area = A
name = "\improper [area.name] APC"
else
- area = get_area_name(areastring)
- name = "\improper [area.name] APC"
+ name = "\improper [get_area_name(area, TRUE)] APC"
area.apc |= src
update_icon()
@@ -429,8 +434,8 @@
//attack with an item - open/close cover, insert cell, or (un)lock interface
/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params)
- if(issilicon(user) && get_dist(src,user)>1)
- return src.attack_hand(user)
+ if(issilicon(user) && get_dist(src, user) > 1)
+ return attack_hand(user)
else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
if(cell)
@@ -445,7 +450,7 @@
W.forceMove(src)
cell = W
user.visible_message(\
- "[user.name] has inserted the power cell to [src.name]!",\
+ "[user.name] has inserted the power cell to [name]!",\
"You insert the power cell.")
chargecount = 0
update_icon()
@@ -474,7 +479,7 @@
return
user.visible_message("[user.name] adds cables to the APC frame.", \
"You start adding cables to the APC frame...")
- playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
+ playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
if(do_after(user, 20, target = src))
if(C.get_amount() < 10 || !C)
return
@@ -499,10 +504,10 @@
user.visible_message("[user.name] inserts the power control board into [src].", \
"You start to insert the power control board into the frame...")
- playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
+ playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
if(do_after(user, 10, target = src))
- if(has_electronics==0)
- has_electronics = 1
+ if(!has_electronics)
+ has_electronics = TRUE
locked = FALSE
to_chat(user, "You place the power control board inside the frame.")
qdel(W)
@@ -549,11 +554,11 @@
return
to_chat(user, "You are trying to remove the power control board..." )
if(I.use_tool(src, user, 50, volume = I.tool_volume))
- if(has_electronics==1)
- has_electronics = 0
+ if(has_electronics)
+ has_electronics = FALSE
if(stat & BROKEN)
user.visible_message(\
- "[user.name] has broken the power control board inside [src.name]!",
+ "[user.name] has broken the power control board inside [name]!",
"You break the charred power control board and remove the remains.",
"You hear a crack.")
return
@@ -561,19 +566,19 @@
else if(emagged) // We emag board, not APC's frame
emagged = FALSE
user.visible_message(
- "[user.name] has discarded emaged power control board from [src.name]!",
- "You discarded shorten board.")
+ "[user.name] has discarded the shorted power control board from [name]!",
+ "You discarded the shorted board.")
return
else if(malfhack) // AI hacks board, not APC's frame
user.visible_message(\
- "[user.name] has discarded strangely programmed power control board from [src.name]!",
- "You discarded strangely programmed board.")
+ "[user.name] has discarded strangely the programmed power control board from [name]!",
+ "You discarded the strangely programmed board.")
malfai = null
malfhack = 0
return
else
user.visible_message(\
- "[user.name] has removed the power control board from [src.name]!",
+ "[user.name] has removed the power control board from [name]!",
"You remove the power control board.")
new /obj/item/apc_electronics(loc)
return
@@ -648,7 +653,7 @@
else if(stat & (BROKEN|MAINT))
to_chat(user, "Nothing happens!")
else
- if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack)
+ if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack)
locked = !locked
to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.")
update_icon()
@@ -711,36 +716,29 @@
// attack with hand - remove cell (if cover open) or interact with the APC
/obj/machinery/power/apc/attack_hand(mob/user)
-// if(!can_use(user)) This already gets called in interact() and in topic()
-// return
if(!user)
return
- src.add_fingerprint(user)
+ add_fingerprint(user)
- if(usr == user && opened && (!issilicon(user)))
+ if(usr == user && opened && !issilicon(user))
if(cell)
- if(issilicon(user))
- cell.loc=src.loc // Drop it, whoops.
- else
- user.put_in_hands(cell)
+ user.put_in_hands(cell)
cell.add_fingerprint(user)
cell.update_icon()
-
- src.cell = null
- user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.")
-// to_chat(user, "You remove the power cell.")
- charging = 0
- src.update_icon()
+ cell = null
+ user.visible_message("[user.name] removes [cell] from [src]!", "You remove the [cell].")
+ charging = FALSE
+ update_icon()
return
if(stat & (BROKEN|MAINT))
return
- src.interact(user)
+ interact(user)
/obj/machinery/power/apc/attack_ghost(mob/user)
if(panel_open)
wires.Interact(user)
- return ui_interact(user)
+ return tgui_interact(user)
/obj/machinery/power/apc/interact(mob/user)
if(!user)
@@ -749,7 +747,7 @@
if(panel_open)
wires.Interact(user)
- return ui_interact(user)
+ return tgui_interact(user)
/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf)
@@ -770,22 +768,16 @@
else
return APC_MALF_NOT_HACKED
-/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(!user)
- return
-
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/power/apc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- // the ui does not exist, so we'll create a new one
- ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460)
+ ui = new(user, src, ui_key, "APC", name, 510, 460, master_ui, state)
ui.open()
- // Auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+/obj/machinery/power/apc/tgui_data(mob/user)
+ var/list/data = list()
data["locked"] = is_locked(user)
+ data["normallyLocked"] = locked
data["isOperating"] = operating
data["externalPower"] = main_status
data["powerCellStatus"] = cell ? cell.percent() : null
@@ -853,10 +845,6 @@
// to_chat(world, "[area.power_equip]")
area.power_change()
-/obj/machinery/power/apc/proc/isWireCut(var/wireIndex)
- return wires.IsIndexCut(wireIndex)
-
-
/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic()
if(user.can_admin_interact())
return 1
@@ -866,7 +854,7 @@
var/mob/living/silicon/ai/AI = user
var/mob/living/silicon/robot/robot = user
if( \
- src.aidisabled || \
+ aidisabled || \
malfhack && istype(malfai) && \
( \
(istype(AI) && (malfai!=AI && malfai != AI.parent)) || \
@@ -877,21 +865,21 @@
to_chat(user, "\The [src] has AI control disabled!")
user << browse(null, "window=apc")
user.unset_machine()
- return 0
+ return FALSE
else
- if((!in_range(src, user) || !istype(src.loc, /turf)))
- return 0
+ if((!in_range(src, user) || !istype(loc, /turf)))
+ return FALSE
var/mob/living/carbon/human/H = user
if(istype(H))
if(H.getBrainLoss() >= 60)
for(var/mob/M in viewers(src, null))
to_chat(M, "[H] stares cluelessly at [src] and drools.")
- return 0
+ return FALSE
else if(prob(H.getBrainLoss()))
to_chat(user, "You momentarily forget how to use [src].")
- return 0
- return 1
+ return FALSE
+ return TRUE
/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob)
if(user.can_admin_interact())
@@ -909,104 +897,59 @@
else
return locked
-/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1)
- if(..())
- return 1
-
- if(!can_use(usr, 1))
- return 1
-
- if(href_list["lock"])
- if(!is_authenticated(usr))
- return
-
- coverlocked = !coverlocked
-
- else if(href_list["breaker"])
- if(!is_authenticated(usr))
- return
-
- toggle_breaker()
-
- else if(href_list["toggle_nightshift"])
- if(!is_authenticated(usr))
- return
-
- if(last_nightshift_switch > world.time + 100) // don't spam...
- to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!")
- return
- last_nightshift_switch = world.time
- set_nightshift(!nightshift_lights)
-
- else if(href_list["cmode"])
- if(!is_authenticated(usr))
- return
-
- chargemode = !chargemode
- if(!chargemode)
- charging = 0
- update_icon()
-
- else if(href_list["eqp"])
- if(!is_authenticated(usr))
- return
-
- var/val = text2num(href_list["eqp"])
- equipment = setsubsystem(val)
- update_icon()
- update()
-
- else if(href_list["lgt"])
- if(!is_authenticated(usr))
- return
-
- var/val = text2num(href_list["lgt"])
- lighting = setsubsystem(val)
- update_icon()
- update()
-
- else if(href_list["env"])
- if(!is_authenticated(usr))
- return
-
- var/val = text2num(href_list["env"])
- environ = setsubsystem(val)
- update_icon()
- update()
- else if( href_list["close"] )
- SSnanoui.close_user_uis(usr, src)
-
- return 0
- else if(href_list["close2"])
- usr << browse(null, "window=apcwires")
-
- return 0
-
- else if(href_list["overload"])
- if(issilicon(usr) && !aidisabled)
- overload_lighting()
-
- else if(href_list["malfhack"])
- if(get_malf_status(usr))
- malfhack(usr)
-
- else if(href_list["occupyapc"])
- if(get_malf_status(usr))
- malfoccupy(usr)
-
- else if(href_list["deoccupyapc"])
- if(get_malf_status(usr))
- malfvacate()
-
- else if(href_list["toggleaccess"])
- if(istype(usr, /mob/living/silicon))
- if(emagged || aidisabled || (stat & (BROKEN|MAINT)))
- to_chat(usr, "The APC does not respond to the command.")
+/obj/machinery/power/apc/tgui_act(action, params)
+ if(..() || !can_use(usr, TRUE) || (locked && !usr.has_unlimited_silicon_privilege && (action != "toggle_nightshift") && !usr.can_admin_interact()))
+ return
+ . = TRUE
+ switch(action)
+ if("lock")
+ if(usr.has_unlimited_silicon_privilege)
+ if(emagged || stat & BROKEN)
+ to_chat(usr, "The APC does not respond to the command!")
+ return FALSE
+ else
+ locked = !locked
+ update_icon()
else
- locked = !locked
+ to_chat(usr, "Access Denied!")
+ return FALSE
+ if("cover")
+ coverlocked = !coverlocked
+ if("breaker")
+ toggle_breaker(usr)
+ if("toggle_nightshift")
+ if(last_nightshift_switch > world.time + 100) // don't spam...
+ to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!")
+ return FALSE
+ last_nightshift_switch = world.time
+ set_nightshift(!nightshift_lights)
+ if("charge")
+ chargemode = !chargemode
+ if("channel")
+ if(params["eqp"])
+ equipment = setsubsystem(text2num(params["eqp"]))
update_icon()
-
- return 0
+ update()
+ else if(params["lgt"])
+ lighting = setsubsystem(text2num(params["lgt"]))
+ update_icon()
+ update()
+ else if(params["env"])
+ environ = setsubsystem(text2num(params["env"]))
+ update_icon()
+ update()
+ if("overload")
+ if(usr.has_unlimited_silicon_privilege)
+ overload_lighting()
+ if("hack")
+ if(get_malf_status(usr))
+ malfhack(usr)
+ if("occupy")
+ if(get_malf_status(usr))
+ malfoccupy(usr)
+ if("deoccupy")
+ if(get_malf_status(usr))
+ malfvacate()
/obj/machinery/power/apc/proc/toggle_breaker()
operating = !operating
@@ -1037,7 +980,7 @@
if(!malf.can_shunt)
to_chat(malf, "You cannot shunt!")
return
- if(!is_station_level(src.z))
+ if(!is_station_level(z))
return
occupier = new /mob/living/silicon/ai(src,malf.laws,null,1)
occupier.adjustOxyLoss(malf.getOxyLoss())
@@ -1084,21 +1027,21 @@
/obj/machinery/power/apc/proc/ion_act()
//intended to be exactly the same as an AI malf attack
- if(!src.malfhack && is_station_level(src.z))
+ if(!malfhack && is_station_level(z))
if(prob(3))
- src.locked = 1
- if(src.cell.charge > 0)
- src.cell.charge = 0
+ locked = TRUE
+ if(cell.charge > 0)
+ cell.charge = 0
cell.corrupt()
- src.malfhack = 1
+ malfhack = TRUE
update_icon()
var/datum/effect_system/smoke_spread/smoke = new
- smoke.set_up(3, 0, src.loc)
+ smoke.set_up(3, 0, loc)
smoke.attach(src)
smoke.start()
do_sparks(3, 1, src)
for(var/mob/M in viewers(src))
- M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2)
+ M.show_message("The [name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2)
/obj/machinery/power/apc/surplus()
@@ -1141,12 +1084,12 @@
var/excess = surplus()
- if(!src.avail())
- main_status = 0
+ if(!avail())
+ main_status = APC_EXTERNAL_POWER_NOTCONNECTED
else if(excess < 0)
- main_status = 1
+ main_status = APC_EXTERNAL_POWER_NOENERGY
else
- main_status = 2
+ main_status = APC_EXTERNAL_POWER_GOOD
if(debug)
log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]")
@@ -1192,31 +1135,31 @@
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
autoflag = 3
- if(report_power_alarm && is_station_contact(z))
- SSalarms.power_alarm.clearAlarm(loc, src)
+ if(report_power_alarm)
+ area.poweralert(TRUE, src)
else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment
if(autoflag != 2)
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
- if(report_power_alarm && is_station_contact(z))
- SSalarms.power_alarm.triggerAlarm(loc, src)
+ if(report_power_alarm)
+ area.poweralert(FALSE, src)
autoflag = 2
else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment
if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0))
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 2)
environ = autoset(environ, 1)
- if(report_power_alarm && is_station_contact(z))
- SSalarms.power_alarm.triggerAlarm(loc, src)
+ if(report_power_alarm)
+ area.poweralert(FALSE, src)
autoflag = 1
else if(cell.charge <= 0) // zero charge, turn all off
if(autoflag != 0)
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
- if(report_power_alarm && is_station_contact(z))
- SSalarms.power_alarm.triggerAlarm(loc, src)
+ if(report_power_alarm)
+ area.poweralert(FALSE, src)
autoflag = 0
// now trickle-charge the cell
@@ -1261,7 +1204,7 @@
if(shock_mobs.len)
var/mob/living/L = pick(shock_mobs)
L.electrocute_act(rand(5, 25), "electrical arc")
- playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1)
+ playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, TRUE)
Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5)
else // no cell, switch everything off
@@ -1271,8 +1214,8 @@
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
- if(report_power_alarm && is_station_contact(z))
- SSalarms.power_alarm.triggerAlarm(loc, src)
+ if(report_power_alarm)
+ area.poweralert(FALSE, src)
autoflag = 0
// update icon & area power if anything changed
@@ -1372,4 +1315,22 @@
L.update(FALSE)
CHECK_TICK
+/obj/machinery/power/apc/proc/relock_callback()
+ locked = TRUE
+ updateDialog()
+
+/obj/machinery/power/apc/proc/check_main_power_callback()
+ if(!wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2))
+ shorted = FALSE
+ updateDialog()
+
+/obj/machinery/power/apc/proc/check_ai_control_callback()
+ if(!wires.is_cut(WIRE_AI_CONTROL))
+ aidisabled = FALSE
+ updateDialog()
+
#undef APC_UPDATE_ICON_COOLDOWN
+
+#undef APC_EXTERNAL_POWER_NOTCONNECTED
+#undef APC_EXTERNAL_POWER_NOENERGY
+#undef APC_EXTERNAL_POWER_GOOD
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index b6c65d979cf..992020e7090 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -229,14 +229,15 @@ By design, d1 is the smallest direction and d2 is the highest
if(current_size >= STAGE_FIVE)
deconstruct()
-obj/structure/cable/proc/cable_color(var/colorC)
- if(colorC)
- if(colorC == "rainbow")
- color = color_rainbow()
- else
- color = colorC
+obj/structure/cable/proc/cable_color(colorC)
+ if(!colorC)
+ color = COLOR_RED
+ else if(colorC == "rainbow")
+ color = color_rainbow()
+ else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
+ color = COLOR_ORANGE
else
- color = "#DD0000"
+ color = colorC
/obj/structure/cable/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
@@ -845,13 +846,15 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
..()
-/obj/item/stack/cable_coil/proc/cable_color(var/colorC)
- if(colorC)
- if(colorC == "rainbow")
- colorC = color_rainbow()
- color = colorC
- else
+/obj/item/stack/cable_coil/proc/cable_color(colorC)
+ if(!colorC)
color = COLOR_RED
+ else if(colorC == "rainbow")
+ color = color_rainbow()
+ else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
+ color = COLOR_ORANGE
+ else
+ color = colorC
/obj/item/stack/cable_coil/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index df457111704..9305faf6637 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -177,6 +177,8 @@
var/nightshift_light_power = 0.45
var/nightshift_light_color = "#FFDDCC"
+ var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode
+
// the smaller bulb light fixture
/obj/machinery/light/small
@@ -238,7 +240,11 @@
switch(status) // set icon_states
if(LIGHT_OK)
- icon_state = "[base_state][on]"
+ var/area/A = get_area(src)
+ if(A && A.fire)
+ icon_state = "[base_state]_emergency"
+ else
+ icon_state = "[base_state][on]"
if(LIGHT_EMPTY)
icon_state = "[base_state]-empty"
on = FALSE
@@ -260,10 +266,20 @@
on = FALSE
update_icon()
if(on)
- var/BR = nightshift_enabled ? nightshift_light_range : brightness_range
- var/PO = nightshift_enabled ? nightshift_light_power : brightness_power
- var/CO = nightshift_enabled ? nightshift_light_color : brightness_color
- var/matching = light_range == BR && light_power == PO && light_color == CO
+ var/BR = brightness_range
+ var/PO = brightness_power
+ var/CO = brightness_color
+ if(color)
+ CO = color
+ var/area/A = get_area(src)
+ if(A && A.fire)
+ CO = bulb_emergency_colour
+ else if(nightshift_enabled)
+ BR = nightshift_light_range
+ PO = nightshift_light_power
+ if(!color)
+ CO = nightshift_light_color
+ var/matching = light && BR == light.light_range && PO == light.light_power && CO == light.light_color
if(!matching)
switchcount++
if(rigged)
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 046cf68a543..960340e90b6 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -310,16 +310,25 @@ field_generator power level display
//This is here to help fight the "hurr durr, release singulo cos nobody will notice before the
//singulo eats the evidence". It's not fool-proof but better than nothing.
//I want to avoid using global variables.
- spawn(1)
- var/temp = 1 //stops spam
- for(var/thing in GLOB.singularities)
- var/obj/singularity/O = thing
- if(O.last_warning && temp)
- if((world.time - O.last_warning) > 50) //to stop message-spam
- temp = 0
- message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (JMP)",1)
- investigate_log("has failed whilst a singulo exists.","singulo")
- O.last_warning = world.time
+ INVOKE_ASYNC(src, .proc/admin_alert)
+
+/obj/machinery/field/generator/proc/admin_alert()
+ var/temp = TRUE //stops spam
+ for(var/thing in GLOB.singularities)
+ var/obj/singularity/O = thing
+ if(O.last_warning && temp && atoms_share_level(O, src))
+ if((world.time - O.last_warning) > 50) //to stop message-spam
+ temp = FALSE
+ // To the person who asks "Hey affected, why are you using this massive operator when you can use AREACOORD?" Well, ill tell you
+ // get_area_name is fucking broken and uses a for(x in world) search
+ // It doesnt even work, is expensive, and returns 0
+ // Im not refactoring one thing which could risk breaking all admin location logs
+ // Fight me
+ // [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)] works much better and actually works at all
+ // Oh and yes, this exact comment was pasted from the exact same thing I did to tcomms code. Dont at me.
+ message_admins("A singularity exists and a containment field has failed on the same Z-Level. Singulo location: [O ? "[get_location_name(O, TRUE)] [COORD(O)]" : "nonexistent location"] [ADMIN_JMP(O)] | Field generator location: [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]")
+ investigate_log("has failed whilst a singulo exists.","singulo")
+ O.last_warning = world.time
/obj/machinery/field/generator/shock_field(mob/living/user)
if(fields.len)
diff --git a/code/modules/power/singularity/investigate.dm b/code/modules/power/singularity/investigate.dm
index 43e8c9f8a8b..4835e09411c 100644
--- a/code/modules/power/singularity/investigate.dm
+++ b/code/modules/power/singularity/investigate.dm
@@ -1,4 +1,4 @@
-/area/engine/engineering/power_alert(var/alarming)
- if(alarming)
- investigate_log("has a power alarm!","singulo")
+/area/engine/engineering/poweralert(state, source)
+ if(state != poweralm)
+ investigate_log("has a power alarm!", "singulo")
..()
diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm
index b8ba53ae432..3ad96050123 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_control.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm
@@ -27,6 +27,7 @@
use_log = list()
/obj/machinery/particle_accelerator/control_box/Destroy()
+ SStgui.close_uis(wires)
if(active)
toggle_power()
QDEL_NULL(wires)
@@ -41,6 +42,11 @@
else if(construction_state == 2) // Wires exposed
wires.Interact(user)
+/obj/machinery/particle_accelerator/control_box/multitool_act(mob/living/user, obj/item/I)
+ if(construction_state == 2) // Wires exposed
+ wires.Interact(user)
+ return TRUE
+
/obj/machinery/particle_accelerator/control_box/update_state()
if(construction_state < 3)
use_power = NO_POWER_USE
@@ -93,18 +99,18 @@
usr.unset_machine()
return
if(href_list["togglep"])
- if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE))
+ if(!wires.is_cut(WIRE_PARTICLE_POWER))
toggle_power()
else if(href_list["scan"])
part_scan()
else if(href_list["strengthup"])
- if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
+ if(!wires.is_cut(WIRE_PARTICLE_STRENGTH))
add_strength()
else if(href_list["strengthdown"])
- if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
+ if(!wires.is_cut(WIRE_PARTICLE_STRENGTH))
remove_strength()
updateDialog()
diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm
index 5f58bcb5dd4..5743bea8475 100644
--- a/code/modules/power/tesla/coil.dm
+++ b/code/modules/power/tesla/coil.dm
@@ -21,6 +21,7 @@
RefreshParts()
/obj/machinery/power/tesla_coil/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
return ..()
diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm
index 8536a0195e3..e6165103234 100644
--- a/code/modules/projectiles/ammunition.dm
+++ b/code/modules/projectiles/ammunition.dm
@@ -123,6 +123,7 @@
if(keep)
stored_ammo.Insert(1,b)
update_mat_value()
+ update_icon()
return b
/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0)
diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm
index 8cb48f8e36a..5cc6a19b840 100644
--- a/code/modules/projectiles/ammunition/magazines.dm
+++ b/code/modules/projectiles/ammunition/magazines.dm
@@ -133,6 +133,9 @@
/obj/item/ammo_box/magazine/internal/shot/riot/short
max_ammo = 3
+/obj/item/ammo_box/magazine/internal/shot/riot/buckshot
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+
/obj/item/ammo_box/magazine/internal/grenadelauncher
name = "grenade launcher internal magazine"
ammo_type = /obj/item/ammo_casing/a40mm
@@ -380,10 +383,7 @@
origin_tech = "combat=3;syndicate=1"
caliber = "shotgun"
max_ammo = 8
-
-/obj/item/ammo_box/magazine/m12g/update_icon()
- ..()
- icon_state = "[initial(icon_state)]-[CEILING(ammo_count(0)/8, 1)*8]"
+ multiple_sprites = 2
/obj/item/ammo_box/magazine/m12g/buckshot
name = "shotgun magazine (12g buckshot slugs)"
@@ -411,6 +411,24 @@
icon_state = "m12gbc"
ammo_type = /obj/item/ammo_casing/shotgun/breaching
+/obj/item/ammo_box/magazine/m12g/XtrLrg
+ name = "\improper XL shotgun magazine (12g slugs)"
+ desc = "An extra large drum magazine."
+ icon_state = "m12gXlSl"
+ w_class = WEIGHT_CLASS_NORMAL
+ ammo_type = /obj/item/ammo_casing/shotgun
+ max_ammo = 16
+
+/obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot
+ name = "\improper XL shotgun magazine (12g buckshot)"
+ icon_state = "m12gXlBs"
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+
+/obj/item/ammo_box/magazine/m12g/XtrLrg/dragon
+ name = "\improper XL shotgun magazine (12g dragon's breath)"
+ icon_state = "m12gXlDb"
+ ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath
+
/obj/item/ammo_box/magazine/toy
name = "foam force META magazine"
ammo_type = /obj/item/ammo_casing/caseless/foam_dart
diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm
index 25db1f10dcb..ca4499c8832 100644
--- a/code/modules/projectiles/guns/projectile.dm
+++ b/code/modules/projectiles/guns/projectile.dm
@@ -95,6 +95,7 @@
if(!user.unEquip(A))
return
to_chat(user, "You screw [S] onto [src].")
+ playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
suppressed = A
S.oldsound = fire_sound
S.initial_w_class = w_class
@@ -120,6 +121,7 @@
..()
return
to_chat(user, "You unscrew [suppressed] from [src].")
+ playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
user.put_in_hands(suppressed)
fire_sound = S.oldsound
w_class = S.initial_w_class
diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm
index 17a20f82d59..ea52a954a5b 100644
--- a/code/modules/projectiles/guns/projectile/automatic.dm
+++ b/code/modules/projectiles/guns/projectile/automatic.dm
@@ -275,13 +275,27 @@
if(magazine)
overlays.Cut()
overlays += "[magazine.icon_state]"
- return
+ if(istype(magazine, /obj/item/ammo_box/magazine/m12g/XtrLrg))
+ w_class = WEIGHT_CLASS_BULKY
+ else
+ w_class = WEIGHT_CLASS_NORMAL
+ else
+ w_class = WEIGHT_CLASS_NORMAL
/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon()
overlays.Cut()
update_magazine()
icon_state = "bulldog[chambered ? "" : "-e"]"
+/obj/item/gun/projectile/automatic/shotgun/bulldog/attackby(var/obj/item/A as obj, mob/user as mob, params)
+ if(istype(A, /obj/item/ammo_box/magazine/m12g/XtrLrg))
+ if(istype(loc, /obj/item/storage)) // To prevent inventory exploits
+ var/obj/item/storage/Strg = loc
+ if(Strg.max_w_class < WEIGHT_CLASS_BULKY)
+ to_chat(user, "You can't reload [src], with a XL mag, while it's in a normal bag.")
+ return
+ return ..()
+
/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag)
..()
empty_alarm()
diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm
index 17a55722d2c..6c11fda5fd7 100644
--- a/code/modules/projectiles/guns/projectile/shotgun.dm
+++ b/code/modules/projectiles/guns/projectile/shotgun.dm
@@ -197,6 +197,8 @@
..()
post_sawoff()
+/obj/item/gun/projectile/shotgun/riot/buckshot //comes pre-loaded with buckshot rather than rubber
+ mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/buckshot
///////////////////////
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index b843b2d17a2..e813597c4d8 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -543,6 +543,14 @@
build_path = /obj/item/assembly/health
category = list("initial", "Medical")
+/datum/design/stethoscope
+ name = "Stethoscope"
+ id = "stethoscope"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 500)
+ build_path = /obj/item/clothing/accessory/stethoscope
+ category = list("initial", "Medical")
+
/datum/design/timer
name = "Timer"
id = "timer"
diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm
index 826bdbf16aa..054dc2db810 100644
--- a/code/modules/research/designs/comp_board_designs.dm
+++ b/code/modules/research/designs/comp_board_designs.dm
@@ -272,6 +272,16 @@
build_path = /obj/item/circuitboard/solar_control
category = list("Computer Boards")
+/datum/design/sm_monitor
+ name = "Console Board (Supermatter Monitoring)"
+ desc = "Allows for the construction of circuit boards used to build a supermatter monitoring console"
+ id = "sm_monitor"
+ req_tech = list("programming" = 2, "powerstorage" = 2)
+ build_type = IMPRINTER
+ materials = list(MAT_GLASS = 1000)
+ build_path = /obj/item/circuitboard/sm_monitor
+ category = list("Computer Boards")
+
/datum/design/spacepodlocator
name = "Console Board (Spacepod Locator)"
desc = "Allows for the construction of circuit boards used to build a space-pod locating console"
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index a1ea27b0592..75e64a43215 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -751,7 +751,7 @@
category = list("Exosuit Equipment")
/datum/design/mech_mining_scanner
- name = "Exosuit Engineering Equipement (Mining Scanner)"
+ name = "Exosuit Engineering Equipment (Mining Scanner)"
id = "mech_mscanner"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/mecha_equipment/mining_scanner
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index caa273594de..43cc010ac1c 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -291,8 +291,7 @@
for(var/turf/T in oview(1, src))
if(!T.density)
if(prob(EFFECT_PROB_VERYHIGH))
- var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/greenglow(T)
- reagentdecal.reagents.add_reagent("radium", 7)
+ new /obj/effect/decal/cleanable/greenglow(T)
if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
var/savedName = "[exp_on]"
ejectItem(TRUE)
diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm
index 56e7f8f96ba..7d28be5c5cf 100644
--- a/code/modules/spacepods/spacepod.dm
+++ b/code/modules/spacepods/spacepod.dm
@@ -296,7 +296,7 @@
return
var/sound/S = sound(mysound)
S.wait = 0 //No queue
- S.channel = open_sound_channel()
+ S.channel = SSsounds.random_available_channel()
S.volume = 50
for(var/mob/M in passengers | pilot)
M << S
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 426af9a97ac..4e9b5e57630 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -91,7 +91,7 @@
/obj/machinery/bsa/middle/proc/check_completion()
if(!front || !back)
- return "No linked parts detected!"
+ return "No multitool-linked parts detected!"
if(!front.anchored || !back.anchored || !anchored)
return "Linked parts unwrenched!"
if(front.y != y || back.y != y || !(front.x > x && back.x < x || front.x < x && back.x > x) || front.z != z || back.z != z)
@@ -305,51 +305,45 @@
/obj/machinery/computer/bsa_control/attack_hand(mob/user)
if(..())
return 1
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/bsa_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/bsa_control/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "bsa.tmpl", name, 400, 305)
+ ui = new(user, src, ui_key, "BlueSpaceArtilleryControl", name, 400, 155, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/computer/bsa_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/computer/bsa_control/tgui_data(mob/user)
var/list/data = list()
data["connected"] = cannon
data["notice"] = notice
if(target)
data["target"] = get_target_name()
-
if(cannon)
var/reload_cooldown = cannon.reload_cooldown
var/last_fire_time = cannon.last_fire_time
var/time_to_wait = max(0, round(reload_cooldown - ((world.time / 10) - last_fire_time)))
var/minutes = max(0, round(time_to_wait / 60))
var/seconds = max(0, time_to_wait - (60 * minutes))
-
- data["reloadtime_mins"] = minutes
- data["reloadtime_secs"] = (seconds < 10) ? "0[seconds]" : seconds
+ var/seconds2 = (seconds < 10) ? "0[seconds]" : seconds
+ data["reloadtime_text"] = "[minutes]:[seconds2]"
data["ready"] = minutes == 0 && seconds == 0
else
data["ready"] = FALSE
-
return data
-/obj/machinery/computer/bsa_control/Topic(href, href_list)
+/obj/machinery/computer/bsa_control/tgui_act(action, params)
if(..())
- return 1
-
- if(href_list["build"])
- cannon = deploy()
- . = TRUE
- else if(href_list["fire"])
- fire(usr)
- . = TRUE
- else if(href_list["recalibrate"])
- calibrate(usr)
- . = TRUE
+ return
+ switch(action)
+ if("build")
+ cannon = deploy()
+ if("fire")
+ fire(usr)
+ if("recalibrate")
+ calibrate(usr)
update_icon()
+ return TRUE
/obj/machinery/computer/bsa_control/proc/calibrate(mob/user)
var/list/gps_locators = list()
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index db801fa5d0f..033cd9fdf94 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -7,7 +7,7 @@
#define VAULT_NOBREATH "Lung Enhancement"
#define VAULT_FIREPROOF "Thermal Regulation"
#define VAULT_STUNTIME "Neural Repathing"
-#define VAULT_ARMOUR "Bone Reinforcement"
+#define VAULT_ARMOUR "Hardened Skin"
#define VAULT_SPEED "Leg Muscle Stimulus"
#define VAULT_QUICK "Arm Muscle Stimulus"
diff --git a/code/modules/store/items.dm b/code/modules/store/items.dm
index 4c1610474ba..0a0f19fb0f7 100644
--- a/code/modules/store/items.dm
+++ b/code/modules/store/items.dm
@@ -111,6 +111,12 @@
typepath = /obj/item/instrument/eguitar
cost = 500
+/datum/storeitem/banjo
+ name = "Banjo"
+ desc = "It's pretty much just a drum with a neck and strings."
+ typepath = /obj/item/instrument/banjo
+ cost = 500
+
/datum/storeitem/piano_synth
name = "Piano Synthesizer"
desc = "An advanced electronic synthesizer that can emulate various instruments."
diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm
index b0403d8ae49..837c6e0d056 100644
--- a/code/modules/surgery/organs/blood.dm
+++ b/code/modules/surgery/organs/blood.dm
@@ -54,7 +54,7 @@
var/obj/item/organ/external/BP = X
var/brutedamage = BP.brute_dam
- if(BP.is_robotic() && !ismachineperson(src))
+ if(BP.is_robotic())
continue
//We want an accurate reading of .len
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 1b205f957ce..59385ff85a3 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -8,7 +8,7 @@
var/eye_colour = "#000000" // Should never be null
var/list/colourmatrix = null
var/list/colourblind_matrix = MATRIX_GREYSCALE //Special colourblindness parameters. By default, it's black-and-white.
- var/list/replace_colours = LIST_GREYSCALE_REPLACE
+ var/list/replace_colours = GREYSCALE_COLOR_REPLACE
var/dependent_disabilities = list() //Gets set by eye-dependent disabilities such as colourblindness so the eyes can transfer the disability during transplantation.
var/weld_proof = null //If set, the eyes will not take damage during welding. eg. IPC optical sensors do not take damage when they weld things while all other eyes will.
diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm
index 3dab4065f7d..5b9b5056604 100644
--- a/code/modules/surgery/organs/organ.dm
+++ b/code/modules/surgery/organs/organ.dm
@@ -236,35 +236,6 @@
status &= ~ORGAN_SPLINTED
status |= ORGAN_ROBOT
-/obj/item/organ/external/emp_act(severity)
- if(!is_robotic() || emp_proof)
- return
- if(tough)
- switch(severity)
- if(1)
- receive_damage(0, 5.5)
- if(owner)
- owner.Stun(10)
- if(2)
- receive_damage(0, 2.8)
- if(owner)
- owner.Stun(5)
- else
- switch(severity)
- if(1)
- receive_damage(0, 20)
- if(2)
- receive_damage(0, 7)
-
-/obj/item/organ/internal/emp_act(severity)
- if(!is_robotic() || emp_proof)
- return
- switch(severity)
- if(1)
- receive_damage(20, 1)
- if(2)
- receive_damage(7, 1)
-
/obj/item/organ/proc/shock_organ(intensity)
return
diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm
index 143bf1d1ed6..5fdf95f09a9 100644
--- a/code/modules/surgery/organs/organ_external.dm
+++ b/code/modules/surgery/organs/organ_external.dm
@@ -41,6 +41,9 @@
var/list/obj/item/organ/external/children
var/list/convertable_children = list()
+ // Does the organ take reduce damage from EMPs? IPC limbs get this by default
+ var/emp_resistant = FALSE
+
// Internal organs of this body part
var/list/internal_organs = list()
@@ -256,6 +259,34 @@
return update_icon()
+/obj/item/organ/external/emp_act(severity)
+ if(!is_robotic() || emp_proof)
+ return
+ if(tough) // Augmented limbs
+ switch(severity)
+ if(1)
+ receive_damage(0, 5.5)
+ if(owner)
+ owner.Stun(10)
+ if(2)
+ receive_damage(0, 2.8)
+ if(owner)
+ owner.Stun(5)
+ else if(emp_resistant) // IPC limbs
+ switch(severity)
+ if(1)
+ // 5.28 (9 * 0.66 burn_mod) burn damage, 65.34 damage with 11 limbs.
+ receive_damage(0, 9)
+ if(2)
+ // 3.63 (5 * 0.66 burn_mod) burn damage, 39.93 damage with 11 limbs.
+ receive_damage(0, 5.5)
+ else // Basic prosthetic limbs
+ switch(severity)
+ if(1)
+ receive_damage(0, 20)
+ if(2)
+ receive_damage(0, 7)
+
/*
This function completely restores a damaged organ to perfect condition.
*/
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 53283ada95e..5151b347f4d 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -73,6 +73,15 @@
A.Remove(M)
return src
+/obj/item/organ/internal/emp_act(severity)
+ if(!is_robotic() || emp_proof)
+ return
+ switch(severity)
+ if(1)
+ receive_damage(20, 1)
+ if(2)
+ receive_damage(7, 1)
+
/obj/item/organ/internal/replaced(var/mob/living/carbon/human/target)
insert(target)
diff --git a/code/modules/surgery/organs/subtypes/machine.dm b/code/modules/surgery/organs/subtypes/machine.dm
index 52cad684e11..415cdb7875d 100644
--- a/code/modules/surgery/organs/subtypes/machine.dm
+++ b/code/modules/surgery/organs/subtypes/machine.dm
@@ -5,6 +5,7 @@
min_broken_damage = 30
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/head/ipc/New(mob/living/carbon/holder, datum/species/species_override = null)
..(holder, /datum/species/machine) // IPC heads need to be explicitly set to this since you can print them
@@ -13,6 +14,7 @@
/obj/item/organ/external/chest/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/chest/ipc/New()
..()
@@ -21,6 +23,7 @@
/obj/item/organ/external/groin/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/groin/ipc/New()
..()
@@ -29,6 +32,7 @@
/obj/item/organ/external/arm/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/arm/ipc/New()
..()
@@ -37,6 +41,7 @@
/obj/item/organ/external/arm/right/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/arm/right/ipc/New()
..()
@@ -45,6 +50,7 @@
/obj/item/organ/external/leg/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/leg/ipc/New()
..()
@@ -53,6 +59,7 @@
/obj/item/organ/external/leg/right/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/leg/right/ipc/New()
..()
@@ -61,6 +68,7 @@
/obj/item/organ/external/foot/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/foot/ipc/New()
..()
@@ -69,6 +77,7 @@
/obj/item/organ/external/foot/right/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/foot/right/ipc/New()
..()
@@ -77,6 +86,7 @@
/obj/item/organ/external/hand/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/hand/ipc/New()
..()
@@ -85,6 +95,7 @@
/obj/item/organ/external/hand/right/ipc
encased = null
status = ORGAN_ROBOT
+ emp_resistant = TRUE
/obj/item/organ/external/hand/right/ipc/New()
..()
diff --git a/code/modules/surgery/organs/subtypes/tajaran.dm b/code/modules/surgery/organs/subtypes/tajaran.dm
index e8d87b55b89..226bc82dd2d 100644
--- a/code/modules/surgery/organs/subtypes/tajaran.dm
+++ b/code/modules/surgery/organs/subtypes/tajaran.dm
@@ -7,14 +7,14 @@
icon = 'icons/obj/species_organs/tajaran.dmi'
name = "tajaran eyeballs"
colourblind_matrix = MATRIX_TAJ_CBLIND //The colour matrix and darksight parameters that the mob will recieve when they get the disability.
- replace_colours = LIST_TAJ_REPLACE
+ replace_colours = TRITANOPIA_COLOR_REPLACE
see_in_dark = 8
/obj/item/organ/internal/eyes/tajaran/farwa //Being the lesser form of Tajara, Farwas have an utterly incurable version of their colourblindness.
name = "farwa eyeballs"
colourmatrix = MATRIX_TAJ_CBLIND
see_in_dark = 8
- replace_colours = LIST_TAJ_REPLACE
+ replace_colours = TRITANOPIA_COLOR_REPLACE
/obj/item/organ/internal/heart/tajaran
name = "tajaran heart"
diff --git a/code/modules/surgery/organs/subtypes/vulpkanin.dm b/code/modules/surgery/organs/subtypes/vulpkanin.dm
index a2815d32dc6..bd56532ebd9 100644
--- a/code/modules/surgery/organs/subtypes/vulpkanin.dm
+++ b/code/modules/surgery/organs/subtypes/vulpkanin.dm
@@ -7,14 +7,14 @@
name = "vulpkanin eyeballs"
icon = 'icons/obj/species_organs/vulpkanin.dmi'
colourblind_matrix = MATRIX_VULP_CBLIND //The colour matrix and darksight parameters that the mob will recieve when they get the disability.
- replace_colours = LIST_VULP_REPLACE
+ replace_colours = PROTANOPIA_COLOR_REPLACE
see_in_dark = 8
/obj/item/organ/internal/eyes/vulpkanin/wolpin //Being the lesser form of Vulpkanin, Wolpins have an utterly incurable version of their colourblindness.
name = "wolpin eyeballs"
colourmatrix = MATRIX_VULP_CBLIND
see_in_dark = 8
- replace_colours = LIST_VULP_REPLACE
+ replace_colours = PROTANOPIA_COLOR_REPLACE
/obj/item/organ/internal/heart/vulpkanin
name = "vulpkanin heart"
diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css
index 8c177840a1b..529827ad369 100644
--- a/goon/browserassets/css/browserOutput-dark.css
+++ b/goon/browserassets/css/browserOutput-dark.css
@@ -401,6 +401,10 @@ h1.alert, h2.alert {color: #FFF;}
.connectionClosed.restored {background: green;}
.internal.boldnshit {color: #6685f5; font-weight: bold;}
+.rebooting {background: #2979AF; color: white; padding: 5px;}
+.rebooting a {color: white !important; text-decoration-color: white !important;}
+#reconnectTimer {font-weight: bold;}
+
/* HELPER CLASSES */
.text-normal {font-weight: normal; font-style: normal;}
.hidden {display: none; visibility: hidden;}
diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css
index 0d7d987b0e7..31da7aa96f5 100644
--- a/goon/browserassets/css/browserOutput.css
+++ b/goon/browserassets/css/browserOutput.css
@@ -400,6 +400,10 @@ h1.alert, h2.alert {color: #000000;}
.connectionClosed.restored {background: green;}
.internal.boldnshit {color: blue; font-weight: bold;}
+.rebooting {background: #2979AF; color: white; padding: 5px;}
+.rebooting a {color: white !important; text-decoration-color: white !important;}
+#reconnectTimer {font-weight: bold;}
+
/* HELPER CLASSES */
.text-normal {font-weight: normal; font-style: normal;}
.hidden {display: none; visibility: hidden;}
diff --git a/goon/browserassets/js/browserOutput.js b/goon/browserassets/js/browserOutput.js
index 3cac81ca8e2..414a5d644b6 100644
--- a/goon/browserassets/js/browserOutput.js
+++ b/goon/browserassets/js/browserOutput.js
@@ -69,10 +69,13 @@ var opts = {
'macros': {},
// Emoji toggle
- 'enableEmoji': true
+ 'enableEmoji': true,
+
+ // Reboot message stuff
+ 'rebootIntervalHandler': null
};
-var regexHasError = false; //variable to check if regex has excepted
+var regexHasError = false; //variable to check if regex has excepted
function outerHTML(el) {
var wrap = document.createElement('div');
@@ -97,10 +100,10 @@ if (typeof String.prototype.trim !== 'function') {
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
-
+
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
- }
+ }
if (start === undefined) { start = 0; }
return this.indexOf(search, start) !== -1;
};
@@ -124,7 +127,7 @@ function byondDecode(message) {
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
// Marvelous.
message = message.replace(/\+/g, "%20");
- try {
+ try {
// This is a workaround for the above not always working when BYOND's shitty url encoding breaks.
// Basically, sometimes BYOND's double encoding trick just arbitrarily produces something that makes decodeURIComponent
// throw an "Invalid Encoding URI" URIError... the simplest way to work around this is to just ignore it and use unescape instead
@@ -217,10 +220,10 @@ function highlightTerms(el) {
ind = next_tag;
}
}
-
+
element.innerHTML = s;
}
-
+
for (var i = 0; i < opts.highlightTerms.length; i++) { //Each highlight term
if(opts.highlightTerms[i]) {
if(!opts.highlightRegexEnable){
@@ -573,6 +576,34 @@ function toggleWasd(state) {
opts.wasd = (state == 'on' ? true : false);
}
+function reboot(timeRaw) {
+ var timeLeftSecs = parseInt(timeRaw);
+ const intervalSecs = 1; // tick every 1 second
+
+ rebootFinished();
+ internalOutput('', 'internal');
+
+ opts.rebootIntervalHandler = setInterval(function() {
+ timeLeftSecs -= intervalSecs;
+ if (timeLeftSecs <= 0) {
+ $("#reconnectTimer").text('Reconnecting...');
+ window.location.href = 'byond://winset?command=.reconnect';
+ clearInterval(opts.rebootIntervalHandler)
+ opts.rebootIntervalHandler = null;
+ } else {
+ $("#reconnectTimer").text('Reconnect (' + timeLeftSecs + ')');
+ }
+ }, intervalSecs * 1000);
+}
+
+function rebootFinished() {
+ if (opts.rebootIntervalHandler != null) {
+ clearInterval(opts.rebootIntervalHandler)
+ }
+ $(" Reconnected automatically!").insertBefore("#reconnectTimer");
+ $("#reconnectTimer").remove();
+}
+
/*****************************************
*
* MAKE MACRO DICTIONARY
@@ -652,7 +683,7 @@ $(function() {
'shideSpam': getCookie('hidespam'),
'darkChat': getCookie('darkChat'),
};
-
+
if (savedConfig.sfontSize) {
$messages.css('font-size', savedConfig.sfontSize);
internalOutput('Loaded font size setting of: '+savedConfig.sfontSize+'', 'internal');
@@ -1010,18 +1041,18 @@ $(function() {
} else {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
-
+
// synchronous requests are depricated in modern browsers
- xmlHttp.open('GET', 'browserOutput.css', true);
+ xmlHttp.open('GET', 'browserOutput.css', true);
xmlHttp.onload = function (e) {
if (xmlHttp.status === 200) { // request successful
-
+
// Generate Log
var saved = '';
saved += $messages.html();
saved = saved.replace(/&/g, '&');
saved = saved.replace(/Chat Log \
'+
@@ -1031,7 +1062,7 @@ $(function() {
openWindow('Style Doc Retrieve Error: '+xmlHttp.statusText);
}
}
-
+
// timeout and request errors
xmlHttp.timeout = 300;
xmlHttp.ontimeout = function (e) {
diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm
index 74c9d844728..b073bece409 100644
--- a/goon/code/datums/browserOutput.dm
+++ b/goon/code/datums/browserOutput.dm
@@ -109,6 +109,7 @@ var/list/chatResources = list(
loaded = TRUE
winset(owner, "browseroutput", "is-disabled=false")
+ owner << output(null, "browseroutput:rebootFinished")
if(owner.holder)
loadAdmin()
for(var/message in messageQueue)
@@ -245,8 +246,8 @@ var/list/chatResources = list(
var/to_chat_filename
var/to_chat_line
var/to_chat_src
-// Call using macro: to_chat(target, message, flag)
-/proc/to_chat_immediate(target, message, flag)
+
+/proc/to_chat(target, message, flag)
if(!is_valid_tochat_message(message) || !is_valid_tochat_target(target))
target << message
@@ -304,17 +305,4 @@ var/to_chat_src
target << output(output_message, "browseroutput:output")
-/proc/to_chat(target, message, flag)
- /*
- If any of the following conditions are met, do NOT use SSchat. These conditions include:
- - Is the MC still initializing?
- - Has SSchat initialized?
- - Has SSchat been offlined due to MC crashes?
- If any of these are met, use the old chat system, otherwise people wont see messages
- */
- if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized || SSchat?.flags & SS_NO_FIRE)
- to_chat_immediate(target, message, flag)
- return
- SSchat.queue(target, message, flag)
-
#undef MAX_COOKIE_LENGTH
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index 5200ad72e5a..1bd516110e5 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/mob/inhands/clothing_lefthand.dmi b/icons/mob/inhands/clothing_lefthand.dmi
index 589135b8701..379c727930f 100644
Binary files a/icons/mob/inhands/clothing_lefthand.dmi and b/icons/mob/inhands/clothing_lefthand.dmi differ
diff --git a/icons/mob/inhands/clothing_righthand.dmi b/icons/mob/inhands/clothing_righthand.dmi
index 8fd5f7d71b8..2fc6aa9c4c1 100644
Binary files a/icons/mob/inhands/clothing_righthand.dmi and b/icons/mob/inhands/clothing_righthand.dmi differ
diff --git a/icons/mob/inhands/equipment/instruments_lefthand.dmi b/icons/mob/inhands/equipment/instruments_lefthand.dmi
index 225f1768d99..aa3608fec28 100644
Binary files a/icons/mob/inhands/equipment/instruments_lefthand.dmi and b/icons/mob/inhands/equipment/instruments_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/instruments_righthand.dmi b/icons/mob/inhands/equipment/instruments_righthand.dmi
index cc1df97183a..28085ff746d 100644
Binary files a/icons/mob/inhands/equipment/instruments_righthand.dmi and b/icons/mob/inhands/equipment/instruments_righthand.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index a378d2e5b04..b2f1bfa90c6 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 0a8847d7e4a..a7772c3ebf4 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/species/drask/head.dmi b/icons/mob/species/drask/head.dmi
index b9f0604c960..f0c888e9f68 100644
Binary files a/icons/mob/species/drask/head.dmi and b/icons/mob/species/drask/head.dmi differ
diff --git a/icons/mob/species/drask/suit.dmi b/icons/mob/species/drask/suit.dmi
index d75a28f2bee..cef6dbdf836 100644
Binary files a/icons/mob/species/drask/suit.dmi and b/icons/mob/species/drask/suit.dmi differ
diff --git a/icons/mob/species/grey/head.dmi b/icons/mob/species/grey/head.dmi
index 4ff783bbb74..c059a3bff9d 100644
Binary files a/icons/mob/species/grey/head.dmi and b/icons/mob/species/grey/head.dmi differ
diff --git a/icons/mob/species/grey/mask.dmi b/icons/mob/species/grey/mask.dmi
index cbbd722ae69..348a39c0f03 100644
Binary files a/icons/mob/species/grey/mask.dmi and b/icons/mob/species/grey/mask.dmi differ
diff --git a/icons/mob/species/grey/suit.dmi b/icons/mob/species/grey/suit.dmi
index 3070f6627ea..c22c9daf3dd 100644
Binary files a/icons/mob/species/grey/suit.dmi and b/icons/mob/species/grey/suit.dmi differ
diff --git a/icons/mob/species/grey/uniform.dmi b/icons/mob/species/grey/uniform.dmi
index c956187ffa1..7a74f930598 100644
Binary files a/icons/mob/species/grey/uniform.dmi and b/icons/mob/species/grey/uniform.dmi differ
diff --git a/icons/mob/species/skrell/helmet.dmi b/icons/mob/species/skrell/helmet.dmi
index ef05daf5115..e54cdb52d0f 100644
Binary files a/icons/mob/species/skrell/helmet.dmi and b/icons/mob/species/skrell/helmet.dmi differ
diff --git a/icons/mob/species/vox/head.dmi b/icons/mob/species/vox/head.dmi
index 6c4b2b43f6a..fb5d153d30d 100644
Binary files a/icons/mob/species/vox/head.dmi and b/icons/mob/species/vox/head.dmi differ
diff --git a/icons/mob/species/vox/suit.dmi b/icons/mob/species/vox/suit.dmi
index 827b67b4332..4161227ac79 100644
Binary files a/icons/mob/species/vox/suit.dmi and b/icons/mob/species/vox/suit.dmi differ
diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi
index 26c2a7ea734..12a0a371a94 100644
Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ
diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi
index 4a250a1fffc..b1876a7d063 100644
Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ
diff --git a/icons/obj/atmospherics/blue_pipe_tank.dmi b/icons/obj/atmospherics/blue_pipe_tank.dmi
deleted file mode 100644
index 3d2ee4c9d69..00000000000
Binary files a/icons/obj/atmospherics/blue_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/digital_valve.dmi b/icons/obj/atmospherics/digital_valve.dmi
deleted file mode 100644
index 136523b7223..00000000000
Binary files a/icons/obj/atmospherics/digital_valve.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/dp_vent_pump.dmi b/icons/obj/atmospherics/dp_vent_pump.dmi
deleted file mode 100644
index 86dae2cf435..00000000000
Binary files a/icons/obj/atmospherics/dp_vent_pump.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/filter.dmi b/icons/obj/atmospherics/filter.dmi
deleted file mode 100644
index 99afb55d22b..00000000000
Binary files a/icons/obj/atmospherics/filter.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/mainspipe.dmi b/icons/obj/atmospherics/mainspipe.dmi
deleted file mode 100644
index df6c2bc0d3a..00000000000
Binary files a/icons/obj/atmospherics/mainspipe.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/n2o_pipe_tank.dmi b/icons/obj/atmospherics/n2o_pipe_tank.dmi
deleted file mode 100644
index 40efbd9f15d..00000000000
Binary files a/icons/obj/atmospherics/n2o_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/omni_devices.dmi b/icons/obj/atmospherics/omni_devices.dmi
deleted file mode 100644
index 44497310026..00000000000
Binary files a/icons/obj/atmospherics/omni_devices.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/orange_pipe_tank.dmi b/icons/obj/atmospherics/orange_pipe_tank.dmi
deleted file mode 100644
index cc9442b8033..00000000000
Binary files a/icons/obj/atmospherics/orange_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/passive_gate.dmi b/icons/obj/atmospherics/passive_gate.dmi
deleted file mode 100644
index 42e8c9dc74f..00000000000
Binary files a/icons/obj/atmospherics/passive_gate.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pipe_manifold.dmi b/icons/obj/atmospherics/pipe_manifold.dmi
deleted file mode 100644
index 1e6f5750d1b..00000000000
Binary files a/icons/obj/atmospherics/pipe_manifold.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pipe_tank.dmi b/icons/obj/atmospherics/pipe_tank.dmi
deleted file mode 100644
index 72310a2657d..00000000000
Binary files a/icons/obj/atmospherics/pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pipe_vent.dmi b/icons/obj/atmospherics/pipe_vent.dmi
deleted file mode 100644
index e40f5946cf8..00000000000
Binary files a/icons/obj/atmospherics/pipe_vent.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/portables_connector.dmi b/icons/obj/atmospherics/portables_connector.dmi
deleted file mode 100644
index c651b959990..00000000000
Binary files a/icons/obj/atmospherics/portables_connector.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pump.dmi b/icons/obj/atmospherics/pump.dmi
deleted file mode 100644
index e44a21991ba..00000000000
Binary files a/icons/obj/atmospherics/pump.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/red_orange_pipe_tank.dmi b/icons/obj/atmospherics/red_orange_pipe_tank.dmi
deleted file mode 100644
index 1770c46312a..00000000000
Binary files a/icons/obj/atmospherics/red_orange_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/red_pipe_tank.dmi b/icons/obj/atmospherics/red_pipe_tank.dmi
deleted file mode 100644
index 3fedc659e03..00000000000
Binary files a/icons/obj/atmospherics/red_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/relief_valve.dmi b/icons/obj/atmospherics/relief_valve.dmi
deleted file mode 100644
index 485bf792b19..00000000000
Binary files a/icons/obj/atmospherics/relief_valve.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/valve.dmi b/icons/obj/atmospherics/valve.dmi
deleted file mode 100644
index 9ce45199bd7..00000000000
Binary files a/icons/obj/atmospherics/valve.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/vent_pump.dmi b/icons/obj/atmospherics/vent_pump.dmi
deleted file mode 100644
index 6b1f57baa44..00000000000
Binary files a/icons/obj/atmospherics/vent_pump.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/vent_scrubber.dmi b/icons/obj/atmospherics/vent_scrubber.dmi
deleted file mode 100644
index 7133072cd79..00000000000
Binary files a/icons/obj/atmospherics/vent_scrubber.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/volume_pump.dmi b/icons/obj/atmospherics/volume_pump.dmi
deleted file mode 100644
index e3ff3cf9854..00000000000
Binary files a/icons/obj/atmospherics/volume_pump.dmi and /dev/null differ
diff --git a/icons/obj/cigarettes.dmi b/icons/obj/cigarettes.dmi
index a0133e5b383..97710ac4805 100644
Binary files a/icons/obj/cigarettes.dmi and b/icons/obj/cigarettes.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index c9ebceba369..30f34f01b4f 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index acb6af50d18..62fc0a6120c 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi
index ac416274ebe..042b5fe7f38 100644
Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ
diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi
index 7351c8c7483..35ce076db6c 100644
Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ
diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi
index f1d09247bbd..1e61cebb799 100644
Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ
diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi
index a82f3fa10a2..e30f3e44392 100644
Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ
diff --git a/icons/obj/lighting.dmi b/icons/obj/lighting.dmi
index f8943c2c39b..49b594c1656 100644
Binary files a/icons/obj/lighting.dmi and b/icons/obj/lighting.dmi differ
diff --git a/icons/obj/musician.dmi b/icons/obj/musician.dmi
index 21078c58969..d55ecb7ebc0 100644
Binary files a/icons/obj/musician.dmi and b/icons/obj/musician.dmi differ
diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi
index 60eb897b2b1..51e9b66ee30 100644
Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ
diff --git a/icons/obj/recycling.dmi b/icons/obj/recycling.dmi
index 2a47e6817bb..c30eb425dda 100644
Binary files a/icons/obj/recycling.dmi and b/icons/obj/recycling.dmi differ
diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi
index 1ef7016307d..c51d2903b14 100644
Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ
diff --git a/nano/templates/alarm_monitor.tmpl b/nano/templates/alarm_monitor.tmpl
deleted file mode 100644
index 5dae051a576..00000000000
--- a/nano/templates/alarm_monitor.tmpl
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-{{for data.categories}}
- {{:value.category}}
- {{for value.alarms :alarmValue:alarmIndex}}
- {{if alarmValue.origin_lost}}
- {{:alarmValue.name}} Alarm Origin Lost
- {{else}}
- {{:alarmValue.name}}
- {{/if}}
- {{if alarmValue.has_cameras || alarmValue.lost_sources != ""}}
-
- {{if alarmValue.has_cameras}}
-
- {{for alarmValue.cameras :cameraValue:cameraIndex}}
- {{if cameraValue.deact}}
- {{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}}
- {{else}}
- {{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}}
- {{/if}}
- {{/for}}
-
- {{else}}
- No cameras found.
- {{/if}}
- {{if alarmValue.lost_sources != ""}}
-
- Lost Alarm Sources: {{:alarmValue.lost_sources}}
-
- {{/if}}
-
- {{/if}}
- {{empty}}
- --All Systems Nominal
- {{/for}}
-{{/for}}
\ No newline at end of file
diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl
deleted file mode 100644
index 234074fe577..00000000000
--- a/nano/templates/apc.tmpl
+++ /dev/null
@@ -1,197 +0,0 @@
-
- {{if data.siliconUser}}
-
- Interface Lock:
-
-
- {{:helper.link('Engaged', 'lock', {'toggleaccess' : 1}, data.siliconLock ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'toggleaccess' : 1}, data.malfStatus >= 2 ? 'linkOff' : (data.siliconLock ? null : 'selected'))}}
-
-
- {{else}}
- {{if data.locked}}
- Swipe an ID card to unlock this interface
- {{else}}
- Swipe an ID card to lock this interface
- {{/if}}
- {{/if}}
-
-
-
-
- Power Status
-
-
-
- Main Breaker:
-
-
- {{if data.locked && !data.siliconUser}}
- {{if data.isOperating}}
- On
- {{else}}
- Off
- {{/if}}
- {{else}}
- {{:helper.link('On', 'power-off', {'breaker' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'breaker' : 1}, data.isOperating ? null : 'selected')}}
- {{/if}}
-
-
-
-
-
- External Power:
-
-
- {{if data.externalPower == 2}}
- Good
- {{else data.externalPower == 1}}
- Low
- {{else}}
- None
- {{/if}}
-
-
-
-
-
- Power Cell:
-
- {{if data.powerCellStatus == null}}
-
- Power cell removed.
-
- {{else}}
-
- {{:helper.displayBar(data.powerCellStatus, 0, 100, (data.powerCellStatus >= 50) ? 'good' : (data.powerCellStatus >= 25) ? 'average' : 'bad')}}
-
- {{:helper.smoothRound(data.powerCellStatus, 1)}}%
-
- {{/if}}
-
-
- {{if data.powerCellStatus != null}}
-
-
- Charge Mode:
-
-
- {{if data.locked && !data.siliconUser}}
- {{if data.chargeMode}}
- Auto
- {{else}}
- Off
- {{/if}}
- {{else}}
- {{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}}
- {{/if}}
-
- {{if data.chargingStatus > 1}}
- [Fully Charged]
- {{else data.chargingStatus == 1}}
- [Charging]
- {{else}}
- [Not Charging]
- {{/if}}
-
-
- {{/if}}
-
-
- Power Channels
-
- {{for data.powerChannels}}
-
-
- {{:value.title}}:
-
-
- {{:helper.smoothRound(value.powerLoad)}} W
-
-
-
- {{if value.status <= 1}}
- Off
- {{else value.status >= 2}}
- On
- {{/if}}
- {{if data.locked}}
- {{if value.status == 1 || value.status == 3}}
- Auto
- {{else}}
- Manual
- {{/if}}
- {{/if}}
-
- {{if !data.locked || data.siliconUser}}
-
- {{:helper.link('Auto', 'refresh', value.topicParams.auto, (value.status == 1 || value.status == 3) ? 'selected' : null)}}
- {{:helper.link('On', 'power-off', value.topicParams.on, (value.status == 2) ? 'selected' : null)}}
- {{:helper.link('Off', 'close', value.topicParams.off, (value.status == 0) ? 'selected' : null)}}
-
- {{/if}}
-
- {{/for}}
-
-
-
- Total Load:
-
-
- {{:helper.smoothRound(data.totalLoad)}} W
-
-
-
-
-
-
-
- Cover Lock:
-
-
- {{if data.locked && !data.siliconUser}}
- {{if data.coverLocked}}
- Engaged
- {{else}}
- Disengaged
- {{/if}}
- {{else}}
- {{:helper.link('Engaged', 'lock', {'lock' : 1}, data.coverLocked ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, data.coverLocked ? null : 'selected')}}
- {{/if}}
-
-
-
- {{if data.siliconUser}}
- System Overrides
-
-
- {{:helper.link('Overload Lighting Circuit', 'lightbulb-o', {'overload' : 1})}}
- {{if data.malfStatus == 1}}
- {{:helper.link('Override Programming', 'file-text', {'malfhack' : 1})}}
- {{else data.malfStatus == 2}}
- {{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1})}}
- {{else data.malfStatus == 3}}
- {{:helper.link('Return to Main Core', 'arrow-left', {'deoccupyapc' : 1})}}
- {{else data.malfStatus == 4}}
- {{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1}, 'linkOff')}}
- {{/if}}
-
- {{/if}}
-
-
-
- Night Shift Lighting:
-
-
- {{if data.locked}}
- {{if data.nightshiftLights}}
- On
- {{else}}
- Off
- {{/if}}
- {{else}}
- {{:helper.link('Enabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? 'selected' : null)}}{{:helper.link('Disabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? null : 'selected')}}
- {{/if}}
-
-
-
-
diff --git a/nano/templates/atmos_alert.tmpl b/nano/templates/atmos_alert.tmpl
deleted file mode 100644
index bdb6c2c872b..00000000000
--- a/nano/templates/atmos_alert.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-Priority Alerts
-{{for data.priority_alarms}}
-
- {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}}
-
-{{empty}}
- No priority alerts detected.
-{{/for}}
-
-Minor Alerts
-{{for data.minor_alarms}}
-
- {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}}
-
-{{empty}}
- No minor alerts detected.
-{{/for}}
-
diff --git a/nano/templates/bsa.tmpl b/nano/templates/bsa.tmpl
deleted file mode 100644
index a0e8a60deb5..00000000000
--- a/nano/templates/bsa.tmpl
+++ /dev/null
@@ -1,51 +0,0 @@
-
-{{if data.notice}}
- {{:data.notice}}
-{{/if}}
-Bluespace Artillery Control
-
-
- Target:
-
-
- {{:helper.link(data.target ? data.target : "None", 'crosshairs', {'recalibrate' : 1})}}
-
-
-
-
- Reload Cooldown:
-
-
- {{if data.ready}}
- Ready
- {{else data.reloadtime_mins || data.reloadtime_secs}}
- {{:data.reloadtime_mins}}:{{:data.reloadtime_secs}}
- {{else}}
- No cannon connected!
- {{/if}}
-
-
-
-
- Controls:
-
-
- {{:helper.link('FIRE!', 'warning', {'fire' : 1}, data.ready && data.target ? null : 'disabled')}}
-
-
-{{if !data.connected}}
-
-
- Maintenance:
-
-
- {{:helper.link('Complete Deployment', 'wrench', {'build' : 1})}}
-
-
-{{else}}
-
-Deployment of weapon authorized by Nanotrasen Naval Command. Remember, friendly fire is grounds for termination of your contract and life.
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/door_control.tmpl b/nano/templates/door_control.tmpl
deleted file mode 100644
index 3c8dc66afb7..00000000000
--- a/nano/templates/door_control.tmpl
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
- Main power is
- {{if data.main_power_loss == 0}}
- online
- {{else data.main_power_loss == -1}}
- offline
- {{else}}
- offline for {{:data.main_power_loss}} second{{:data.main_power_loss == 1 ? '' : 's'}}
- {{/if}}.
-
-
- {{:helper.link(data.main_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'main_power'}, data.main_power_loss == 0 ? null : 'disabled', data.main_power_loss == 0 ? 'redButton' : null)}}
-
-
-
-
-
- Backup power is
- {{if data.backup_power_loss == 0}}
- online
- {{else data.backup_power_loss == -1}}
- offline
- {{else}}
- offline for {{:data.backup_power_loss}} second{{:data.backup_power_loss == 1 ? '' : 's'}}
- {{/if}}.
-
-
- {{:helper.link(data.backup_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'backup_power'}, data.backup_power_loss == 0 ? null : 'disabled', data.backup_power_loss == 0 ? 'redButton' : null)}}
-
-
-
-
-
- Electrified Status:
-
-
- {{:helper.link('Offline' , null, {'command' : 'electrify_permanently', 'activate' : "0" }, data.electrified == 0 ? 'selected' : null)}}
- {{:helper.link(data.electrified <= 0 ? 'Temporary (30s)' : 'Temporary (' + data.electrified +'s)', null, {'command' : 'electrify_temporary', 'activate' : "1"}, null, data.electrified > 0 ? 'redButton' : null)}}
- {{:helper.link('Permanent', null, {'command' : 'electrify_permanently', 'activate' : "1"}, data.electrified == -1 ? 'redButton' : null)}}
-
-
-
-
-
-
- {{for data.commands}}
-
- | {{:value.name}}: |
- {{:helper.link(value.enabled, null, {'command' : value.command, 'activate' : value.act ? 1 : 0}, value.active ? 'selected' : null)}} |
- {{:helper.link(value.disabled, null, {'command' : value.command, 'activate' : value.act ? 0 : 1}, !value.active ? (value.danger ? 'redButton' : 'selected') : null)}} |
-
- {{/for}}
-
diff --git a/nano/templates/slotmachine.tmpl b/nano/templates/slotmachine.tmpl
deleted file mode 100644
index 29545450a14..00000000000
--- a/nano/templates/slotmachine.tmpl
+++ /dev/null
@@ -1,34 +0,0 @@
-
-{{if data.money != null}}
-
- {{:data.plays}} players have tried their luck today!
-
-
- Credits Remaining:
- {{:helper.string(" {1} ", data.money >= 10 ? "" : "bad", helper.smoothRound(data.money))}}
-
-
-
- Ten credits to play!
-
-
- {{:helper.link('SPIN!', 'refresh', {'ops' : 1}, data.money >= 10 && !data.working ? null : 'disabled')}}
-
-
- {{if data.result}}
-
- {{:data.result}}
-
- {{/if}}
- {{if data.working}}
- Spinning!
- {{/if}}
-{{else}}
-
- Could not scan your card or could not find account!
- Please wear or hold your ID and try again.
-
-{{/if}}
diff --git a/nano/templates/song.tmpl b/nano/templates/song.tmpl
deleted file mode 100644
index 28ccae0c923..00000000000
--- a/nano/templates/song.tmpl
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
- Playback
-
- {{:helper.link('New', 'file', {'newsong': 1})}}
- {{:helper.link('Import', 'folder-open', {'import': 1})}}
- {{:helper.link('Play', 'play', {'play': 1}, data.lines.length > 0 ? (data.playing ? 'selected' : null) : 'disabled')}}
- {{:helper.link('Stop', 'stop', {'stop': 1}, !data.playing ? 'selected' : null)}}
-
-
-
- Repeat Song:
- {{:helper.link('-', null, {'repeat' : -10}, data.repeat > 0 && !data.playing ? null : 'disabled')}}
- {{:helper.link('-', null, {'repeat' : -1}, data.repeat > 0 && !data.playing ? null : 'disabled')}}
- {{:data.repeat}}
- {{:helper.link('+', null, {'repeat' : 1}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}}
- {{:helper.link('+', null, {'repeat' : 10}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}}
-
-
- Tempo:
- {{:helper.link('-', null, {'tempo' : 10}, data.tempo < data.maxTempo ? null : 'disabled')}}
- {{:helper.link('-', null, {'tempo' : 1}, data.tempo < data.maxTempo ? null : 'disabled')}}
- {{:helper.round(600 / data.tempo)}} BPM
- {{:helper.link('+', null, {'tempo' : -1}, data.tempo > data.minTempo ? null : 'disabled')}}
- {{:helper.link('+', null, {'tempo' : -10}, data.tempo > data.minTempo ? null : 'disabled')}}
-
-
-
-Editor
-
- {{for data.lines}}
-
- {{:index + 1}}:
-
- {{:helper.link('Edit', 'pencil', {'modifyline': index+1}, data.playing ? 'disabled' : null)}}
- {{:helper.link('Insert', 'arrow-up', {'insertline': index+1}, data.playing ? 'disabled' : null)}}
- {{:helper.link('Delete', 'trash', {'deleteline': index+1}, data.playing ? 'disabled' : null)}}
-
- {{:value}}
-
- {{/for}}
-
-
- {{:helper.link('Add Line', 'arrow-down', {'insertline': data.lines.length + 1}, data.playing ? 'disabled' : null)}}
-
-
-
-
-
-
- {{:helper.link('Help', data.help ? 'close' : 'question', {'help': 1})}}
-
-
- {{if data.help}}
-
- Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-).
-
- Every note in a chord will play together, with the chord timed by the tempo as defined above.
-
-
- Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
-
- By default, every note is natural and in octave 3. Defining a different state for either is remembered for each note.
-
- - Example: C,D,E,F,G,A,B will play a C major scale.
- - After a note has an accidental or octave placed, it will be remembered: C,C4,C#,C3 is C3,C4,C4#,C3#
-
-
-
- Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B.
- A pause may be denoted by an empty chord: C,E,,C,G.
-
- To make a chord be a different time, end it with /x, where the chord length will be length defined by tempo / x, eg: C,G/2,E/4.
-
-
- Combined, an example line is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4.
-
- - Lines may be up to 50 characters.
- - A song may only contain up to 50 lines.
-
-
- {{/if}}
-
-
\ No newline at end of file
diff --git a/nano/templates/wires.tmpl b/nano/templates/wires.tmpl
deleted file mode 100644
index 2702d97d7dc..00000000000
--- a/nano/templates/wires.tmpl
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- {{for data.wires}}
-
- | {{:value.colour_name}} {{if value.index}}({{:value.index}}){{/if}} |
-
- {{:helper.link(value.cut ? 'Mend' : 'Cut', null, {'action' : 'cut', 'wire' : value.colour})}}
- {{:helper.link('Pulse', null, {'action' : 'pulse', 'wire' : value.colour})}}
- {{:helper.link(value.attached ? 'Detach' : 'Attach', null, {'action' : 'attach', 'wire' : value.colour})}}
- |
-
- {{/for}}
-
-
-{{if data.status_len}}
-
-
-
- {{for data.status}}
- {{:value}}
- {{/for}}
-
-
-{{/if}}
diff --git a/paradise.dme b/paradise.dme
index 6b97e59a0b5..6e1235bfc86 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -40,6 +40,7 @@
#include "code\__DEFINES\genetics.dm"
#include "code\__DEFINES\hud.dm"
#include "code\__DEFINES\hydroponics.dm"
+#include "code\__DEFINES\instruments.dm"
#include "code\__DEFINES\inventory.dm"
#include "code\__DEFINES\is_helpers.dm"
#include "code\__DEFINES\job.dm"
@@ -75,6 +76,7 @@
#include "code\__DEFINES\tools.dm"
#include "code\__DEFINES\typeids.dm"
#include "code\__DEFINES\vv.dm"
+#include "code\__DEFINES\wires.dm"
#include "code\__DEFINES\zlevel.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
@@ -216,7 +218,6 @@
#include "code\controllers\subsystem\assets.dm"
#include "code\controllers\subsystem\atoms.dm"
#include "code\controllers\subsystem\changelog.dm"
-#include "code\controllers\subsystem\chat.dm"
#include "code\controllers\subsystem\events.dm"
#include "code\controllers\subsystem\fires.dm"
#include "code\controllers\subsystem\garbage.dm"
@@ -240,6 +241,7 @@
#include "code\controllers\subsystem\parallax.dm"
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\shuttles.dm"
+#include "code\controllers\subsystem\sounds.dm"
#include "code\controllers\subsystem\spacedrift.dm"
#include "code\controllers\subsystem\statistics.dm"
#include "code\controllers\subsystem\sun.dm"
@@ -252,6 +254,7 @@
#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\processing\dcs.dm"
#include "code\controllers\subsystem\processing\fastprocess.dm"
+#include "code\controllers\subsystem\processing\instruments.dm"
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\processing.dm"
#include "code\controllers\subsystem\tickets\mentor_tickets.dm"
@@ -306,6 +309,7 @@
#include "code\datums\components\paintable.dm"
#include "code\datums\components\slippery.dm"
#include "code\datums\components\spawner.dm"
+#include "code\datums\components\spooky.dm"
#include "code\datums\components\squeak.dm"
#include "code\datums\components\swarming.dm"
#include "code\datums\diseases\_disease.dm"
@@ -730,6 +734,7 @@
#include "code\game\machinery\computer\salvage_ship.dm"
#include "code\game\machinery\computer\security.dm"
#include "code\game\machinery\computer\skills.dm"
+#include "code\game\machinery\computer\sm_monitor.dm"
#include "code\game\machinery\computer\specops_shuttle.dm"
#include "code\game\machinery\computer\station_alert.dm"
#include "code\game\machinery\computer\store.dm"
@@ -879,7 +884,6 @@
#include "code\game\objects\items\devices\flashlight.dm"
#include "code\game\objects\items\devices\floor_painter.dm"
#include "code\game\objects\items\devices\handheld_defib.dm"
-#include "code\game\objects\items\devices\instruments.dm"
#include "code\game\objects\items\devices\laserpointer.dm"
#include "code\game\objects\items\devices\lightreplacer.dm"
#include "code\game\objects\items\devices\machineprototype.dm"
@@ -1084,7 +1088,6 @@
#include "code\game\objects\structures\misc.dm"
#include "code\game\objects\structures\mop_bucket.dm"
#include "code\game\objects\structures\morgue.dm"
-#include "code\game\objects\structures\musician.dm"
#include "code\game\objects\structures\noticeboard.dm"
#include "code\game\objects\structures\plasticflaps.dm"
#include "code\game\objects\structures\reflector.dm"
@@ -1244,14 +1247,6 @@
#include "code\modules\admin\verbs\SDQL2\SDQL_2.dm"
#include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm"
#include "code\modules\admin\verbs\SDQL2\useful_procs.dm"
-#include "code\modules\alarm\alarm.dm"
-#include "code\modules\alarm\alarm_handler.dm"
-#include "code\modules\alarm\atmosphere_alarm.dm"
-#include "code\modules\alarm\burglar_alarm.dm"
-#include "code\modules\alarm\camera_alarm.dm"
-#include "code\modules\alarm\fire_alarm.dm"
-#include "code\modules\alarm\motion_alarm.dm"
-#include "code\modules\alarm\power_alarm.dm"
#include "code\modules\antagonists\_common\antag_datum.dm"
#include "code\modules\antagonists\_common\antag_helpers.dm"
#include "code\modules\antagonists\_common\antag_hud.dm"
@@ -1612,6 +1607,25 @@
#include "code\modules\hydroponics\grown\tobacco.dm"
#include "code\modules\hydroponics\grown\tomato.dm"
#include "code\modules\hydroponics\grown\towercap.dm"
+#include "code\modules\instruments\_instrument_data.dm"
+#include "code\modules\instruments\_instrument_key.dm"
+#include "code\modules\instruments\brass.dm"
+#include "code\modules\instruments\chromatic_percussion.dm"
+#include "code\modules\instruments\fun.dm"
+#include "code\modules\instruments\guitar.dm"
+#include "code\modules\instruments\hardcoded.dm"
+#include "code\modules\instruments\organ.dm"
+#include "code\modules\instruments\piano.dm"
+#include "code\modules\instruments\synth_tones.dm"
+#include "code\modules\instruments\objs\items\_instrument.dm"
+#include "code\modules\instruments\objs\items\headphones.dm"
+#include "code\modules\instruments\objs\items\instruments.dm"
+#include "code\modules\instruments\objs\structures\_musician.dm"
+#include "code\modules\instruments\objs\structures\piano.dm"
+#include "code\modules\instruments\songs\_song.dm"
+#include "code\modules\instruments\songs\_song_ui.dm"
+#include "code\modules\instruments\songs\play_legacy.dm"
+#include "code\modules\instruments\songs\play_synthesized.dm"
#include "code\modules\karma\karma.dm"
#include "code\modules\keybindings\bindings_admin.dm"
#include "code\modules\keybindings\bindings_ai.dm"
@@ -2069,7 +2083,6 @@
#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm"
#include "code\modules\modular_computers\file_system\programs\command\card.dm"
#include "code\modules\modular_computers\file_system\programs\command\comms.dm"
-#include "code\modules\modular_computers\file_system\programs\engineering\alarm.dm"
#include "code\modules\modular_computers\file_system\programs\engineering\power_monitor.dm"
#include "code\modules\modular_computers\file_system\programs\engineering\sm_monitor.dm"
#include "code\modules\modular_computers\file_system\programs\generic\configurator.dm"
@@ -2107,7 +2120,6 @@
#include "code\modules\nano\interaction\physical.dm"
#include "code\modules\nano\interaction\self.dm"
#include "code\modules\nano\interaction\zlevel.dm"
-#include "code\modules\nano\modules\alarm_monitor.dm"
#include "code\modules\nano\modules\atmos_control.dm"
#include "code\modules\nano\modules\ert_manager.dm"
#include "code\modules\nano\modules\human_appearance.dm"
diff --git a/sound/instruments/banjo/Ab3.ogg b/sound/instruments/banjo/Ab3.ogg
new file mode 100644
index 00000000000..66e263bd615
Binary files /dev/null and b/sound/instruments/banjo/Ab3.ogg differ
diff --git a/sound/instruments/banjo/Ab4.ogg b/sound/instruments/banjo/Ab4.ogg
new file mode 100644
index 00000000000..f003e03233a
Binary files /dev/null and b/sound/instruments/banjo/Ab4.ogg differ
diff --git a/sound/instruments/banjo/Ab5.ogg b/sound/instruments/banjo/Ab5.ogg
new file mode 100644
index 00000000000..c405725208e
Binary files /dev/null and b/sound/instruments/banjo/Ab5.ogg differ
diff --git a/sound/instruments/banjo/An3.ogg b/sound/instruments/banjo/An3.ogg
new file mode 100644
index 00000000000..1700704c9c1
Binary files /dev/null and b/sound/instruments/banjo/An3.ogg differ
diff --git a/sound/instruments/banjo/An4.ogg b/sound/instruments/banjo/An4.ogg
new file mode 100644
index 00000000000..eb7279f869e
Binary files /dev/null and b/sound/instruments/banjo/An4.ogg differ
diff --git a/sound/instruments/banjo/An5.ogg b/sound/instruments/banjo/An5.ogg
new file mode 100644
index 00000000000..d9cf57c0feb
Binary files /dev/null and b/sound/instruments/banjo/An5.ogg differ
diff --git a/sound/instruments/banjo/Bb3.ogg b/sound/instruments/banjo/Bb3.ogg
new file mode 100644
index 00000000000..d3f757c0ace
Binary files /dev/null and b/sound/instruments/banjo/Bb3.ogg differ
diff --git a/sound/instruments/banjo/Bb4.ogg b/sound/instruments/banjo/Bb4.ogg
new file mode 100644
index 00000000000..a9d869091bf
Binary files /dev/null and b/sound/instruments/banjo/Bb4.ogg differ
diff --git a/sound/instruments/banjo/Bb5.ogg b/sound/instruments/banjo/Bb5.ogg
new file mode 100644
index 00000000000..a56e6c25005
Binary files /dev/null and b/sound/instruments/banjo/Bb5.ogg differ
diff --git a/sound/instruments/banjo/Bn2.ogg b/sound/instruments/banjo/Bn2.ogg
new file mode 100644
index 00000000000..3154f974193
Binary files /dev/null and b/sound/instruments/banjo/Bn2.ogg differ
diff --git a/sound/instruments/banjo/Bn3.ogg b/sound/instruments/banjo/Bn3.ogg
new file mode 100644
index 00000000000..6c72ec2fd5a
Binary files /dev/null and b/sound/instruments/banjo/Bn3.ogg differ
diff --git a/sound/instruments/banjo/Bn4.ogg b/sound/instruments/banjo/Bn4.ogg
new file mode 100644
index 00000000000..b0e9a2b3b2f
Binary files /dev/null and b/sound/instruments/banjo/Bn4.ogg differ
diff --git a/sound/instruments/banjo/Bn5.ogg b/sound/instruments/banjo/Bn5.ogg
new file mode 100644
index 00000000000..1b002140b87
Binary files /dev/null and b/sound/instruments/banjo/Bn5.ogg differ
diff --git a/sound/instruments/banjo/Cn3.ogg b/sound/instruments/banjo/Cn3.ogg
new file mode 100644
index 00000000000..6ef414d9d01
Binary files /dev/null and b/sound/instruments/banjo/Cn3.ogg differ
diff --git a/sound/instruments/banjo/Cn4.ogg b/sound/instruments/banjo/Cn4.ogg
new file mode 100644
index 00000000000..4a26a6741db
Binary files /dev/null and b/sound/instruments/banjo/Cn4.ogg differ
diff --git a/sound/instruments/banjo/Cn5.ogg b/sound/instruments/banjo/Cn5.ogg
new file mode 100644
index 00000000000..901ed3bc08b
Binary files /dev/null and b/sound/instruments/banjo/Cn5.ogg differ
diff --git a/sound/instruments/banjo/Cn6.ogg b/sound/instruments/banjo/Cn6.ogg
new file mode 100644
index 00000000000..5cdbbb17cea
Binary files /dev/null and b/sound/instruments/banjo/Cn6.ogg differ
diff --git a/sound/instruments/banjo/Db3.ogg b/sound/instruments/banjo/Db3.ogg
new file mode 100644
index 00000000000..1ebffdf5025
Binary files /dev/null and b/sound/instruments/banjo/Db3.ogg differ
diff --git a/sound/instruments/banjo/Db4.ogg b/sound/instruments/banjo/Db4.ogg
new file mode 100644
index 00000000000..5b939365086
Binary files /dev/null and b/sound/instruments/banjo/Db4.ogg differ
diff --git a/sound/instruments/banjo/Db5.ogg b/sound/instruments/banjo/Db5.ogg
new file mode 100644
index 00000000000..6ee4dde9479
Binary files /dev/null and b/sound/instruments/banjo/Db5.ogg differ
diff --git a/sound/instruments/banjo/Db6.ogg b/sound/instruments/banjo/Db6.ogg
new file mode 100644
index 00000000000..fd73894fda6
Binary files /dev/null and b/sound/instruments/banjo/Db6.ogg differ
diff --git a/sound/instruments/banjo/Dn3.ogg b/sound/instruments/banjo/Dn3.ogg
new file mode 100644
index 00000000000..77491b01b8c
Binary files /dev/null and b/sound/instruments/banjo/Dn3.ogg differ
diff --git a/sound/instruments/banjo/Dn4.ogg b/sound/instruments/banjo/Dn4.ogg
new file mode 100644
index 00000000000..11f68b5a157
Binary files /dev/null and b/sound/instruments/banjo/Dn4.ogg differ
diff --git a/sound/instruments/banjo/Dn5.ogg b/sound/instruments/banjo/Dn5.ogg
new file mode 100644
index 00000000000..2e9ebe49891
Binary files /dev/null and b/sound/instruments/banjo/Dn5.ogg differ
diff --git a/sound/instruments/banjo/Dn6.ogg b/sound/instruments/banjo/Dn6.ogg
new file mode 100644
index 00000000000..89ae62361dc
Binary files /dev/null and b/sound/instruments/banjo/Dn6.ogg differ
diff --git a/sound/instruments/banjo/Eb3.ogg b/sound/instruments/banjo/Eb3.ogg
new file mode 100644
index 00000000000..1d1e43049d2
Binary files /dev/null and b/sound/instruments/banjo/Eb3.ogg differ
diff --git a/sound/instruments/banjo/Eb4.ogg b/sound/instruments/banjo/Eb4.ogg
new file mode 100644
index 00000000000..2722655f5a3
Binary files /dev/null and b/sound/instruments/banjo/Eb4.ogg differ
diff --git a/sound/instruments/banjo/Eb5.ogg b/sound/instruments/banjo/Eb5.ogg
new file mode 100644
index 00000000000..7a109dfdf79
Binary files /dev/null and b/sound/instruments/banjo/Eb5.ogg differ
diff --git a/sound/instruments/banjo/En3.ogg b/sound/instruments/banjo/En3.ogg
new file mode 100644
index 00000000000..4610efdd4f0
Binary files /dev/null and b/sound/instruments/banjo/En3.ogg differ
diff --git a/sound/instruments/banjo/En4.ogg b/sound/instruments/banjo/En4.ogg
new file mode 100644
index 00000000000..64c14daf915
Binary files /dev/null and b/sound/instruments/banjo/En4.ogg differ
diff --git a/sound/instruments/banjo/En5.ogg b/sound/instruments/banjo/En5.ogg
new file mode 100644
index 00000000000..8e0b6c1637e
Binary files /dev/null and b/sound/instruments/banjo/En5.ogg differ
diff --git a/sound/instruments/banjo/Fn3.ogg b/sound/instruments/banjo/Fn3.ogg
new file mode 100644
index 00000000000..5cdc4f13fb3
Binary files /dev/null and b/sound/instruments/banjo/Fn3.ogg differ
diff --git a/sound/instruments/banjo/Fn4.ogg b/sound/instruments/banjo/Fn4.ogg
new file mode 100644
index 00000000000..78d5454f186
Binary files /dev/null and b/sound/instruments/banjo/Fn4.ogg differ
diff --git a/sound/instruments/banjo/Fn5.ogg b/sound/instruments/banjo/Fn5.ogg
new file mode 100644
index 00000000000..b21559b4656
Binary files /dev/null and b/sound/instruments/banjo/Fn5.ogg differ
diff --git a/sound/instruments/banjo/Gb3.ogg b/sound/instruments/banjo/Gb3.ogg
new file mode 100644
index 00000000000..fd055b74717
Binary files /dev/null and b/sound/instruments/banjo/Gb3.ogg differ
diff --git a/sound/instruments/banjo/Gb4.ogg b/sound/instruments/banjo/Gb4.ogg
new file mode 100644
index 00000000000..f2c62510ed0
Binary files /dev/null and b/sound/instruments/banjo/Gb4.ogg differ
diff --git a/sound/instruments/banjo/Gb5.ogg b/sound/instruments/banjo/Gb5.ogg
new file mode 100644
index 00000000000..ab17347912b
Binary files /dev/null and b/sound/instruments/banjo/Gb5.ogg differ
diff --git a/sound/instruments/banjo/Gn3.ogg b/sound/instruments/banjo/Gn3.ogg
new file mode 100644
index 00000000000..ad52ef85c08
Binary files /dev/null and b/sound/instruments/banjo/Gn3.ogg differ
diff --git a/sound/instruments/banjo/Gn4.ogg b/sound/instruments/banjo/Gn4.ogg
new file mode 100644
index 00000000000..2ddb13b86b3
Binary files /dev/null and b/sound/instruments/banjo/Gn4.ogg differ
diff --git a/sound/instruments/banjo/Gn5.ogg b/sound/instruments/banjo/Gn5.ogg
new file mode 100644
index 00000000000..d5a7886c4cf
Binary files /dev/null and b/sound/instruments/banjo/Gn5.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg
new file mode 100644
index 00000000000..aaa1e27ab89
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg
new file mode 100644
index 00000000000..ce50e76aae6
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg
new file mode 100644
index 00000000000..22f34d67592
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg
new file mode 100644
index 00000000000..eb5bb7c295e
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg
new file mode 100644
index 00000000000..bd299e321ab
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg
new file mode 100644
index 00000000000..0519d2d20dd
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg
new file mode 100644
index 00000000000..3b969a34b1c
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg
new file mode 100644
index 00000000000..75f709c16fe
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg
new file mode 100644
index 00000000000..ba347f80034
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg differ
diff --git a/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg
new file mode 100644
index 00000000000..cee89761d0d
Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg
new file mode 100644
index 00000000000..105f7676557
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg
new file mode 100644
index 00000000000..4aa33b6cded
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg
new file mode 100644
index 00000000000..d661e8d7580
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg
new file mode 100644
index 00000000000..bf650f1a6fa
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg
new file mode 100644
index 00000000000..c00f7949b7e
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg
new file mode 100644
index 00000000000..72588e9ca4c
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg
new file mode 100644
index 00000000000..b2a0b445b92
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg
new file mode 100644
index 00000000000..ecf6778343b
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg
new file mode 100644
index 00000000000..867e9ce00d0
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg
new file mode 100644
index 00000000000..446d45993e8
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg
new file mode 100644
index 00000000000..54d56400c03
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg
new file mode 100644
index 00000000000..f3770c1f1a0
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg
new file mode 100644
index 00000000000..28954fbb47f
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg
new file mode 100644
index 00000000000..1233f5314a3
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg
new file mode 100644
index 00000000000..00daf331357
Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg
new file mode 100644
index 00000000000..13ad54bff00
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg
new file mode 100644
index 00000000000..17bf392c4b2
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg
new file mode 100644
index 00000000000..feda419a0ad
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg
new file mode 100644
index 00000000000..bd088dd850e
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg
new file mode 100644
index 00000000000..09cdbeec42c
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg
new file mode 100644
index 00000000000..f82c39cee5b
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg
new file mode 100644
index 00000000000..23bfd113d6c
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg
new file mode 100644
index 00000000000..e5ec38d5ab8
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg
new file mode 100644
index 00000000000..42a6cdfad3c
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg
new file mode 100644
index 00000000000..cd6414c0aa2
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg
new file mode 100644
index 00000000000..e5366018653
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg
new file mode 100644
index 00000000000..60382228374
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg
new file mode 100644
index 00000000000..648549d594a
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg
new file mode 100644
index 00000000000..01ba59a908c
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg
new file mode 100644
index 00000000000..7cfaa8ca72b
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg
new file mode 100644
index 00000000000..b4ca49dc047
Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg
new file mode 100644
index 00000000000..7c9870a7c3b
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg
new file mode 100644
index 00000000000..5723c2edd27
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg
new file mode 100644
index 00000000000..329f14f6feb
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg
new file mode 100644
index 00000000000..5e8ac69de28
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg
new file mode 100644
index 00000000000..ddc44c69c29
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg
new file mode 100644
index 00000000000..28557475284
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg
new file mode 100644
index 00000000000..906fff5bd8d
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg
new file mode 100644
index 00000000000..96d28a7206d
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg
new file mode 100644
index 00000000000..9b917b7eb53
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg
new file mode 100644
index 00000000000..c68410d6f09
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg
new file mode 100644
index 00000000000..df84ba99e8e
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg
new file mode 100644
index 00000000000..af8c178efe8
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg
new file mode 100644
index 00000000000..268b41f1fce
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg
new file mode 100644
index 00000000000..04ceb54bfc2
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg
new file mode 100644
index 00000000000..b321983e74f
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg
new file mode 100644
index 00000000000..250a5c08e08
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg
new file mode 100644
index 00000000000..8b1c23007bb
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg
new file mode 100644
index 00000000000..098587183bb
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg
new file mode 100644
index 00000000000..81b60ef4c2f
Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg
new file mode 100644
index 00000000000..39e992fbd85
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg
new file mode 100644
index 00000000000..04aa9852815
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg
new file mode 100644
index 00000000000..aff97942e9e
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg
new file mode 100644
index 00000000000..19fd937707a
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg
new file mode 100644
index 00000000000..452e7485be1
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg
new file mode 100644
index 00000000000..66c88185a73
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg
new file mode 100644
index 00000000000..d93c5176ced
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg
new file mode 100644
index 00000000000..fabd90d2e6a
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg
new file mode 100644
index 00000000000..e4cda1487aa
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg
new file mode 100644
index 00000000000..c596994b3eb
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg
new file mode 100644
index 00000000000..d265514e27b
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg
new file mode 100644
index 00000000000..3e17b3f99a6
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg
new file mode 100644
index 00000000000..b57a8a9109a
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg
new file mode 100644
index 00000000000..ce4d9535e84
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg
new file mode 100644
index 00000000000..bb02363fffb
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg
new file mode 100644
index 00000000000..1a532ac8d42
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg
new file mode 100644
index 00000000000..16ff313baa3
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg
new file mode 100644
index 00000000000..04161d2571b
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg
new file mode 100644
index 00000000000..30a3c653a1c
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg
new file mode 100644
index 00000000000..f6bc891506c
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg
new file mode 100644
index 00000000000..ab47f6940c9
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg
new file mode 100644
index 00000000000..5dfb9aa5291
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg
new file mode 100644
index 00000000000..7bc8784207e
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg
new file mode 100644
index 00000000000..185b4d3db64
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg
new file mode 100644
index 00000000000..f358ef0810d
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg
new file mode 100644
index 00000000000..048f9640bfe
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg
new file mode 100644
index 00000000000..f1083d7dcb2
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg
new file mode 100644
index 00000000000..244ebc3d5f2
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg
new file mode 100644
index 00000000000..d3c68d64e9c
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg
new file mode 100644
index 00000000000..2666ee66134
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg
new file mode 100644
index 00000000000..050e463c0d1
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg differ
diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg
new file mode 100644
index 00000000000..4793c5b7fd7
Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg differ
diff --git a/sound/instruments/synthesis_samples/tones/Sawtooth.ogg b/sound/instruments/synthesis_samples/tones/Sawtooth.ogg
new file mode 100644
index 00000000000..10b1930a64c
Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Sawtooth.ogg differ
diff --git a/sound/instruments/synthesis_samples/tones/Sine.ogg b/sound/instruments/synthesis_samples/tones/Sine.ogg
new file mode 100644
index 00000000000..96a09d501b5
Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Sine.ogg differ
diff --git a/sound/instruments/synthesis_samples/tones/Square.ogg b/sound/instruments/synthesis_samples/tones/Square.ogg
new file mode 100644
index 00000000000..71029c07f95
Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Square.ogg differ
diff --git a/sound/instruments/violin/Ab1.mid b/sound/instruments/violin/Ab1.mid
new file mode 100644
index 00000000000..b8253364b4e
Binary files /dev/null and b/sound/instruments/violin/Ab1.mid differ
diff --git a/sound/instruments/violin/Ab2.mid b/sound/instruments/violin/Ab2.mid
new file mode 100644
index 00000000000..4cd7f9b55a7
Binary files /dev/null and b/sound/instruments/violin/Ab2.mid differ
diff --git a/sound/instruments/violin/Ab3.mid b/sound/instruments/violin/Ab3.mid
new file mode 100644
index 00000000000..e827cfc635e
Binary files /dev/null and b/sound/instruments/violin/Ab3.mid differ
diff --git a/sound/instruments/violin/Ab4.mid b/sound/instruments/violin/Ab4.mid
new file mode 100644
index 00000000000..57e1f76c976
Binary files /dev/null and b/sound/instruments/violin/Ab4.mid differ
diff --git a/sound/instruments/violin/Ab5.mid b/sound/instruments/violin/Ab5.mid
new file mode 100644
index 00000000000..59e95a6d997
Binary files /dev/null and b/sound/instruments/violin/Ab5.mid differ
diff --git a/sound/instruments/violin/Ab6.mid b/sound/instruments/violin/Ab6.mid
new file mode 100644
index 00000000000..9bd3436287b
Binary files /dev/null and b/sound/instruments/violin/Ab6.mid differ
diff --git a/sound/instruments/violin/Ab7.mid b/sound/instruments/violin/Ab7.mid
new file mode 100644
index 00000000000..3c90af807e2
Binary files /dev/null and b/sound/instruments/violin/Ab7.mid differ
diff --git a/sound/instruments/violin/Ab8.mid b/sound/instruments/violin/Ab8.mid
new file mode 100644
index 00000000000..873d771f2ae
Binary files /dev/null and b/sound/instruments/violin/Ab8.mid differ
diff --git a/sound/instruments/violin/An1.mid b/sound/instruments/violin/An1.mid
new file mode 100644
index 00000000000..d7f8a001d93
Binary files /dev/null and b/sound/instruments/violin/An1.mid differ
diff --git a/sound/instruments/violin/An2.mid b/sound/instruments/violin/An2.mid
new file mode 100644
index 00000000000..2f01800a075
Binary files /dev/null and b/sound/instruments/violin/An2.mid differ
diff --git a/sound/instruments/violin/An3.mid b/sound/instruments/violin/An3.mid
new file mode 100644
index 00000000000..c8ed3cdfa6c
Binary files /dev/null and b/sound/instruments/violin/An3.mid differ
diff --git a/sound/instruments/violin/An4.mid b/sound/instruments/violin/An4.mid
new file mode 100644
index 00000000000..e7984ca7e62
Binary files /dev/null and b/sound/instruments/violin/An4.mid differ
diff --git a/sound/instruments/violin/An5.mid b/sound/instruments/violin/An5.mid
new file mode 100644
index 00000000000..e1fd228f7a9
Binary files /dev/null and b/sound/instruments/violin/An5.mid differ
diff --git a/sound/instruments/violin/An6.mid b/sound/instruments/violin/An6.mid
new file mode 100644
index 00000000000..1c8df6c98e5
Binary files /dev/null and b/sound/instruments/violin/An6.mid differ
diff --git a/sound/instruments/violin/An7.mid b/sound/instruments/violin/An7.mid
new file mode 100644
index 00000000000..2784428daf9
Binary files /dev/null and b/sound/instruments/violin/An7.mid differ
diff --git a/sound/instruments/violin/An8.mid b/sound/instruments/violin/An8.mid
new file mode 100644
index 00000000000..2db2ab70a7d
Binary files /dev/null and b/sound/instruments/violin/An8.mid differ
diff --git a/sound/instruments/violin/Bb1.mid b/sound/instruments/violin/Bb1.mid
new file mode 100644
index 00000000000..693b73f5420
Binary files /dev/null and b/sound/instruments/violin/Bb1.mid differ
diff --git a/sound/instruments/violin/Bb2.mid b/sound/instruments/violin/Bb2.mid
new file mode 100644
index 00000000000..40da5f3da15
Binary files /dev/null and b/sound/instruments/violin/Bb2.mid differ
diff --git a/sound/instruments/violin/Bb3.mid b/sound/instruments/violin/Bb3.mid
new file mode 100644
index 00000000000..5bab6ccd636
Binary files /dev/null and b/sound/instruments/violin/Bb3.mid differ
diff --git a/sound/instruments/violin/Bb4.mid b/sound/instruments/violin/Bb4.mid
new file mode 100644
index 00000000000..dce830448ef
Binary files /dev/null and b/sound/instruments/violin/Bb4.mid differ
diff --git a/sound/instruments/violin/Bb5.mid b/sound/instruments/violin/Bb5.mid
new file mode 100644
index 00000000000..fda796e27b9
Binary files /dev/null and b/sound/instruments/violin/Bb5.mid differ
diff --git a/sound/instruments/violin/Bb6.mid b/sound/instruments/violin/Bb6.mid
new file mode 100644
index 00000000000..9e5da684f43
Binary files /dev/null and b/sound/instruments/violin/Bb6.mid differ
diff --git a/sound/instruments/violin/Bb7.mid b/sound/instruments/violin/Bb7.mid
new file mode 100644
index 00000000000..215c56cbe7e
Binary files /dev/null and b/sound/instruments/violin/Bb7.mid differ
diff --git a/sound/instruments/violin/Bb8.mid b/sound/instruments/violin/Bb8.mid
new file mode 100644
index 00000000000..4b55c34691f
Binary files /dev/null and b/sound/instruments/violin/Bb8.mid differ
diff --git a/sound/instruments/violin/Bn1.mid b/sound/instruments/violin/Bn1.mid
new file mode 100644
index 00000000000..27968b5f9e7
Binary files /dev/null and b/sound/instruments/violin/Bn1.mid differ
diff --git a/sound/instruments/violin/Bn2.mid b/sound/instruments/violin/Bn2.mid
new file mode 100644
index 00000000000..54c9b99d03f
Binary files /dev/null and b/sound/instruments/violin/Bn2.mid differ
diff --git a/sound/instruments/violin/Bn3.mid b/sound/instruments/violin/Bn3.mid
new file mode 100644
index 00000000000..f73476fb7bb
Binary files /dev/null and b/sound/instruments/violin/Bn3.mid differ
diff --git a/sound/instruments/violin/Bn4.mid b/sound/instruments/violin/Bn4.mid
new file mode 100644
index 00000000000..2aa30708a6c
Binary files /dev/null and b/sound/instruments/violin/Bn4.mid differ
diff --git a/sound/instruments/violin/Bn5.mid b/sound/instruments/violin/Bn5.mid
new file mode 100644
index 00000000000..0ebe636b714
Binary files /dev/null and b/sound/instruments/violin/Bn5.mid differ
diff --git a/sound/instruments/violin/Bn6.mid b/sound/instruments/violin/Bn6.mid
new file mode 100644
index 00000000000..3b8e1c217f7
Binary files /dev/null and b/sound/instruments/violin/Bn6.mid differ
diff --git a/sound/instruments/violin/Bn7.mid b/sound/instruments/violin/Bn7.mid
new file mode 100644
index 00000000000..afcb1982a13
Binary files /dev/null and b/sound/instruments/violin/Bn7.mid differ
diff --git a/sound/instruments/violin/Bn8.mid b/sound/instruments/violin/Bn8.mid
new file mode 100644
index 00000000000..3afd469256c
Binary files /dev/null and b/sound/instruments/violin/Bn8.mid differ
diff --git a/sound/instruments/violin/Cn1.mid b/sound/instruments/violin/Cn1.mid
new file mode 100644
index 00000000000..857120f31f4
Binary files /dev/null and b/sound/instruments/violin/Cn1.mid differ
diff --git a/sound/instruments/violin/Cn2.mid b/sound/instruments/violin/Cn2.mid
new file mode 100644
index 00000000000..3ccd6670e87
Binary files /dev/null and b/sound/instruments/violin/Cn2.mid differ
diff --git a/sound/instruments/violin/Cn3.mid b/sound/instruments/violin/Cn3.mid
new file mode 100644
index 00000000000..1851e4f8d27
Binary files /dev/null and b/sound/instruments/violin/Cn3.mid differ
diff --git a/sound/instruments/violin/Cn4.mid b/sound/instruments/violin/Cn4.mid
new file mode 100644
index 00000000000..65e8b0efe4e
Binary files /dev/null and b/sound/instruments/violin/Cn4.mid differ
diff --git a/sound/instruments/violin/Cn5.mid b/sound/instruments/violin/Cn5.mid
new file mode 100644
index 00000000000..544f921e43b
Binary files /dev/null and b/sound/instruments/violin/Cn5.mid differ
diff --git a/sound/instruments/violin/Cn6.mid b/sound/instruments/violin/Cn6.mid
new file mode 100644
index 00000000000..7c78dab2f07
Binary files /dev/null and b/sound/instruments/violin/Cn6.mid differ
diff --git a/sound/instruments/violin/Cn7.mid b/sound/instruments/violin/Cn7.mid
new file mode 100644
index 00000000000..3abe4cde086
Binary files /dev/null and b/sound/instruments/violin/Cn7.mid differ
diff --git a/sound/instruments/violin/Cn8.mid b/sound/instruments/violin/Cn8.mid
new file mode 100644
index 00000000000..06f14081b3b
Binary files /dev/null and b/sound/instruments/violin/Cn8.mid differ
diff --git a/sound/instruments/violin/Cn9.mid b/sound/instruments/violin/Cn9.mid
new file mode 100644
index 00000000000..62f4eef045a
Binary files /dev/null and b/sound/instruments/violin/Cn9.mid differ
diff --git a/sound/instruments/violin/Db1.mid b/sound/instruments/violin/Db1.mid
new file mode 100644
index 00000000000..88dba851452
Binary files /dev/null and b/sound/instruments/violin/Db1.mid differ
diff --git a/sound/instruments/violin/Db2.mid b/sound/instruments/violin/Db2.mid
new file mode 100644
index 00000000000..b510926b45f
Binary files /dev/null and b/sound/instruments/violin/Db2.mid differ
diff --git a/sound/instruments/violin/Db3.mid b/sound/instruments/violin/Db3.mid
new file mode 100644
index 00000000000..9954bbe478a
Binary files /dev/null and b/sound/instruments/violin/Db3.mid differ
diff --git a/sound/instruments/violin/Db4.mid b/sound/instruments/violin/Db4.mid
new file mode 100644
index 00000000000..2c5ff74db0a
Binary files /dev/null and b/sound/instruments/violin/Db4.mid differ
diff --git a/sound/instruments/violin/Db5.mid b/sound/instruments/violin/Db5.mid
new file mode 100644
index 00000000000..e5850a3fd04
Binary files /dev/null and b/sound/instruments/violin/Db5.mid differ
diff --git a/sound/instruments/violin/Db6.mid b/sound/instruments/violin/Db6.mid
new file mode 100644
index 00000000000..217c0ad014c
Binary files /dev/null and b/sound/instruments/violin/Db6.mid differ
diff --git a/sound/instruments/violin/Db7.mid b/sound/instruments/violin/Db7.mid
new file mode 100644
index 00000000000..ec32bdbf904
Binary files /dev/null and b/sound/instruments/violin/Db7.mid differ
diff --git a/sound/instruments/violin/Db8.mid b/sound/instruments/violin/Db8.mid
new file mode 100644
index 00000000000..555bce3db0d
Binary files /dev/null and b/sound/instruments/violin/Db8.mid differ
diff --git a/sound/instruments/violin/Dn1.mid b/sound/instruments/violin/Dn1.mid
new file mode 100644
index 00000000000..92e4e0d9581
Binary files /dev/null and b/sound/instruments/violin/Dn1.mid differ
diff --git a/sound/instruments/violin/Dn2.mid b/sound/instruments/violin/Dn2.mid
new file mode 100644
index 00000000000..34eb9d1db1b
Binary files /dev/null and b/sound/instruments/violin/Dn2.mid differ
diff --git a/sound/instruments/violin/Dn3.mid b/sound/instruments/violin/Dn3.mid
new file mode 100644
index 00000000000..fbd56085aaf
Binary files /dev/null and b/sound/instruments/violin/Dn3.mid differ
diff --git a/sound/instruments/violin/Dn4.mid b/sound/instruments/violin/Dn4.mid
new file mode 100644
index 00000000000..e13c7448292
Binary files /dev/null and b/sound/instruments/violin/Dn4.mid differ
diff --git a/sound/instruments/violin/Dn5.mid b/sound/instruments/violin/Dn5.mid
new file mode 100644
index 00000000000..8fd41e5c6fe
Binary files /dev/null and b/sound/instruments/violin/Dn5.mid differ
diff --git a/sound/instruments/violin/Dn6.mid b/sound/instruments/violin/Dn6.mid
new file mode 100644
index 00000000000..d47329e8f9e
Binary files /dev/null and b/sound/instruments/violin/Dn6.mid differ
diff --git a/sound/instruments/violin/Dn7.mid b/sound/instruments/violin/Dn7.mid
new file mode 100644
index 00000000000..b2496603876
Binary files /dev/null and b/sound/instruments/violin/Dn7.mid differ
diff --git a/sound/instruments/violin/Dn8.mid b/sound/instruments/violin/Dn8.mid
new file mode 100644
index 00000000000..56667a1a86d
Binary files /dev/null and b/sound/instruments/violin/Dn8.mid differ
diff --git a/sound/instruments/violin/Eb1.mid b/sound/instruments/violin/Eb1.mid
new file mode 100644
index 00000000000..829e6fcf185
Binary files /dev/null and b/sound/instruments/violin/Eb1.mid differ
diff --git a/sound/instruments/violin/Eb2.mid b/sound/instruments/violin/Eb2.mid
new file mode 100644
index 00000000000..66029b340cc
Binary files /dev/null and b/sound/instruments/violin/Eb2.mid differ
diff --git a/sound/instruments/violin/Eb3.mid b/sound/instruments/violin/Eb3.mid
new file mode 100644
index 00000000000..c982375941e
Binary files /dev/null and b/sound/instruments/violin/Eb3.mid differ
diff --git a/sound/instruments/violin/Eb4.mid b/sound/instruments/violin/Eb4.mid
new file mode 100644
index 00000000000..016ed4f1edf
Binary files /dev/null and b/sound/instruments/violin/Eb4.mid differ
diff --git a/sound/instruments/violin/Eb5.mid b/sound/instruments/violin/Eb5.mid
new file mode 100644
index 00000000000..ddb511795df
Binary files /dev/null and b/sound/instruments/violin/Eb5.mid differ
diff --git a/sound/instruments/violin/Eb6.mid b/sound/instruments/violin/Eb6.mid
new file mode 100644
index 00000000000..b7242b9ab99
Binary files /dev/null and b/sound/instruments/violin/Eb6.mid differ
diff --git a/sound/instruments/violin/Eb7.mid b/sound/instruments/violin/Eb7.mid
new file mode 100644
index 00000000000..773538340a5
Binary files /dev/null and b/sound/instruments/violin/Eb7.mid differ
diff --git a/sound/instruments/violin/Eb8.mid b/sound/instruments/violin/Eb8.mid
new file mode 100644
index 00000000000..4ad074e173b
Binary files /dev/null and b/sound/instruments/violin/Eb8.mid differ
diff --git a/sound/instruments/violin/En1.mid b/sound/instruments/violin/En1.mid
new file mode 100644
index 00000000000..79ab68df9df
Binary files /dev/null and b/sound/instruments/violin/En1.mid differ
diff --git a/sound/instruments/violin/En2.mid b/sound/instruments/violin/En2.mid
new file mode 100644
index 00000000000..cd61c8d0de5
Binary files /dev/null and b/sound/instruments/violin/En2.mid differ
diff --git a/sound/instruments/violin/En3.mid b/sound/instruments/violin/En3.mid
new file mode 100644
index 00000000000..da5b703d545
Binary files /dev/null and b/sound/instruments/violin/En3.mid differ
diff --git a/sound/instruments/violin/En4.mid b/sound/instruments/violin/En4.mid
new file mode 100644
index 00000000000..f7d3af024ff
Binary files /dev/null and b/sound/instruments/violin/En4.mid differ
diff --git a/sound/instruments/violin/En5.mid b/sound/instruments/violin/En5.mid
new file mode 100644
index 00000000000..d3d353943f9
Binary files /dev/null and b/sound/instruments/violin/En5.mid differ
diff --git a/sound/instruments/violin/En6.mid b/sound/instruments/violin/En6.mid
new file mode 100644
index 00000000000..73eb5b0697d
Binary files /dev/null and b/sound/instruments/violin/En6.mid differ
diff --git a/sound/instruments/violin/En7.mid b/sound/instruments/violin/En7.mid
new file mode 100644
index 00000000000..79a9462c844
Binary files /dev/null and b/sound/instruments/violin/En7.mid differ
diff --git a/sound/instruments/violin/En8.mid b/sound/instruments/violin/En8.mid
new file mode 100644
index 00000000000..88947fc7318
Binary files /dev/null and b/sound/instruments/violin/En8.mid differ
diff --git a/sound/instruments/violin/Fn1.mid b/sound/instruments/violin/Fn1.mid
new file mode 100644
index 00000000000..abe0d4e4051
Binary files /dev/null and b/sound/instruments/violin/Fn1.mid differ
diff --git a/sound/instruments/violin/Fn2.mid b/sound/instruments/violin/Fn2.mid
new file mode 100644
index 00000000000..d245bef3b54
Binary files /dev/null and b/sound/instruments/violin/Fn2.mid differ
diff --git a/sound/instruments/violin/Fn3.mid b/sound/instruments/violin/Fn3.mid
new file mode 100644
index 00000000000..e532e30dac9
Binary files /dev/null and b/sound/instruments/violin/Fn3.mid differ
diff --git a/sound/instruments/violin/Fn4.mid b/sound/instruments/violin/Fn4.mid
new file mode 100644
index 00000000000..47219c72fa2
Binary files /dev/null and b/sound/instruments/violin/Fn4.mid differ
diff --git a/sound/instruments/violin/Fn5.mid b/sound/instruments/violin/Fn5.mid
new file mode 100644
index 00000000000..630d16371d9
Binary files /dev/null and b/sound/instruments/violin/Fn5.mid differ
diff --git a/sound/instruments/violin/Fn6.mid b/sound/instruments/violin/Fn6.mid
new file mode 100644
index 00000000000..08cbc981bdb
Binary files /dev/null and b/sound/instruments/violin/Fn6.mid differ
diff --git a/sound/instruments/violin/Fn7.mid b/sound/instruments/violin/Fn7.mid
new file mode 100644
index 00000000000..6c28c7d272e
Binary files /dev/null and b/sound/instruments/violin/Fn7.mid differ
diff --git a/sound/instruments/violin/Fn8.mid b/sound/instruments/violin/Fn8.mid
new file mode 100644
index 00000000000..2d73762f269
Binary files /dev/null and b/sound/instruments/violin/Fn8.mid differ
diff --git a/sound/instruments/violin/Gb1.mid b/sound/instruments/violin/Gb1.mid
new file mode 100644
index 00000000000..d18668e8911
Binary files /dev/null and b/sound/instruments/violin/Gb1.mid differ
diff --git a/sound/instruments/violin/Gb2.mid b/sound/instruments/violin/Gb2.mid
new file mode 100644
index 00000000000..302f0c6fdc1
Binary files /dev/null and b/sound/instruments/violin/Gb2.mid differ
diff --git a/sound/instruments/violin/Gb3.mid b/sound/instruments/violin/Gb3.mid
new file mode 100644
index 00000000000..1f592fc9039
Binary files /dev/null and b/sound/instruments/violin/Gb3.mid differ
diff --git a/sound/instruments/violin/Gb4.mid b/sound/instruments/violin/Gb4.mid
new file mode 100644
index 00000000000..45854126f98
Binary files /dev/null and b/sound/instruments/violin/Gb4.mid differ
diff --git a/sound/instruments/violin/Gb5.mid b/sound/instruments/violin/Gb5.mid
new file mode 100644
index 00000000000..fb1e1da339a
Binary files /dev/null and b/sound/instruments/violin/Gb5.mid differ
diff --git a/sound/instruments/violin/Gb6.mid b/sound/instruments/violin/Gb6.mid
new file mode 100644
index 00000000000..bfa896bb784
Binary files /dev/null and b/sound/instruments/violin/Gb6.mid differ
diff --git a/sound/instruments/violin/Gb7.mid b/sound/instruments/violin/Gb7.mid
new file mode 100644
index 00000000000..a27763c1d47
Binary files /dev/null and b/sound/instruments/violin/Gb7.mid differ
diff --git a/sound/instruments/violin/Gb8.mid b/sound/instruments/violin/Gb8.mid
new file mode 100644
index 00000000000..aaab80a7276
Binary files /dev/null and b/sound/instruments/violin/Gb8.mid differ
diff --git a/sound/instruments/violin/Gn1.mid b/sound/instruments/violin/Gn1.mid
new file mode 100644
index 00000000000..1df52ab0760
Binary files /dev/null and b/sound/instruments/violin/Gn1.mid differ
diff --git a/sound/instruments/violin/Gn2.mid b/sound/instruments/violin/Gn2.mid
new file mode 100644
index 00000000000..6e0ca383127
Binary files /dev/null and b/sound/instruments/violin/Gn2.mid differ
diff --git a/sound/instruments/violin/Gn3.mid b/sound/instruments/violin/Gn3.mid
new file mode 100644
index 00000000000..bb3e6dedcbf
Binary files /dev/null and b/sound/instruments/violin/Gn3.mid differ
diff --git a/sound/instruments/violin/Gn4.mid b/sound/instruments/violin/Gn4.mid
new file mode 100644
index 00000000000..0c46432afee
Binary files /dev/null and b/sound/instruments/violin/Gn4.mid differ
diff --git a/sound/instruments/violin/Gn5.mid b/sound/instruments/violin/Gn5.mid
new file mode 100644
index 00000000000..f39dcf5e2b9
Binary files /dev/null and b/sound/instruments/violin/Gn5.mid differ
diff --git a/sound/instruments/violin/Gn6.mid b/sound/instruments/violin/Gn6.mid
new file mode 100644
index 00000000000..0efa2259ca1
Binary files /dev/null and b/sound/instruments/violin/Gn6.mid differ
diff --git a/sound/instruments/violin/Gn7.mid b/sound/instruments/violin/Gn7.mid
new file mode 100644
index 00000000000..22fd1b6bcb0
Binary files /dev/null and b/sound/instruments/violin/Gn7.mid differ
diff --git a/sound/instruments/violin/Gn8.mid b/sound/instruments/violin/Gn8.mid
new file mode 100644
index 00000000000..16b7171d627
Binary files /dev/null and b/sound/instruments/violin/Gn8.mid differ
diff --git a/sound/music/thunderdome.ogg b/sound/music/thunderdome.ogg
index 26b18df5e05..82780e416d4 100644
Binary files a/sound/music/thunderdome.ogg and b/sound/music/thunderdome.ogg differ
diff --git a/sound/weapons/banjoslap.ogg b/sound/weapons/banjoslap.ogg
new file mode 100644
index 00000000000..06a86a535dd
Binary files /dev/null and b/sound/weapons/banjoslap.ogg differ
diff --git a/sound/weapons/guitarslam.ogg b/sound/weapons/guitarslam.ogg
new file mode 100644
index 00000000000..4fa53db9404
Binary files /dev/null and b/sound/weapons/guitarslam.ogg differ
diff --git a/tgui/packages/tgui/components/Collapsible.js b/tgui/packages/tgui/components/Collapsible.js
index 84af070fe18..080074e48ef 100644
--- a/tgui/packages/tgui/components/Collapsible.js
+++ b/tgui/packages/tgui/components/Collapsible.js
@@ -22,7 +22,7 @@ export class Collapsible extends Component {
...rest
} = props;
return (
-
+ |