diff --git a/.travis.yml b/.travis.yml index e4443d14ee0..74ac9fedb9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,8 +52,6 @@ jobs: - libstdc++6:i386 - libssl-dev:i386 - gcc-multilib - - g++-7 - - g++-7-multilib - pkg-config:i386 - zlib1g-dev:i386 cache: @@ -61,6 +59,8 @@ jobs: - $HOME/.cargo - $HOME/.rustup - $HOME/BYOND + before_install: + - 'sudo apt-get -qq update' install: - tools/travis/install_byond.sh - source $HOME/BYOND/byond/bin/byondsetup diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm index cc7896a0a7a..b715346969b 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 17efbaee17c..91339293420 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/listeningpost.dmm b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm index 32d5ea2af1d..5607083f0a8 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm @@ -99,6 +99,12 @@ /obj/item/tank/air, /turf/simulated/floor/plasteel, /area/ruin/powered) +"u" = ( +/obj/machinery/tcomms/relay/ruskie{ + network_id = "LISTENINGOUTPOST-RELAY" + }, +/turf/simulated/floor/plasteel, +/area/ruin/powered) "w" = ( /obj/machinery/light/small, /turf/simulated/floor/plasteel, @@ -922,7 +928,7 @@ b f f f -c +f f D m @@ -964,7 +970,7 @@ f f p Y -f +u f j i 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/RandomRuins/SpaceRuins/wizardcrash.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm index a063d88a252..3add952e8e2 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm @@ -152,12 +152,7 @@ /turf/simulated/floor/wood, /area/ruin/unpowered) "az" = ( -/obj/item/spellbook/oneuse/charge{ - desc = "For the technology-hating wizard."; - spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech; - spellname = "Disable Technology"; - used = 1 - }, +/obj/item/spellbook/oneuse/emp/used, /turf/simulated/floor/wood, /area/ruin/unpowered) "aA" = ( diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 353f5d83637..f8dc3f902c9 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -35243,7 +35243,7 @@ }, /area/bridge) "bnW" = ( -/obj/machinery/computer/station_alert/all, +/obj/machinery/computer/station_alert, /turf/simulated/floor/plasteel{ dir = 0; icon_state = "yellow" @@ -72568,7 +72568,7 @@ pixel_x = -2; pixel_y = 2 }, -/obj/item/circuitboard/stationalert_all{ +/obj/item/circuitboard/stationalert{ pixel_x = 1; pixel_y = -1 }, @@ -75836,7 +75836,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; @@ -77200,6 +77200,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/logs.dm b/code/__DEFINES/logs.dm index 25d1fac7323..96fac582283 100644 --- a/code/__DEFINES/logs.dm +++ b/code/__DEFINES/logs.dm @@ -4,3 +4,8 @@ #define SAY_LOG "Say" #define EMOTE_LOG "Emote" #define MISC_LOG "Misc" +#define DEADCHAT_LOG "Deadchat" +#define OOC_LOG "OOC" +#define LOOC_LOG "LOOC" + +#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, DEADCHAT_LOG, OOC_LOG, LOOC_LOG, MISC_LOG) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index b56024bfab8..070aaaa2249 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -92,3 +92,8 @@ // Firelock states #define FD_OPEN 1 #define FD_CLOSED 2 + +// Computer login types +#define LOGIN_TYPE_NORMAL 1 +#define LOGIN_TYPE_AI 2 +#define LOGIN_TYPE_ROBOT 3 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/tgui.dm b/code/__DEFINES/tgui.dm new file mode 100644 index 00000000000..0ef159f11c2 --- /dev/null +++ b/code/__DEFINES/tgui.dm @@ -0,0 +1,8 @@ +// TGUI defines +#define TGUI_MODAL_INPUT_MAX_LENGTH 1024 +#define TGUI_MODAL_INPUT_MAX_LENGTH_NAME 64 // Names for generally anything don't go past 32, let alone 64. + +#define TGUI_MODAL_OPEN 1 +#define TGUI_MODAL_DELEGATE 2 +#define TGUI_MODAL_ANSWER 3 +#define TGUI_MODAL_CLOSE 4 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 bd077204f00..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) @@ -425,66 +443,8 @@ if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) . = TRUE -proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/roletext = be_special_type ? get_roletext(be_special_type) : null - var/list/mob/dead/observer/candidates = list() - var/time_passed = world.time - if(!Question) - Question = "Would you like to be a special role?" - - for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) - if(!G.key || !G.client) - continue - if(be_special_type) - if(!(be_special_type in G.client.prefs.be_special)) - continue - if(antag_age_check) - if(!player_old_enough_antag(G.client, be_special_type)) - continue - if(roletext) - if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) - continue - if(config.use_exp_restrictions && min_hours) - if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) - continue - if(check_antaghud && cannotPossess(G)) - continue - spawn(0) - G << 'sound/misc/notice2.ogg'//Alerting them to their consideration - if(flashwindow) - window_flash(G.client) - var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") - if(!G?.client) - return - switch(ans) - if("Yes") - to_chat(G, "Choice registered: Yes.") - if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. - to_chat(G, "Sorry, you were too late for the consideration!") - G << 'sound/machines/buzz-sigh.ogg' - return - candidates += G - if("No") - to_chat(G, "Choice registered: No.") - return - if("Not This Round") - to_chat(G, "Choice registered: No.") - to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") - G.client.prefs.be_special -= be_special_type - return - else - return - sleep(poll_time) - - //Check all our candidates, to make sure they didn't log off during the 30 second wait period. - for(var/mob/dead/observer/G in candidates) - if(!G.key || !G.client) - candidates.Remove(G) - - return candidates - -/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) +/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = FALSE, flashwindow = TRUE, check_antaghud = TRUE, source) + var/list/willing_ghosts = SSghost_spawns.poll_candidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud, source) var/list/selected_ghosts = list() if(!willing_ghosts.len) return selected_ghosts diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index f9b9850bd2d..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) @@ -693,6 +690,9 @@ proc/dd_sortedObjectList(list/incoming) // Lazying Episode 3 #define LAZYSET(L, K, V) LAZYINITLIST(L); L[K] = V; +/// Returns whether a numerical index is within a given list's bounds. Faster than isnull(LAZYACCESS(L, I)). +#define ISINDEXSAFE(L, I) (I >= 1 && I <= length(L)) + //same, but returns nothing and acts on list in place /proc/shuffle_inplace(list/L) if(!L) 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/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index d6caa29f670..fc0f7520e0e 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1919,16 +1919,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) return pois -/* - * Returns all player mobs in an assoc list with ckey as key and the mob as value -*/ -/proc/get_assoc_mob_list_by_ckey() - var/list/mob/mobs = list() - for(var/i in GLOB.player_list) - var/mob/M = i - mobs[M.ckey] = M - return mobs - /proc/flash_color(mob_or_client, flash_color="#960000", flash_time=20) var/client/C if(istype(mob_or_client, /mob)) 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/_globalvars/logging.dm b/code/_globalvars/logging.dm index f5f71e65c57..50e70bc62a4 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -34,4 +34,6 @@ GLOBAL_PROTECT(IClog) GLOBAL_LIST_EMPTY(OOClog) GLOBAL_PROTECT(OOClog) +GLOBAL_DATUM_INIT(logging, /datum/logging, new /datum/logging()) + GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs")) diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index fbb04d4c021..b0676eabdc7 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -145,12 +145,10 @@ /mob/living/silicon/ai/MiddleClickOn(var/atom/A) A.AIMiddleClick(src) -/* - The following criminally helpful code is just the previous code cleaned up; - I have no idea why it was in atoms.dm instead of respective files. -*/ -/atom/proc/AICtrlShiftClick(var/mob/user) // Examines +// DEFAULT PROCS TO OVERRIDE + +/atom/proc/AICtrlShiftClick(mob/user) // Examines if(user.client) user.examinate(src) return @@ -158,74 +156,86 @@ /atom/proc/AIAltShiftClick() return -/obj/machinery/door/airlock/AIAltShiftClick() // Sets/Unsets Emergency Access Override - if(density) - Topic(src, list("src" = UID(), "command"="emergency", "activate" = "1"), 1) // 1 meaning no window (consistency!) - else - Topic(src, list("src" = UID(), "command"="emergency", "activate" = "0"), 1) - return - -/atom/proc/AIShiftClick(var/mob/user) +/atom/proc/AIShiftClick(mob/living/user) // borgs use this too if(user.client) user.examinate(src) return -/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors! - if(density) - Topic(src, list("src" = UID(), "command"="open", "activate" = "1"), 1) // 1 meaning no window (consistency!) - else - Topic(src, list("src" = UID(), "command"="open", "activate" = "0"), 1) +/atom/proc/AICtrlClick(mob/living/silicon/ai/user) return -/atom/proc/AICtrlClick(var/mob/living/silicon/ai/user) - return - -/obj/machinery/door/airlock/AICtrlClick() // Bolts doors - if(locked) - Topic(src, list("src" = UID(), "command"="bolts", "activate" = "0"), 1)// 1 meaning no window (consistency!) - else - Topic(src, list("src" = UID(), "command"="bolts", "activate" = "1"), 1) - -/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs. - Topic("breaker=1", list("breaker"="1"), 0) // 0 meaning no window (consistency! wait...) - -/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets - Topic(src, list("src" = UID(), "command"="enable", "value"="[!enabled]"), 1) // 1 meaning no window (consistency!) - -/atom/proc/AIAltClick(var/atom/A) +/atom/proc/AIAltClick(atom/A) AltClick(A) -/obj/machinery/door/airlock/AIAltClick() // Electrifies doors. - if(!electrified_until) - // permanent shock - Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "1"), 1) // 1 meaning no window (consistency!) - else - // disable/6 is not in Topic; disable/5 disables both temporary and permanent shock - Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "0"), 1) +/atom/proc/AIMiddleClick(mob/living/user) return +/mob/living/silicon/ai/TurfAdjacent(turf/T) + return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) + + +// APC + +/obj/machinery/power/apc/AICtrlClick(mob/living/user) // turns off/on APCs. + toggle_breaker(user) + + +// TURRETCONTROL + +/obj/machinery/turretid/AICtrlClick(mob/living/silicon/ai/user) //turns off/on Turrets + enabled = !enabled + updateTurrets() + /obj/machinery/turretid/AIAltClick() //toggles lethal on turrets - Topic(src, list("src" = UID(), "command"="lethal", "value"="[!lethal]"), 1) // 1 meaning no window (consistency!) + if(lethal_is_configurable) + lethal = !lethal + updateTurrets() -/atom/proc/AIMiddleClick() - return +// AIRLOCKS -/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights. - if(!src.lights) - Topic(src, list("src" = UID(), "command"="lights", "activate" = "1"), 1) // 1 meaning no window (consistency!) +/obj/machinery/door/airlock/AIAltShiftClick(mob/user) // Sets/Unsets Emergency Access Override + emergency = !emergency + update_icon() + +/obj/machinery/door/airlock/AIShiftClick(mob/user) // Opens and closes doors! + if(welded) + to_chat(user, "The airlock has been welded shut!") + if(locked) + locked = !locked + if(density) + open() else - Topic(src, list("src" = UID(), "command"="lights", "activate" = "0"), 1) - return + close() -/obj/machinery/ai_slipper/AICtrlClick() //Turns liquid dispenser on or off +/obj/machinery/door/airlock/AICtrlClick(mob/living/silicon/ai/user) // Bolts doors + locked = !locked + update_icon() + +/obj/machinery/door/airlock/AIAltClick(mob/living/silicon/ai/user) // Electrifies doors. + if(wires.is_cut(WIRE_ELECTRIFY)) + to_chat(user, "The electrification wire is cut - Cannot electrify the door.") + if(isElectrified()) + electrify(0) // un-shock + else + electrify(-1) // permanent shock + + +/obj/machinery/door/airlock/AIMiddleClick(mob/living/user) // Toggles door bolt lights. + if(wires.is_cut(WIRE_BOLT_LIGHT)) + to_chat(user, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.") + else if(lights) + lights = FALSE + to_chat(user, "The door bolt lights have been disabled.") + else if(!lights) + lights = TRUE + to_chat(user, "The door bolt lights have been enabled.") + update_icon() + + +// AI-CONTROLLED SLIP GENERATOR IN AI CORE + +/obj/machinery/ai_slipper/AICtrlClick(mob/living/silicon/ai/user) //Turns liquid dispenser on or off ToggleOn() /obj/machinery/ai_slipper/AIAltClick() //Dispenses liquid if on Activate() - -// -// Override AdjacentQuick for AltClicking -// - -/mob/living/silicon/ai/TurfAdjacent(var/turf/T) - return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) 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/alert.dm b/code/_onclick/hud/alert.dm index a93e65c762b..7b53bce22de 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -3,7 +3,7 @@ //PUBLIC - call these wherever you want -/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE) +/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim) /* Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already @@ -59,14 +59,16 @@ LAZYSET(alerts, category, alert) // This also creates the list if it doesn't exist if(client && hud_used) hud_used.reorganize_alerts() - alert.transform = matrix(32, 6, MATRIX_TRANSLATE) - animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) - if(alert.timeout) - spawn(alert.timeout) - if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout) - clear_alert(category) - alert.timeout = world.time + alert.timeout - world.tick_lag + if(!no_anim) + alert.transform = matrix(32, 6, MATRIX_TRANSLATE) + animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) + + var/timeout = timeout_override || alert.timeout + if(timeout) + addtimer(CALLBACK(alert, /obj/screen/alert/.proc/do_timeout, src, category), timeout) + alert.timeout = world.time + timeout - world.tick_lag + return alert // Proc to clear an existing alert. @@ -94,7 +96,6 @@ var/alerttooltipstyle = "" var/override_alerts = FALSE //If it is overriding other alerts of the same type - /obj/screen/alert/MouseEntered(location,control,params) openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle) @@ -102,6 +103,12 @@ /obj/screen/alert/MouseExited() closeToolTip(usr) +/obj/screen/alert/proc/do_timeout(mob/M, category) + if(!M || !M.alerts) + return + + if(timeout && M.alerts[category] == src && world.time >= timeout) + M.clear_alert(category) //Gas alerts /obj/screen/alert/not_enough_oxy @@ -507,6 +514,34 @@ so as to remain in compliance with the most up-to-date laws." timeout = 300 var/atom/target = null var/action = NOTIFY_JUMP + var/show_time_left = FALSE // If true you need to call START_PROCESSING manually + var/image/time_left_overlay // The last image showing the time left + var/datum/candidate_poll/poll // If set, on Click() it'll register the player as a candidate + +/obj/screen/alert/notify_action/process() + if(show_time_left) + var/timeleft = timeout - world.time + if(timeleft <= 0) + return PROCESS_KILL + + if(time_left_overlay) + overlays -= time_left_overlay + + var/obj/O = new + O.maptext = "[CEILING(timeleft / 10, 1)]" + O.maptext_width = O.maptext_height = 128 + var/matrix/M = new + M.Translate(4, 16) + O.transform = M + + var/image/I = image(O) + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + 1 + overlays += I + + time_left_overlay = I + qdel(O) + ..() /obj/screen/alert/notify_action/Destroy() target = null @@ -515,20 +550,55 @@ so as to remain in compliance with the most up-to-date laws." /obj/screen/alert/notify_action/Click() if(!usr || !usr.client) return - if(!target) - return var/mob/dead/observer/G = usr if(!istype(G)) return - switch(action) - if(NOTIFY_ATTACK) - target.attack_ghost(G) - if(NOTIFY_JUMP) - var/turf/T = get_turf(target) - if(T && isturf(T)) - G.loc = T - if(NOTIFY_FOLLOW) - G.ManualFollow(target) + + if(poll) + if(poll.sign_up(G)) + // Add a small overlay to indicate we've signed up + display_signed_up() + else if(target) + switch(action) + if(NOTIFY_ATTACK) + target.attack_ghost(G) + if(NOTIFY_JUMP) + var/turf/T = get_turf(target) + if(T && isturf(T)) + G.loc = T + if(NOTIFY_FOLLOW) + G.ManualFollow(target) + +/obj/screen/alert/notify_action/Topic(href, href_list) + if(..()) + return TRUE + + if(href_list["signup"] && isobserver(usr) && poll?.sign_up(usr)) + display_signed_up() + +/obj/screen/alert/notify_action/proc/display_signed_up() + var/image/I = image('icons/mob/screen_gen.dmi', icon_state = "selector") + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + 2 + overlays += I + +/obj/screen/alert/notify_action/proc/display_stacks(stacks = 1) + if(stacks <= 1) + return + + var/obj/O = new + O.maptext = "[stacks]x" + O.maptext_width = O.maptext_height = 128 + var/matrix/M = new + M.Translate(4, 2) + O.transform = M + + var/image/I = image(O) + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + 1 + overlays += I + + qdel(O) /obj/screen/alert/notify_soulstone name = "Soul Stone" 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/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index d628124e052..6c9e95f457e 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -13,7 +13,7 @@ using.icon_state = mymob.a_intent static_inventory += using action_intent = using - + using = new /obj/screen/guardian/Manifest() using.screen_loc = ui_rhand static_inventory += using @@ -49,8 +49,8 @@ /obj/screen/guardian/Manifest/Click() if(isguardian(usr)) var/mob/living/simple_animal/hostile/guardian/G = usr - G.Manifest() - + if(G.loc == G.summoner) + G.Manifest() /obj/screen/guardian/Recall icon_state = "recall" 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/ghost_spawns.dm b/code/controllers/subsystem/ghost_spawns.dm new file mode 100644 index 00000000000..ea0aefb88bd --- /dev/null +++ b/code/controllers/subsystem/ghost_spawns.dm @@ -0,0 +1,275 @@ +SUBSYSTEM_DEF(ghost_spawns) + name = "Ghost Spawns" + init_order = INIT_ORDER_EVENTS + flags = SS_BACKGROUND + wait = 1 SECONDS + runlevels = RUNLEVEL_GAME + offline_implications = "Ghosts will no longer be able to respawn as event mobs (Blob, etc..). Shuttle call recommended." + + /// List of polls currently ongoing, to be checked on next fire() + var/list/datum/candidate_poll/currently_polling + /// Whether there are active polls or not + var/polls_active = FALSE + /// Number of polls performed since the start + var/total_polls = 0 + /// The poll that's closest to finishing + var/datum/candidate_poll/next_poll_to_finish + +/datum/controller/subsystem/ghost_spawns/fire() + if(!polls_active) + return + if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still.. + currently_polling = list() + + for(var/poll in currently_polling) + var/datum/candidate_poll/P = poll + if(P.time_left() <= 0) + polling_finished(P) + +/** + * Polls for candidates with a question and a preview of the role + * + * This proc replaces /proc/pollCandidates. + * Should NEVER be used in a proc that has waitfor set to FALSE/0 (due to #define UNTIL) + * Arguments: + * * question - The question to ask to potential candidates + * * role - The role to poll for. Should be a ROLE_x enum. If set, potential candidates who aren't eligible will be ignored + * * antag_age_check - Whether to filter out potential candidates who don't have an old enough account + * * poll_time - How long to poll for in deciseconds + * * ignore_respawnability - Whether to ignore the player's respawnability + * * min_hours - The amount of hours needed for a potential candidate to be eligible + * * flash_window - Whether the poll should flash a potential candidate's game window + * * check_antaghud - Whether to filter out potential candidates who enabled AntagHUD + * * source - The atom, atom prototype, icon or mutable appearance to display as an icon in the alert + */ +/datum/controller/subsystem/ghost_spawns/proc/poll_candidates(question = "Would you like to play a special role?", role, antag_age_check = FALSE, poll_time = 30 SECONDS, ignore_respawnability = FALSE, min_hours = 0, flash_window = TRUE, check_antaghud = TRUE, source) + log_debug("Polling candidates [role ? "for [get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds") + + // Start firing + polls_active = TRUE + total_polls++ + + var/datum/candidate_poll/P = new(role, question, poll_time) + LAZYADD(currently_polling, P) + + // We're the poll closest to completion + if(!next_poll_to_finish || poll_time < next_poll_to_finish.time_left()) + next_poll_to_finish = P + + var/category = "[P.hash]_notify_action" + + for(var/mob/dead/observer/M in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) + if(!is_eligible(M, role, antag_age_check, role, min_hours, check_antaghud)) + continue + + SEND_SOUND(M, 'sound/misc/notice2.ogg') + if(flash_window) + window_flash(M.client) + + // If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one, + // we need to keep the first one's timeout rather than use the shorter one + var/obj/screen/alert/notify_action/current_alert = LAZYACCESS(M.alerts, category) + var/alert_time = poll_time + var/alert_poll = P + if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag)) + alert_time = current_alert.timeout - world.time + world.tick_lag + alert_poll = current_alert.poll + + // Send them an on-screen alert + var/obj/screen/alert/notify_action/A = M.throw_alert(category, /obj/screen/alert/notify_action, timeout_override = alert_time, no_anim = TRUE) + if(!A) + continue + + A.icon = ui_style2icon(M.client?.prefs.UI_style) + A.name = "Looking for candidates" + A.desc = "[question]\n\n(expires in [poll_time / 10] seconds)" + A.show_time_left = TRUE + A.poll = alert_poll + + // Sign up inheritance and stacking + var/inherited_sign_up = FALSE + var/num_stack = 1 + for(var/existing_poll in currently_polling) + var/datum/candidate_poll/P2 = existing_poll + if(P != P2 && P.hash == P2.hash) + // If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one + if(!inherited_sign_up && (M in P2.signed_up) && P.sign_up(M, TRUE)) + A.display_signed_up() + inherited_sign_up = TRUE + // This number is used to display the number of polls the alert regroups + num_stack++ + if(num_stack > 1) + A.display_stacks(num_stack) + + // Image to display + var/image/I + if(source) + if(!ispath(source)) + var/atom/S = source + var/old_layer = S.layer + var/old_plane = S.plane + + S.layer = FLOAT_LAYER + S.plane = FLOAT_PLANE + A.overlays += S + S.layer = old_layer + S.plane = old_plane + else + I = image(source, layer = FLOAT_LAYER, dir = SOUTH) + else + // Just use a generic image + I = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER, dir = SOUTH) + + if(I) + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + A.overlays += I + + // Chat message + var/act_jump = "" + if(isatom(source)) + act_jump = "\[Teleport]" + var/act_signup = "\[Sign Up]" + to_chat(M, "Now looking for candidates [role ? "to play as \an [get_roletext(role)]" : "\"[question]\""]. [act_jump] [act_signup]") + + // Start processing it so it updates visually the timer + START_PROCESSING(SSprocessing, A) + A.process() + + // Sleep until the time is up + UNTIL(P.finished) + return P.signed_up + +/** + * Returns whether an observer is eligible to be an event mob + * + * Arguments: + * * M - The mob to check eligibility + * * role - The role to check eligibility for. Checks 1. the client has enabled the role 2. the account's age for this role if antag_age_check is TRUE + * * antag_age_check - Whether to check the account's age or not for the given role. + * * role_text - The role's clean text. Used for checking job bans to determine eligibility + * * min_hours - The amount of minimum hours the client needs before being eligible + * * check_antaghud - Whether to consider a client who enabled AntagHUD ineligible or not + */ +/datum/controller/subsystem/ghost_spawns/proc/is_eligible(mob/M, role, antag_age_check, role_text, min_hours, check_antaghud) + . = FALSE + if(!M.key || !M.client) + return + if(role) + if(!(role in M.client.prefs.be_special)) + return + if(antag_age_check) + if(!player_old_enough_antag(M.client, role)) + return + if(role_text) + if(jobban_isbanned(M, role_text) || jobban_isbanned(M, "Syndicate")) + return + if(config.use_exp_restrictions && min_hours) + if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) + return + if(check_antaghud && cannotPossess(M)) + return + + return TRUE + +/** + * Called by the subsystem when a poll's timer runs out + * + * Can be called manually to finish a poll prematurely + * Arguments: + * * P - The poll to finish + */ +/datum/controller/subsystem/ghost_spawns/proc/polling_finished(datum/candidate_poll/P) + // Trim players who aren't eligible anymore + var/len_pre_trim = length(P.signed_up) + P.trim_candidates() + log_debug("Candidate poll [P.role ? "for [get_roletext(P.role)]" : "\"[P.question]\""] finished. [len_pre_trim] players signed up, [length(P.signed_up)] after trimming") + + P.finished = TRUE + currently_polling -= P + + // Determine which is the next poll closest the completion or "disable" firing if there's none + if(!length(currently_polling)) + polls_active = FALSE + next_poll_to_finish = null + else if(P == next_poll_to_finish) + next_poll_to_finish = null + for(var/poll in currently_polling) + var/datum/candidate_poll/P2 = poll + if(!next_poll_to_finish || P2.time_left() < next_poll_to_finish.time_left()) + next_poll_to_finish = P2 + +/datum/controller/subsystem/ghost_spawns/stat_entry(msg) + msg += "Active: [length(currently_polling)] | Total: [total_polls]" + if(next_poll_to_finish) + msg += " | Next: [DisplayTimeText(next_poll_to_finish.time_left())] ([length(next_poll_to_finish.signed_up)] candidates)" + ..(msg) + +// The datum that describes one instance of candidate polling +/datum/candidate_poll + var/role // The role the poll is for + var/question // The question asked to observers + var/duration // The duration of the poll + var/list/mob/dead/observer/signed_up // The players who signed up to this poll + var/time_started // The world.time at which the poll was created + var/finished = FALSE // Whether the polling is finished + var/hash // Used to categorize in the alerts system + +/datum/candidate_poll/New(polled_role, polled_question, poll_duration) + role = polled_role + question = polled_question + duration = poll_duration + signed_up = list() + time_started = world.time + hash = copytext(md5("[question]_[role ? role : "0"]"), 1, 7) + return ..() + +/** + * Attempts to sign a (controlled) mob up + * + * Will fail if the mob is already signed up or the poll's timer ran out. + * Does not check for eligibility + * Arguments: + * * M - The (controlled) mob to sign up + * * silent - Whether no messages should appear or not. If not TRUE, signing up to this poll will also sign the mob up for identical polls + */ +/datum/candidate_poll/proc/sign_up(mob/dead/observer/M, silent = FALSE) + . = FALSE + if(!istype(M) || !M.key || !M.client) + return + if(M in signed_up) + if(!silent) + to_chat(M, "You have already signed up for this!") + return + if(time_left() <= 0) + if(!silent) + to_chat(M, "Sorry, you were too late for the consideration!") + SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg') + return + + signed_up += M + if(!silent) + to_chat(M, "You have signed up for this role! A candidate will be picked randomly soon..") + // Sign them up for any other polls with the same mob type + for(var/existing_poll in SSghost_spawns.currently_polling) + var/datum/candidate_poll/P = existing_poll + if(src != P && hash == P.hash && !(M in P.signed_up)) + P.sign_up(M, TRUE) + + return TRUE + +/** + * Deletes any candidates who may have disconnected from the list + */ +/datum/candidate_poll/proc/trim_candidates() + listclearnulls(signed_up) + for(var/mob in signed_up) + var/mob/M = mob + if(!M.key || !M.client) + signed_up -= M + +/** + * Returns the time left for a poll + */ +/datum/candidate_poll/proc/time_left() + return duration - (world.time - time_started) diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index ef89317d0f5..3adfdf3972f 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -27,7 +27,7 @@ SUBSYSTEM_DEF(jobs) /datum/controller/subsystem/jobs/fire() if(!config.sql_enabled || !config.use_exp_tracking) return - update_exp(5,0) + INVOKE_ASYNC(GLOBAL_PROC, /.proc/update_exp, 5, 0) /datum/controller/subsystem/jobs/proc/SetupOccupations(var/list/faction = list("Station")) occupations = list() @@ -644,6 +644,27 @@ SUBSYSTEM_DEF(jobs) oldjobdatum.current_positions-- newjobdatum.current_positions++ +/datum/controller/subsystem/jobs/proc/notify_dept_head(jobtitle, antext) + // Used to notify the department head of jobtitle X that their employee was brigged, demoted or terminated + if(!jobtitle || !antext) + return + var/datum/job/tgt_job = GetJob(jobtitle) + if(!tgt_job) + return + if(!tgt_job.department_head[1]) + return + var/boss_title = tgt_job.department_head[1] + var/obj/item/pda/target_pda + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.ownrank == boss_title) + target_pda = check_pda + break + if(!target_pda) + return + var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) + if(PM && PM.can_receive()) + PM.notify("Automated Notification: \"[antext]\" (Unable to Reply)") + /datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom) var/record_html = "" 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/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 5d0a53c221e..e53c91ac20d 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -61,7 +61,7 @@ SUBSYSTEM_DEF(ticker) if(GAME_STATE_STARTUP) // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails round_start_time = world.time + (config.pregame_timestart * 10) - to_chat(world, "Welcome to the pre-game lobby!") + to_chat(world, "Welcome to the pre-game lobby!") to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") current_state = GAME_STATE_PREGAME fire() // TG says this is a good idea @@ -217,11 +217,11 @@ SUBSYSTEM_DEF(ticker) for(var/obj/effect/landmark/spacepod/random/R in L) qdel(R) - to_chat(world, "Enjoy the game!") + to_chat(world, "Enjoy the game!") world << sound('sound/AI/welcome.ogg')// Skie if(SSholiday.holidays) - to_chat(world, "and...") + to_chat(world, "and...") for(var/holidayname in SSholiday.holidays) var/datum/holiday/holiday = SSholiday.holidays[holidayname] to_chat(world, "

[holiday.greet()]

") diff --git a/code/controllers/subsystem/tickets/mentor_tickets.dm b/code/controllers/subsystem/tickets/mentor_tickets.dm index 3fd003418a4..af4ea2e914d 100644 --- a/code/controllers/subsystem/tickets/mentor_tickets.dm +++ b/code/controllers/subsystem/tickets/mentor_tickets.dm @@ -6,17 +6,23 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets /datum/controller/subsystem/tickets/mentor_tickets name = "Mentor Tickets" + offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed." ticket_system_name = "Mentor Tickets" ticket_name = "Mentor Ticket" span_class = "mentorhelp" + other_ticket_name = "Admin" + other_ticket_permission = R_ADMIN close_rights = R_MENTOR | R_ADMIN - offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed." - -/datum/controller/subsystem/tickets/mentor_tickets/message_staff(var/msg) - message_mentorTicket(msg) + rights_needed = R_MENTOR | R_ADMIN | R_MOD /datum/controller/subsystem/tickets/mentor_tickets/Initialize() close_messages = list("- [ticket_name] Closed -", "Please try to be as descriptive as possible in mentor helps. Mentors do not know the full situation you're in and need more information to give you a helpful response.", "Your [ticket_name] has now been closed.") return ..() + +/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg) + message_mentorTicket(msg) + +/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T) + SStickets.newTicket(T.clientName, T.content, T.title) diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm index 879ee35abee..64f91e96b6c 100644 --- a/code/controllers/subsystem/tickets/tickets.dm +++ b/code/controllers/subsystem/tickets/tickets.dm @@ -12,18 +12,23 @@ SUBSYSTEM_DEF(tickets) name = "Admin Tickets" - var/span_class = "adminticket" - var/ticket_system_name = "Admin Tickets" - var/ticket_name = "Admin Ticket" - var/close_rights = R_ADMIN - var/list/close_messages init_order = INIT_ORDER_TICKETS wait = 300 priority = FIRE_PRIORITY_TICKETS offline_implications = "Admin tickets will no longer be marked as stale. No immediate action is needed." - flags = SS_BACKGROUND + var/span_class = "adminticket" + var/ticket_system_name = "Admin Tickets" + var/ticket_name = "Admin Ticket" + var/close_rights = R_ADMIN + var/rights_needed = R_ADMIN | R_MOD + + /// The name of the other ticket type to convert to + var/other_ticket_name = "Mentor" + /// Which permission to look for when seeing if there is staff available for the other ticket type + var/other_ticket_permission = R_MENTOR + var/list/close_messages var/list/allTickets = list() //make it here because someone might ahelp before the system has initialized var/ticketCounter = 1 @@ -114,9 +119,37 @@ SUBSYSTEM_DEF(tickets) to_chat_safe(returnClient(N), "Your [ticket_name] has now been resolved.") return TRUE +/datum/controller/subsystem/tickets/proc/convert_to_other_ticket(ticketId) + if(!check_rights(rights_needed)) + return + if(alert("Are you sure to convert this ticket to an '[other_ticket_name]' ticket?",,"Yes","No") != "Yes") + return + if(!other_ticket_system_staff_check()) + return + var/datum/ticket/T = allTickets[ticketId] + convert_ticket(T) + +/datum/controller/subsystem/tickets/proc/other_ticket_system_staff_check() + var/list/staff = staff_countup(other_ticket_permission) + if(!staff[1]) + if(alert("No active staff online to answer the ticket. Are you sure you want to convert the ticket?",, "No", "Yes") != "Yes") + return FALSE + return TRUE + +/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T) + T.ticketState = TICKET_CLOSED + var/client/C = usr.client + to_chat_safe(T.clientName, list("[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.",\ + "Be sure to use the correct type of help next time!")) + message_staff("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.") + log_game("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.") + create_other_system_ticket(T) + +/datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T) + SSmentor_tickets.newTicket(T.clientName, T.content, T.title) /datum/controller/subsystem/tickets/proc/autoRespond(N) - if(!check_rights(R_ADMIN|R_MOD)) + if(!check_rights(rights_needed)) return var/datum/ticket/T = allTickets[N] @@ -158,14 +191,17 @@ SUBSYSTEM_DEF(tickets) resolveTicket(N) message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with: [message_key] ") log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]") + if("Mentorhelp") + convert_ticket(T) else var/msg_sound = sound('sound/effects/adminhelp.ogg') SEND_SOUND(returnClient(N), msg_sound) - to_chat(returnClient(N), "[key_name_hidden(C)] is autoresponding with: [response_phrases[message_key]]")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key] + to_chat_safe(returnClient(N), "[key_name_hidden(C)] is autoresponding with: [response_phrases[message_key]]")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key] message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with: [message_key] ") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key T.lastStaffResponse = "Autoresponse: [message_key]" resolveTicket(N) log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]") + //Set ticket state with key N to closed /datum/controller/subsystem/tickets/proc/closeTicket(N) var/datum/ticket/T = allTickets[N] @@ -353,7 +389,7 @@ UI STUFF dat += "" dat += "
[T.content[i]]


" - dat += "Re-Open[check_rights(R_ADMIN|R_MOD, 0) ? "Auto": ""]Resolve

" + dat += "Re-Open[check_rights(rights_needed, 0) ? "Auto": ""]Resolve

" if(!T.staffAssigned) dat += "No staff member assigned to this [ticket_name] - Take Ticket
" @@ -368,6 +404,7 @@ UI STUFF dat += "

" dat += "Close Ticket" + dat += "Convert Ticket" var/datum/browser/popup = new(user, "[ticket_system_name]detail", "[ticket_system_name] #[T.ticketNum]", 1000, 600) popup.set_content(dat) @@ -450,7 +487,6 @@ UI STUFF if(closeTicket(indexNum)) showDetailUI(usr, indexNum) - if(href_list["detailreopen"]) var/indexNum = text2num(href_list["detailreopen"]) if(openTicket(indexNum)) @@ -470,6 +506,10 @@ UI STUFF var/indexNum = text2num(href_list["autorespond"]) autoRespond(indexNum) + if(href_list["convert_ticket"]) + var/indexNum = text2num(href_list["convert_ticket"]) + convert_to_other_ticket(indexNum) + if(href_list["resolveall"]) if(ticket_system_name == "Mentor Tickets") usr.client.resolveAllMentorTickets() diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 3eaed85c303..8d02529ae6b 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -7,7 +7,7 @@ set name = "Restart Controller" set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" - if(!holder) + if(!check_rights(R_DEBUG)) return switch(controller) if("Master") @@ -20,13 +20,14 @@ 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" set desc = "Debug the various periodic loop controllers for the game (be careful!)" - if(!holder) return + if(!check_rights(R_DEBUG)) + return switch(controller) if("failsafe") debug_variables(Failsafe) @@ -64,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/caltrop.dm b/code/datums/components/caltrop.dm index 3bd740973a3..fc781b288a9 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -42,7 +42,7 @@ if(!(flags & CALTROP_BYPASS_SHOES) && (H.shoes || feetCover)) return - if((H.flying) || H.buckled) + if(H.flying || H.floating || H.buckled) return var/damage = rand(min_damage, max_damage) diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm index 88e51209afb..a7b81856468 100644 --- a/code/datums/components/slippery.dm +++ b/code/datums/components/slippery.dm @@ -51,6 +51,6 @@ Additionally calls the parent's `after_slip()` proc on the `victim`. */ /datum/component/slippery/proc/Slip(datum/source, mob/living/carbon/human/victim) - if(istype(victim) && prob(slip_chance) && victim.slip(description, stun, weaken, slip_tiles, walking_is_safe, slip_always, slip_verb)) + if(istype(victim) && !victim.flying && prob(slip_chance) && victim.slip(description, stun, weaken, slip_tiles, walking_is_safe, slip_always, slip_verb)) var/atom/movable/owner = parent owner.after_slip(victim) 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/components/squeak.dm b/code/datums/components/squeak.dm index 4c55ad2ad82..f79439f2b3b 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -67,6 +67,14 @@ var/obj/item/projectile/P = AM if(P.original != parent) return + if(ismob(AM)) + var/mob/M = AM + if(M.flying) + return + if(isliving(AM)) + var/mob/living/L = M + if(L.floating) + return var/atom/current_parent = parent if(isturf(current_parent.loc)) play_squeak() diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm index 7d127eedc7d..2a659477b47 100644 --- a/code/datums/log_record.dm +++ b/code/datums/log_record.dm @@ -3,8 +3,8 @@ var/raw_time // When did this happen? var/what // What happened var/who // Who did it - var/target // Who/what was targeted (can be a string) - var/turf/where // Where did it happen + var/target // Who/what was targeted + var/where // Where did it happen /datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time) log_type = _log_type @@ -12,9 +12,13 @@ who = get_subject_text(_who, _log_type) what = _what target = get_subject_text(_target, _log_type) - if(!_where) + if(!istext(_where) && !isturf(_where)) _where = get_turf(_who) - where = _where + if(isturf(_where)) + var/turf/T = _where + where = ADMIN_COORDJMP(T) + else + where = _where if(!_raw_time) _raw_time = world.time raw_time = _raw_time diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm index 72872e93a3c..c5a17507606 100644 --- a/code/datums/log_viewer.dm +++ b/code/datums/log_viewer.dm @@ -1,31 +1,45 @@ -#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG) +#define UPDATE_CKEY_MOB(__ckey) var/mob/result = selected_ckeys_mobs[__ckey];\ +if(!result || result.ckey != __ckey){\ + result = get_mob_by_ckey(__ckey);\ + selected_ckeys_mobs[__ckey] = result;\ +} + +#define RECORD_WARN_LIMIT 1000 +#define RECORD_HARD_LIMIT 2500 /datum/log_viewer var/time_from = 0 var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up - var/list/selected_mobs = list() // The mobs in question + var/list/selected_mobs = list() // The mobs in question. + var/list/selected_ckeys = list() // The ckeys selected to search for. Will show all mobs the ckey is attached to + var/list/mob/selected_ckeys_mobs = list() var/list/selected_log_types = ALL_LOGS // The log types being searched for - var/list/log_records = list() // Found and sorted records /datum/log_viewer/proc/clear_all() selected_mobs.Cut() selected_log_types = ALL_LOGS + selected_ckeys.Cut() + selected_ckeys_mobs.Cut() time_from = initial(time_from) time_to = initial(time_to) log_records.Cut() return -/datum/log_viewer/proc/search() +/datum/log_viewer/proc/search(user) log_records.Cut() // Empty the old results var/list/invalid_mobs = list() + var/list/ckeys = selected_ckeys.Copy() for(var/i in selected_mobs) var/mob/M = i - if(!M || QDELETED(M)) + if(!M || QDELETED(M) || !M.last_known_ckey) invalid_mobs |= M continue + ckeys |= M.last_known_ckey + + for(var/ckey in ckeys) for(var/log_type in selected_log_types) - var/list/logs = M.logs[log_type] + var/list/logs = GLOB.logging.get_logs_by_type(ckey, log_type) var/len_logs = length(logs) if(len_logs) var/start_index = get_earliest_log_index(logs) @@ -36,8 +50,8 @@ continue log_records.Add(logs.Copy(start_index, end_index + 1)) - if(invalid_mobs.len) - to_chat(usr, "The search criteria contained invalid mobs. They have been removed from the criteria.") + if(length(invalid_mobs)) + to_chat(user, "The search criteria contained invalid mobs. They have been removed from the criteria.") for(var/i in invalid_mobs) selected_mobs -= i // Cleanup @@ -92,16 +106,22 @@ return 0 /datum/log_viewer/proc/add_mobs(list/mob/mobs) - if(!mobs?.len) + if(!length(mobs)) return for(var/i in mobs) - var/mob/M = i - if(istype(M)) - selected_mobs |= M + add_mob(usr, i, FALSE) -/datum/log_viewer/proc/add_mob(mob/user, mob/M) +/datum/log_viewer/proc/add_ckey(mob/user, ckey) + if(!user || !ckey) + return + selected_ckeys |= ckey + UPDATE_CKEY_MOB(ckey) + show_ui(user) + +/datum/log_viewer/proc/add_mob(mob/user, mob/M, show_the_ui = TRUE) if(!M || !user) return + selected_mobs |= M show_ui(user) @@ -110,9 +130,9 @@ var/all_log_types = ALL_LOGS var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;" var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;" - var/dat + var/list/dat = list() dat += "" - dat += "
" + dat += "
" dat += "Time Search Range: [gameTimestamp(wtime = time_from)]" dat += " To: [gameTimestamp(wtime = time_to)]" dat += "
" @@ -125,19 +145,24 @@ continue dat += "[get_display_name(M)]" dat += "Add Mob" - dat += "Add Mob (by ckey)" dat += "Clear All Mobs" dat += "
" + dat += "Ckeys being used:" + for(var/ckey in selected_ckeys) + dat += "[get_ckey_name(ckey)]" + dat += "Add ckey" + dat += "Clear All ckeys" + dat += "
" + dat += "Log Types:" - for(var/i in all_log_types) - var/log_type = i + for(var/log_type in all_log_types) var/enabled = (log_type in selected_log_types) var/text var/style if(enabled) text = "[log_type]" - style = "background: [get_logtype_color(i)]" + style = "background: [get_logtype_color(log_type)]" else text = log_type @@ -153,7 +178,7 @@ var/tdStyleType = "width:80px; text-align:center;" var/tdStyleWho = "width:400px; text-align:center;" var/tdStyleWhere = "width:150px; text-align:center;" - dat += "
" + dat += "
" dat += "" dat += "" for(var/i in log_records) @@ -162,13 +187,12 @@ dat +="\ \ - " - + " dat += "
WhenTypeWhoWhatTargetWhere
[time][L.log_type][L.who][L.what][L.target][ADMIN_COORDJMP(L.where)]
[L.target][L.where]
" dat += "
" var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600) - popup.set_content(dat) + popup.set_content(dat.Join()) popup.open() /datum/log_viewer/Topic(href, href_list) @@ -197,6 +221,19 @@ return if(href_list["search"]) search(usr) + var/records_len = length(log_records) + if(records_len > RECORD_WARN_LIMIT) + var/datum/log_record/last_record = log_records[RECORD_WARN_LIMIT] + var/last_time = gameTimestamp(wtime = last_record.raw_time - 9.99) + var/answer = alert(usr, "More than [RECORD_WARN_LIMIT] records were found. continuing will take a long time. This won't cause much lag for the server. Time at the [RECORD_WARN_LIMIT]th record '[last_time]'", "Warning", "Continue", "Limit to [RECORD_WARN_LIMIT]", "Cancel") + if(answer == "Limit to [RECORD_WARN_LIMIT]") + log_records.Cut(RECORD_WARN_LIMIT) + else if(answer == "Cancel") + log_records.Cut() + else + if(records_len > RECORD_HARD_LIMIT) + to_chat(usr, "Record limit reached. Limiting to [RECORD_HARD_LIMIT].") + log_records.Cut(RECORD_HARD_LIMIT) show_ui(usr) return if(href_list["clear_all"]) @@ -207,15 +244,20 @@ selected_mobs.Cut() show_ui(usr) return + if(href_list["clear_ckeys"]) + selected_ckeys.Cut() + selected_ckeys_mobs.Cut() + show_ui(usr) + return if(href_list["add_mob"]) var/list/mobs = getpois(TRUE, TRUE) var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) A.on_close(CALLBACK(src, .proc/add_mob, usr)) return - if(href_list["add_mob_ckey"]) - var/list/mobs = get_assoc_mob_list_by_ckey() - var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", mobs) - A.on_close(CALLBACK(src, .proc/add_mob, usr)) + if(href_list["add_ckey"]) + var/list/ckeys = GLOB.logging.get_ckeys_logged() + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", ckeys) + A.on_close(CALLBACK(src, .proc/add_ckey, usr)) return if(href_list["remove_mob"]) var/mob/M = locate(href_list["remove_mob"]) @@ -223,6 +265,10 @@ selected_mobs -= M show_ui(usr) return + if(href_list["remove_ckey"]) + selected_ckeys -= href_list["remove_ckey"] + show_ui(usr) + return if(href_list["toggle_log_type"]) var/log_type = href_list["toggle_log_type"] if(log_type in selected_log_types) @@ -246,6 +292,12 @@ return "deepskyblue" if(MISC_LOG) return "gray" + if(DEADCHAT_LOG) + return "#cc00c6" + if(OOC_LOG) + return "#002eb8" + if(LOOC_LOG) + return "#6699CC" return "slategray" /datum/log_viewer/proc/get_display_name(mob/M) @@ -254,4 +306,14 @@ name = "[name] ([M.real_name])" if(isobserver(M)) name = "[name] (DEAD)" - return name + return "\[[M.last_known_ckey]\] [name]" + +/datum/log_viewer/proc/get_ckey_name(ckey) + UPDATE_CKEY_MOB(ckey) + var/mob/M = selected_ckeys_mobs[ckey] + + return get_display_name(M) + +#undef UPDATE_CKEY_MOB +#undef RECORD_WARN_LIMIT +#undef RECORD_HARD_LIMIT diff --git a/code/datums/logging.dm b/code/datums/logging.dm new file mode 100644 index 00000000000..bac26610e5e --- /dev/null +++ b/code/datums/logging.dm @@ -0,0 +1,47 @@ +/datum/logging + var/list/datum/log_record/logs = list() // Assoc list of assoc lists (ckey, (log_type, list/logs)) + +/datum/logging/proc/add_log(ckey, datum/log_record/log) + if(!ckey) + log_debug("GLOB.logging.add_log called with an invalid ckey") + return + + if(!logs[ckey]) + logs[ckey] = list() + + var/list/log_types_list = logs[ckey] + + if(!log_types_list[log.log_type]) + log_types_list[log.log_type] = list() + + var/list/datum/log_record/log_records = log_types_list[log.log_type] + log_records.Add(log) + +/datum/logging/proc/get_ckeys_logged() + var/list/ckeys = list() + for(var/ckey in logs) + ckeys.Add(ckey) + return ckeys + +/* Returns the logs of a given ckey and log_type + * If no logs exist it will return an empty list +*/ +/datum/logging/proc/get_logs_by_type(ckey, log_type) + if(!ckey) + log_debug("GLOB.logging.get_logs_by_type called with an invalid ckey") + return + if(!log_type || !(log_type in ALL_LOGS)) + log_debug("GLOB.logging.get_logs_by_type called with an invalid log_type '[log_type]'") + return + + var/list/log_types_list = logs[ckey] + // Check if logs exist for the ckey + if(!length(log_types_list)) + return list() + + var/list/datum/log_record/log_records = log_types_list[log_type] + + // Check if logs exist for this type + if(!log_records) + return list() + return log_records 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/mind.dm b/code/datums/mind.dm index 103865bee94..0c992bbb32c 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -100,14 +100,6 @@ current.mind = null leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it - for(var/log_type in current.logs) // Copy the old logs - var/list/logs = current.logs[log_type] - if(new_character.logs[log_type]) - new_character.logs[log_type] += logs.Copy() // Append the old ones - new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time - else - new_character.logs[log_type] = logs.Copy() // Just copy them - SSnanoui.user_transferred(current, new_character) if(new_character.mind) //remove any mind currently in our new body's mind variable diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 14bc959fbb1..2b48d5da5f7 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -222,10 +222,10 @@ name = "NT Undercover Operative" // Disguised NT special forces, sent to quietly eliminate or keep tabs on people in high positions (e.g: captain) - uniform = /obj/item/clothing/under/color/black + uniform = /obj/item/clothing/under/color/random back = /obj/item/storage/backpack belt = /obj/item/storage/belt/utility/full/multitool - gloves = /obj/item/clothing/gloves/combat + gloves = /obj/item/clothing/gloves/color/yellow shoes = /obj/item/clothing/shoes/chameleon/noslip l_ear = /obj/item/radio/headset/centcom id = /obj/item/card/id 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/spell.dm b/code/datums/spell.dm index 3855dbcfac3..cb10700e5c6 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -444,6 +444,11 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) return +// Normally, AoE spells will generate an attack log for every turf they loop over, while searching for targets. +// With this override, all /aoe_turf type spells will only generate 1 log, saying that the user has cast the spell. +/obj/effect/proc_holder/spell/aoe_turf/perform(list/targets, recharge, mob/user, make_attack_logs) + add_attack_logs(user, null, "Cast the AoE spell [name]", ATKLOG_ALL) + return ..(targets, recharge, user, FALSE) /obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) //Checks for obstacles from A to B diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm index 88f5eec9474..e4c652ae1d3 100644 --- a/code/datums/spells/knock.dm +++ b/code/datums/spells/knock.dm @@ -13,11 +13,6 @@ action_icon_state = "knock" sound = 'sound/magic/knock.ogg' -// Knock doesn't need to generate an attack log for every turf, set `make_attack_logs` to FALSE and just create a custom one. -/obj/effect/proc_holder/spell/aoe_turf/knock/perform(list/targets, recharge, mob/user) - add_attack_logs(user, user, "cast the spell [name]", ATKLOG_ALL) - return ..(targets, recharge, user, make_attack_logs = FALSE) - /obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) for(var/turf/T in targets) for(var/obj/machinery/door/door in T.contents) diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 5b21036b2bc..c8f2b6bb99b 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -1173,11 +1173,10 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY containername = "fox crate" /datum/supply_packs/organic/butterfly - name = "Butterflies Crate" + name = "Butterfly Crate" cost = 50 containertype = /obj/structure/closet/critter/butterfly - containername = "butterflies crate" - contraband = 1 + containername = "butterfly crate" /datum/supply_packs/organic/deer name = "Deer Crate" 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/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm index 9f98a481dbf..0233e0aee71 100644 --- a/code/datums/weather/weather_types/floor_is_lava.dm +++ b/code/datums/weather/weather_types/floor_is_lava.dm @@ -35,6 +35,8 @@ return if(!L.client) //Only sentient people are going along with it! return + if(L.flying) + return L.adjustFireLoss(3) /datum/weather/floor_is_lava/fake 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/defines/vox_sounds.dm b/code/defines/vox_sounds.dm index 1d7bb9753a0..5b44f80895b 100644 --- a/code/defines/vox_sounds.dm +++ b/code/defines/vox_sounds.dm @@ -1,7 +1,16 @@ // List is required to compile the resources into the game when it loads. // Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable. -GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg', +GLOBAL_LIST_INIT(vox_alerts, list( +"bizwarn" = 'sound/vox_fem/bizwarn.ogg', +"bloop" = 'sound/vox_fem/bloop.ogg', +"buzwarn" = 'sound/vox_fem/buzwarn.ogg', +"dadeda" = 'sound/vox_fem/dadeda.ogg', +"deeoo" = 'sound/vox_fem/deeoo.ogg' +)) + +GLOBAL_LIST_INIT(vox_sounds, list( +"," = 'sound/vox_fem/,.ogg', "." = 'sound/vox_fem/..ogg', "a" = 'sound/vox_fem/a.ogg', "abortions" = 'sound/vox_fem/abortions.ogg', 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/atoms_movable.dm b/code/game/atoms_movable.dm index 00009c8eeca..d463c85eaf6 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -61,7 +61,7 @@ /atom/movable/proc/get_cell() return -/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/atom/movable/proc/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) if(QDELETED(AM)) return FALSE if(!(AM.can_be_pulled(src, state, force))) @@ -87,7 +87,7 @@ if(ismob(AM)) var/mob/M = AM add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) - if(!supress_message) + if(show_message) visible_message("[src] has grabbed [M] passively!") return TRUE @@ -120,12 +120,18 @@ if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. pulledby.stop_pulling() -/atom/movable/proc/can_be_pulled(user, grab_state, force) +/atom/movable/proc/can_be_pulled(user, grab_state, force, show_message = FALSE) if(src == user || !isturf(loc)) return FALSE - if(anchored || throwing) + if(anchored || move_resist == INFINITY) + if(show_message) + to_chat(user, "[src] appears to be anchored to the ground!") + return FALSE + if(throwing) return FALSE if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) + if(show_message) + to_chat(user, "[src] is too heavy to pull!") return FALSE return TRUE @@ -506,6 +512,7 @@ return //don't do an animation if attacking self var/pixel_x_diff = 0 var/pixel_y_diff = 0 + var/direction = get_dir(src, A) if(direction & NORTH) pixel_y_diff = 8 @@ -525,7 +532,8 @@ if(visual_effect_icon) I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) else if(used_item) - I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) + I = image(icon = used_item, loc = A, layer = A.layer + 0.1) + I.plane = GAME_PLANE // Scale the icon. I.transform *= 0.75 diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index d887153c6d5..3158cf90303 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -7,6 +7,11 @@ #define NEGATE_MUTATION_THRESHOLD 30 // Occupants with over ## percent radiation threshold will not gain mutations +#define PAGE_UI "ui" +#define PAGE_SE "se" +#define PAGE_BUFFER "buffer" +#define PAGE_REJUVENATORS "rejuvenators" + //list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0), /datum/dna2/record var/datum/dna/dna = null @@ -161,6 +166,7 @@ occupant = usr icon_state = "scanner_occupied" add_fingerprint(usr) + SStgui.update_uis(src) /obj/machinery/dna_scannernew/MouseDrop_T(atom/movable/O, mob/user) if(!istype(O)) @@ -214,6 +220,7 @@ return beaker = I + SStgui.update_uis(src) I.forceMove(src) user.visible_message("[user] adds \a [I] to \the [src]!", "You add \a [I] to \the [src]!") return @@ -260,6 +267,7 @@ M.forceMove(src) occupant = M icon_state = "scanner_occupied" + SStgui.update_uis(src) // search for ghosts, if the corpse is empty and the scanner is connected to a cloner if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \ @@ -281,6 +289,7 @@ occupant.forceMove(loc) occupant = null icon_state = "scanner_open" + SStgui.update_uis(src) /obj/machinery/dna_scannernew/force_eject_occupant() go_out(null, TRUE) @@ -296,6 +305,7 @@ occupant = null updateUsrDialog() update_icon() + SStgui.update_uis(src) // Checks if occupants can be irradiated/mutated - prevents exploits where wearing full rad protection would still let you gain mutations /obj/machinery/dna_scannernew/proc/radiation_check() @@ -333,12 +343,11 @@ var/injector_ready = FALSE //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete var/obj/machinery/dna_scannernew/connected = null var/obj/item/disk/data/disk = null - var/selected_menu_key = null + var/selected_menu_key = PAGE_UI anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 10 active_power_usage = 400 - var/waiting_for_user_input = 0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X /obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS @@ -347,7 +356,7 @@ I.forceMove(src) disk = I to_chat(user, "You insert [I].") - SSnanoui.update_uis(src) // update all UIs attached to src() + SStgui.update_uis(src) return else return ..() @@ -399,35 +408,18 @@ if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_interact(user) - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui - * - * @return nothing - */ -/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) +/obj/machinery/computer/scan_consolenew/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) if(user == connected.occupant) 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) + 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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "dna_modifier.tmpl", "DNA Modifier Console", 660, 700) - // open the new ui window + ui = new(user, src, ui_key, "DNAModifier", name, 660, 700, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/computer/scan_consolenew/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/computer/scan_consolenew/tgui_data(mob/user) var/data[0] data["selectedMenuKey"] = selected_menu_key data["locked"] = connected.locked @@ -501,9 +493,12 @@ for(var/datum/reagent/R in connected.beaker.reagents.reagent_list) data["beakerVolume"] += R.volume + // Transfer modal information if there is one + data["modal"] = tgui_modal_data(src) + return data -/obj/machinery/computer/scan_consolenew/Topic(href, href_list) +/obj/machinery/computer/scan_consolenew/tgui_act(action, params) if(..()) return FALSE // don't update uis if(!istype(usr.loc, /turf)) @@ -512,405 +507,350 @@ return FALSE // don't update uis if(irradiating) // Make sure that it isn't already irradiating someone... return FALSE // don't update uis + if(stat & (NOPOWER|BROKEN)) + return add_fingerprint(usr) - if(href_list["selectMenuKey"]) - selected_menu_key = href_list["selectMenuKey"] - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["toggleLock"]) - if((connected && connected.occupant)) - connected.locked = !(connected.locked) - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["pulseRadiation"]) - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(!connected.occupant) - return TRUE // return 1 forces an update to all Nano uis attached to src - - var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - if(connected.radiation_check()) - return TRUE - - if(prob(95)) - if(prob(75)) - randmutb(connected.occupant) - else - randmuti(connected.occupant) - else - if(prob(95)) - randmutg(connected.occupant) - else - randmuti(connected.occupant) - - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["radiationDuration"]) - if(text2num(href_list["radiationDuration"]) > 0) - if(radiation_duration < 20) - radiation_duration += 2 - else - if(radiation_duration > 2) - radiation_duration -= 2 - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["radiationIntensity"]) - if(text2num(href_list["radiationIntensity"]) > 0) - if(radiation_intensity < 10) - radiation_intensity++ - else - if(radiation_intensity > 1) - radiation_intensity-- - return TRUE // return 1 forces an update to all Nano uis attached to src - - //////////////////////////////////////////////////////// - - if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) > 0) - if(selected_ui_target < 15) - selected_ui_target++ - selected_ui_target_hex = selected_ui_target - switch(selected_ui_target) - if(10) - selected_ui_target_hex = "A" - if(11) - selected_ui_target_hex = "B" - if(12) - selected_ui_target_hex = "C" - if(13) - selected_ui_target_hex = "D" - if(14) - selected_ui_target_hex = "E" - if(15) - selected_ui_target_hex = "F" - else - selected_ui_target = 0 - selected_ui_target_hex = 0 - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) < 1) - if(selected_ui_target > 0) - selected_ui_target-- - selected_ui_target_hex = selected_ui_target - switch(selected_ui_target) - if(10) - selected_ui_target_hex = "A" - if(11) - selected_ui_target_hex = "B" - if(12) - selected_ui_target_hex = "C" - if(13) - selected_ui_target_hex = "D" - if(14) - selected_ui_target_hex = "E" - else - selected_ui_target = 15 - selected_ui_target_hex = "F" - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["selectUIBlock"] && href_list["selectUISubblock"]) // This chunk of code updates selected block / sub-block based on click - var/select_block = text2num(href_list["selectUIBlock"]) - var/select_subblock = text2num(href_list["selectUISubblock"]) - if((select_block <= DNA_UI_LENGTH) && (select_block >= 1)) - selected_ui_block = select_block - if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1)) - selected_ui_subblock = select_subblock - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["pulseUIRadiation"]) - var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock) - - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(!connected.occupant) - return TRUE - - if(prob((80 + (radiation_duration / 2)))) - var/radiation = (radiation_intensity + radiation_duration) - connected.occupant.apply_effect(radiation,IRRADIATE,0) - - if(connected.radiation_check()) - return TRUE - - block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration) - connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block) - connected.occupant.UpdateAppearance() - else - var/radiation = ((radiation_intensity * 2) + radiation_duration) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - if(connected.radiation_check()) - return TRUE - - if(prob(20 + radiation_intensity)) - randmutb(connected.occupant) - domutcheck(connected.occupant, connected) - else - randmuti(connected.occupant) - connected.occupant.UpdateAppearance() - return TRUE // return 1 forces an update to all Nano uis attached to src - - //////////////////////////////////////////////////////// - - if(href_list["injectRejuvenators"]) - if(!connected.occupant) - return FALSE - var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5 - if(inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking - inject_amount = 0 - if(inject_amount > 50) - inject_amount = 50 - connected.beaker.reagents.trans_to(connected.occupant, inject_amount) - connected.beaker.reagents.reaction(connected.occupant) - return TRUE // return 1 forces an update to all Nano uis attached to src - - //////////////////////////////////////////////////////// - - if(href_list["selectSEBlock"] && href_list["selectSESubblock"]) // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes) - var/select_block = text2num(href_list["selectSEBlock"]) - var/select_subblock = text2num(href_list["selectSESubblock"]) - if((select_block <= DNA_SE_LENGTH) && (select_block >= 1)) - selected_se_block = select_block - if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1)) - selected_se_subblock = select_subblock - //testing("User selected block [selected_se_block] (sent [select_block]), subblock [selected_se_subblock] (sent [select_block]).") - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["pulseSERadiation"]) - var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock) - //var/original_block=block - //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...") - - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(connected.occupant) - if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3))))) - var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - - if(connected.radiation_check()) - return 1 - - var/real_SE_block=selected_se_block - block = miniscramble(block, radiation_intensity, radiation_duration) - if(prob(20)) - if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2) - real_SE_block++ - else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH) - real_SE_block-- - - //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!") - connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block) - domutcheck(connected.occupant, connected) - else - var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - - if(connected.radiation_check()) - return 1 - - if(prob(80 - radiation_duration)) - //testing("Random bad mut!") - randmutb(connected.occupant) - domutcheck(connected.occupant, connected) - else - randmuti(connected.occupant) - //testing("Random identity mut!") - connected.occupant.UpdateAppearance() - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["ejectBeaker"]) - if(connected.beaker) - var/obj/item/reagent_containers/glass/B = connected.beaker - B.forceMove(connected.loc) - connected.beaker = null + if(tgui_act_modal(action, params)) return TRUE - if(href_list["ejectOccupant"]) - connected.eject_occupant(usr) - return TRUE - - // Transfer Buffer Management - if(href_list["bufferOption"]) - var/bufferOption = href_list["bufferOption"] - - // These bufferOptions do not require a bufferId - if(bufferOption == "wipeDisk") - if((isnull(disk)) || (disk.read_only)) - //temphtml = "Invalid disk. Please try again." - return FALSE - - disk.buf = null - //temphtml = "Data saved." - return TRUE - - if(bufferOption == "ejectDisk") - if(!disk) + . = TRUE + switch(action) + if("selectMenuKey") + var/key = params["key"] + if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS))) return - disk.forceMove(get_turf(src)) - disk = null - return TRUE - - // All bufferOptions from here on require a bufferId - if(!href_list["bufferId"]) - return FALSE - - var/bufferId = text2num(href_list["bufferId"]) - - if(bufferId < 1 || bufferId > 3) - return FALSE // Not a valid buffer id - - if(bufferOption == "saveUI") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf = new - databuf.types = DNA2_BUF_UI // DNA2_BUF_UE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.name - databuf.name = "Unique Identifier" - buffers[bufferId] = databuf - return TRUE - - if(bufferOption == "saveUIAndUE") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf = new - databuf.types = DNA2_BUF_UI|DNA2_BUF_UE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.dna.real_name - databuf.name = "Unique Identifier + Unique Enzymes" - buffers[bufferId] = databuf - return TRUE - - if(bufferOption == "saveSE") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf = new - databuf.types = DNA2_BUF_SE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - databuf.dna.real_name = connected.occupant.dna.real_name - databuf.name = "Structural Enzymes" - buffers[bufferId] = databuf - return TRUE - - if(bufferOption == "clear") - buffers[bufferId] = new /datum/dna2/record() - return TRUE - - if(bufferOption == "changeLabel") - var/datum/dna2/record/buf = buffers[bufferId] - var/text = sanitize(input(usr, "New Label:", "Edit Label", buf.name) as text|null) - buf.name = text - buffers[bufferId] = buf - return TRUE - - if(bufferOption == "transfer") - if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna) - return TRUE - - irradiating = 2 + selected_menu_key = key + if("toggleLock") + if(connected && connected.occupant) + connected.locked = !(connected.locked) + if("pulseRadiation") + irradiating = radiation_duration var/lock_state = connected.locked connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - sleep(2 SECONDS) + SStgui.update_uis(src) + sleep(10 * radiation_duration) // sleep for radiation_duration seconds irradiating = 0 connected.locked = lock_state - var/radiation = (rand(20,50) / connected.damage_coeff) + if(!connected.occupant) + return + + var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff) connected.occupant.apply_effect(radiation, IRRADIATE, 0) - if(connected.radiation_check()) - return TRUE + return - var/datum/dna2/record/buf = buffers[bufferId] - - if((buf.types & DNA2_BUF_UI)) - if((buf.types & DNA2_BUF_UE)) - connected.occupant.real_name = buf.dna.real_name - connected.occupant.name = buf.dna.real_name - connected.occupant.UpdateAppearance(buf.dna.UI.Copy()) - else if(buf.types & DNA2_BUF_SE) - connected.occupant.dna.SE = buf.dna.SE.Copy() - connected.occupant.dna.UpdateSE() - domutcheck(connected.occupant, connected) - return TRUE - - if(bufferOption == "createInjector") - if(injector_ready && !waiting_for_user_input) - - var/success = 1 - var/obj/item/dnainjector/I = new /obj/item/dnainjector - var/datum/dna2/record/buf = buffers[bufferId] - buf = buf.copy() - if(href_list["createBlockInjector"]) - waiting_for_user_input=1 - var/list/selectedbuf - if(buf.types & DNA2_BUF_SE) - selectedbuf=buf.dna.SE - else - selectedbuf=buf.dna.UI - var/blk = input(usr,"Select Block","Block") as null|anything in all_dna_blocks(selectedbuf) - success = setInjectorBlock(I,blk,buf) + if(prob(95)) + if(prob(75)) + randmutb(connected.occupant) else - I.buf = buf - waiting_for_user_input = 0 - if(success) - I.forceMove(loc) - I.name += " ([buf.name])" - if(connected) - I.damage_coeff = connected.damage_coeff - injector_ready = FALSE - spawn(300) - injector_ready = TRUE - return TRUE + randmuti(connected.occupant) + else + if(prob(95)) + randmutg(connected.occupant) + else + randmuti(connected.occupant) + if("radiationDuration") + radiation_duration = clamp(text2num(params["value"]), 1, 20) + if("radiationIntensity") + radiation_intensity = clamp(text2num(params["value"]), 1, 10) + //////////////////////////////////////////////////////// + if("changeUITarget") + selected_ui_target = clamp(text2num(params["value"]), 1, 15) + selected_ui_target_hex = num2text(selected_ui_target, 1, 16) + if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click + var/select_block = text2num(params["block"]) + var/select_subblock = text2num(params["subblock"]) + if(!select_block || !select_subblock) + return - if(bufferOption == "loadDisk") - if((isnull(disk)) || (!disk.buf)) - //temphtml = "Invalid disk. Please try again." - return FALSE + selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH) + selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) + if("pulseUIRadiation") + var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock) - buffers[bufferId] = disk.buf.copy() - //temphtml = "Data loaded." - return TRUE + irradiating = radiation_duration + var/lock_state = connected.locked + connected.locked = TRUE //lock it - if(bufferOption == "saveDisk") - if((isnull(disk)) || (disk.read_only)) - //temphtml = "Invalid disk. Please try again." - return FALSE + SStgui.update_uis(src) + sleep(10 * radiation_duration) // sleep for radiation_duration seconds - var/datum/dna2/record/buf = buffers[bufferId] + irradiating = 0 + connected.locked = lock_state - disk.buf = buf.copy() - disk.name = "data disk - '[buf.dna.real_name]'" - //temphtml = "Data saved." - return TRUE + if(!connected.occupant) + return + if(prob((80 + (radiation_duration / 2)))) + var/radiation = (radiation_intensity + radiation_duration) + connected.occupant.apply_effect(radiation,IRRADIATE,0) + + if(connected.radiation_check()) + return + + block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration) + connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block) + connected.occupant.UpdateAppearance() + else + var/radiation = ((radiation_intensity * 2) + radiation_duration) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + if(connected.radiation_check()) + return + + if(prob(20 + radiation_intensity)) + randmutb(connected.occupant) + domutcheck(connected.occupant, connected) + else + randmuti(connected.occupant) + connected.occupant.UpdateAppearance() + //////////////////////////////////////////////////////// + if("injectRejuvenators") + if(!connected.occupant || !connected.beaker) + return + var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50 + if(!inject_amount) + return + connected.beaker.reagents.trans_to(connected.occupant, inject_amount) + connected.beaker.reagents.reaction(connected.occupant) + //////////////////////////////////////////////////////// + if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes) + var/select_block = text2num(params["block"]) + var/select_subblock = text2num(params["subblock"]) + if(!select_block || !select_subblock) + return + + selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH) + selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) + if("pulseSERadiation") + var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock) + //var/original_block=block + //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...") + + irradiating = radiation_duration + var/lock_state = connected.locked + connected.locked = TRUE //lock it + + SStgui.update_uis(src) + sleep(10 * radiation_duration) // sleep for radiation_duration seconds + + irradiating = 0 + connected.locked = lock_state + + if(connected.occupant) + if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3))))) + var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + + if(connected.radiation_check()) + return 1 + + var/real_SE_block=selected_se_block + block = miniscramble(block, radiation_intensity, radiation_duration) + if(prob(20)) + if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2) + real_SE_block++ + else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH) + real_SE_block-- + + //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!") + connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block) + domutcheck(connected.occupant, connected) + else + var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + + if(connected.radiation_check()) + return + + if(prob(80 - radiation_duration)) + //testing("Random bad mut!") + randmutb(connected.occupant) + domutcheck(connected.occupant, connected) + else + randmuti(connected.occupant) + //testing("Random identity mut!") + connected.occupant.UpdateAppearance() + if("ejectBeaker") + if(connected.beaker) + var/obj/item/reagent_containers/glass/B = connected.beaker + B.forceMove(connected.loc) + connected.beaker = null + if("ejectOccupant") + connected.eject_occupant() + // Transfer Buffer Management + if("bufferOption") + var/bufferOption = params["option"] + var/bufferId = text2num(params["id"]) + if(bufferId < 1 || bufferId > 3) // Not a valid buffer id + return + + var/datum/dna2/record/buffer = buffers[bufferId] + switch(bufferOption) + if("saveUI") + if(connected.occupant && connected.occupant.dna) + var/datum/dna2/record/databuf = new + databuf.types = DNA2_BUF_UI // DNA2_BUF_UE + databuf.dna = connected.occupant.dna.Clone() + if(ishuman(connected.occupant)) + databuf.dna.real_name=connected.occupant.name + databuf.name = "Unique Identifier" + buffers[bufferId] = databuf + if("saveUIAndUE") + if(connected.occupant && connected.occupant.dna) + var/datum/dna2/record/databuf = new + databuf.types = DNA2_BUF_UI|DNA2_BUF_UE + databuf.dna = connected.occupant.dna.Clone() + if(ishuman(connected.occupant)) + databuf.dna.real_name=connected.occupant.dna.real_name + databuf.name = "Unique Identifier + Unique Enzymes" + buffers[bufferId] = databuf + if("saveSE") + if(connected.occupant && connected.occupant.dna) + var/datum/dna2/record/databuf = new + databuf.types = DNA2_BUF_SE + databuf.dna = connected.occupant.dna.Clone() + if(ishuman(connected.occupant)) + databuf.dna.real_name = connected.occupant.dna.real_name + databuf.name = "Structural Enzymes" + buffers[bufferId] = databuf + if("clear") + buffers[bufferId] = new /datum/dna2/record() + if("changeLabel") + tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME) + if("transfer") + if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna) + return + + irradiating = 2 + var/lock_state = connected.locked + connected.locked = TRUE //lock it + + SStgui.update_uis(src) + sleep(2 SECONDS) + + irradiating = 0 + connected.locked = lock_state + + var/radiation = (rand(20,50) / connected.damage_coeff) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + + if(connected.radiation_check()) + return + + var/datum/dna2/record/buf = buffers[bufferId] + + if((buf.types & DNA2_BUF_UI)) + if((buf.types & DNA2_BUF_UE)) + connected.occupant.real_name = buf.dna.real_name + connected.occupant.name = buf.dna.real_name + connected.occupant.UpdateAppearance(buf.dna.UI.Copy()) + else if(buf.types & DNA2_BUF_SE) + connected.occupant.dna.SE = buf.dna.SE.Copy() + connected.occupant.dna.UpdateSE() + domutcheck(connected.occupant, connected) + if("createInjector") + if(!injector_ready) + return + if(text2num(params["block"]) > 0) + var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI) + tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices) + else + create_injector(bufferId, TRUE) + if("loadDisk") + if(isnull(disk) || disk.read_only) + return + buffers[bufferId] = disk.buf.copy() + if("saveDisk") + if(isnull(disk) || disk.read_only) + return + var/datum/dna2/record/buf = buffers[bufferId] + disk.buf = buf.copy() + disk.name = "data disk - '[buf.dna.real_name]'" + if("wipeDisk") + if(isnull(disk) || disk.read_only) + return + disk.buf = null + if("ejectDisk") + if(!disk) + return + disk.forceMove(get_turf(src)) + disk = null + +/** + * Creates a blank injector with the name of the buffer at the given buffer_id + * + * Arguments: + * * buffer_id - The ID of the buffer + * * copy_buffer - Whether the injector should copy the buffer contents + */ +/obj/machinery/computer/scan_consolenew/proc/create_injector(buffer_id, copy_buffer = FALSE) + if(buffer_id < 1 || buffer_id > length(buffers)) + return + + // Cooldown + injector_ready = FALSE + addtimer(CALLBACK(src, .proc/injector_cooldown_finish), 30 SECONDS) + + // Create it + var/datum/dna2/record/buf = buffers[buffer_id] + var/obj/item/dnainjector/I = new() + I.forceMove(loc) + I.name += " ([buf.name])" + if(copy_buffer) + I.buf = buf.copy() + if(connected) + I.damage_coeff = connected.damage_coeff + return I + +/** + * Called when the injector creation cooldown finishes + */ +/obj/machinery/computer/scan_consolenew/proc/injector_cooldown_finish() + injector_ready = TRUE + +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/scan_consolenew/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("createInjectorBlock") + var/buffer_id = text2num(arguments["id"]) + if(buffer_id < 1 || buffer_id > length(buffers)) + return + var/datum/dna2/record/buf = buffers[buffer_id] + var/obj/item/dnainjector/I = create_injector(buffer_id) + setInjectorBlock(I, answer, buf.copy()) + if("changeBufferLabel") + var/buffer_id = text2num(arguments["id"]) + if(buffer_id < 1 || buffer_id > length(buffers)) + return + var/datum/dna2/record/buf = buffers[buffer_id] + buf.name = answer + buffers[buffer_id] = buf + else + return FALSE + else + return FALSE + + +#undef PAGE_UI +#undef PAGE_SE +#undef PAGE_BUFFER +#undef PAGE_REJUVENATORS /////////////////////////// DNA MACHINES 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/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index 46b1972e565..34c717f11a8 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -7,6 +7,7 @@ /mob/living/simple_animal/hostile/blob icon = 'icons/mob/blob.dmi' pass_flags = PASSBLOB + status_flags = NONE //No throwing blobspores into deep space to despawn, or throwing blobbernaughts, which are bigger than you. faction = list(ROLE_BLOB) bubble_icon = "blob" atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) @@ -49,6 +50,7 @@ environment_smash = ENVIRONMENT_SMASH_STRUCTURES attacktext = "hits" attack_sound = 'sound/weapons/genhit1.ogg' + flying = TRUE speak_emote = list("pulses") var/obj/structure/blob/factory/factory = null var/list/human_overlays = list() diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index be794b9092b..dc8d0f5aba2 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -17,12 +17,12 @@ START_PROCESSING(SSobj, src) GLOB.poi_list |= src adjustcolors(color) //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(overmind) - adjustcolors(overmind.blob_reagent_datum.color) if(offspring) is_offspring = 1 + if(overmind) + adjustcolors(overmind.blob_reagent_datum.color) + if(!overmind) + create_overmind(new_overmind) point_rate = new_rate ..(loc, h) @@ -104,10 +104,11 @@ var/mob/C = null var/list/candidates = list() if(!new_overmind) + // sendit if(is_offspring) - candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) + candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = src) else - candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) + candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = src) if(length(candidates)) C = pick(candidates) diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm index 6f2eed39499..350da7c7593 100644 --- a/code/game/gamemodes/blob/powers.dm +++ b/code/game/gamemodes/blob/powers.dm @@ -237,7 +237,7 @@ blobber.AIStatus = AI_OFF blobber.LoseTarget() spawn() - var/list/candidates = pollCandidates("Do you want to play as a blobbernaut?", ROLE_BLOB, 1, 100) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber) if(candidates.len) var/mob/C = pick(candidates) if(C) @@ -389,7 +389,7 @@ return split_used = TRUE - new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, "offspring") + new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, offspring = TRUE) qdel(N) if(SSticker && SSticker.mode.name == "blob") 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/changeling/powers/swap_form.dm b/code/game/gamemodes/changeling/powers/swap_form.dm index 0e02df12d88..97192c86a0f 100644 --- a/code/game/gamemodes/changeling/powers/swap_form.dm +++ b/code/game/gamemodes/changeling/powers/swap_form.dm @@ -18,12 +18,15 @@ if((NOCLONE || SKELETON || HUSK) in target.mutations) to_chat(user, "DNA of [target] is ruined beyond usability!") return - if(!istype(target) || issmall(target) || (NO_DNA in target.dna.species.species_traits)) + if(!istype(target) || !target.mind || issmall(target) || (NO_DNA in target.dna.species.species_traits)) to_chat(user, "[target] is not compatible with this ability.") return if(target.mind.changeling) to_chat(user, "We are unable to swap forms with another changeling!") return + if(target.has_brain_worms() || user.has_brain_worms()) + to_chat(user, "A foreign presence repels us from this body!") + return return 1 /datum/action/changeling/swap_form/sting_action(var/mob/living/carbon/user) @@ -63,6 +66,8 @@ target.add_language("Changeling") user.remove_language("Changeling") user.regenerate_icons() + if(target.stat == DEAD && target.suiciding) //If Target committed suicide, unset flag for User + target.suiciding = 0 for(var/power in lingpowers) var/datum/action/changeling/S = power diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 388b16d71af..a68e4c1e9ca 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -140,6 +140,7 @@ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS allowed = list(/obj/item/tome,/obj/item/melee/cultblade) var/current_charges = 3 + var/shield_state = "shield-cult" hoodtype = /obj/item/clothing/head/hooded/cult_hoodie /obj/item/clothing/head/hooded/cult_hoodie @@ -164,33 +165,49 @@ if(current_charges) owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") current_charges-- + playsound(loc, "sparks", 100, 1) new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) if(!current_charges) owner.visible_message("The runed shield around [owner] suddenly disappears!") + shield_state = "broken" owner.update_inv_wear_suit() return 1 return 0 -/obj/item/clothing/suit/hooded/cultrobes/berserker +/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/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/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index 492d128e222..af0dfb5e6e3 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,5 +1,5 @@ /obj/item/paper/talisman - icon = 'icons/obj/paper.dmi' + icon = 'icons/obj/bureaucracy.dmi' icon_state = "paper_talisman" var/cultist_name = "talisman" var/cultist_desc = "A basic talisman. It serves no purpose." diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 57a77167346..1e2dd3d554b 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -424,9 +424,9 @@ proc/display_roundstart_logout_report() return nukecode /datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS) var/mob/dead/observer/theghost = null - if(candidates.len) + if(length(candidates)) theghost = pick(candidates) to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") 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/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index f647ae5a6d1..fdf17f6ddda 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -15,7 +15,7 @@ a_intent = INTENT_HARM can_change_intents = 0 stop_automated_movement = 1 - floating = 1 + flying = TRUE attack_sound = 'sound/weapons/punch1.ogg' minbodytemp = 0 maxbodytemp = INFINITY @@ -246,7 +246,7 @@ src.verbs -= /mob/living/proc/guardian_reset for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list) if(G.summoner == src) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = G) var/mob/dead/observer/new_stand = null if(candidates.len) new_stand = pick(candidates) @@ -312,7 +312,7 @@ used = FALSE return to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = src) var/mob/dead/observer/theghost = null if(candidates.len) diff --git a/code/game/gamemodes/miniantags/guardian/types/ranged.dm b/code/game/gamemodes/miniantags/guardian/types/ranged.dm index 496e31d1bd3..dab7dfb1eca 100644 --- a/code/game/gamemodes/miniantags/guardian/types/ranged.dm +++ b/code/game/gamemodes/miniantags/guardian/types/ranged.dm @@ -1,7 +1,7 @@ /obj/item/projectile/guardian name = "crystal spray" icon_state = "guardian" - damage = 5 + damage = 25 damage_type = BRUTE armour_penetration = 100 @@ -11,7 +11,7 @@ melee_damage_upper = 10 damage_transfer = 0.9 projectiletype = /obj/item/projectile/guardian - ranged_cooldown_time = 1 //fast! + ranged_cooldown_time = 5 //fast! projectilesound = 'sound/effects/hit_on_shattered_glass.ogg' ranged = 1 range = 13 diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm index e9465492a27..7fc9e134195 100644 --- a/code/game/gamemodes/miniantags/morph/morph_event.dm +++ b/code/game/gamemodes/miniantags/morph/morph_event.dm @@ -3,7 +3,7 @@ /datum/event/spawn_morph/proc/get_morph() spawn() - var/list/candidates = pollCandidates("Do you want to play as a morph?", ROLE_MORPH, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, source = /mob/living/simple_animal/hostile/morph) if(!candidates.len) key_of_morph = null return kill() diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm index 637ab69bf04..cba5ea62a2a 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant.dm @@ -34,7 +34,7 @@ status_flags = 0 wander = 0 density = 0 - flying = 1 + flying = TRUE move_resist = INFINITY mob_size = MOB_SIZE_TINY pass_flags = PASSTABLE | PASSGRILLE | PASSMOB @@ -141,7 +141,7 @@ giveObjectivesandGoals() giveSpells() else - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a revenant?", poll_time = 15 SECONDS) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", poll_time = 15 SECONDS, source = /mob/living/simple_animal/revenant) var/mob/dead/observer/theghost = null if(candidates.len) theghost = pick(candidates) @@ -397,7 +397,7 @@ spawn() if(!key_of_revenant) message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant) if(!candidates.len) qdel(R) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm index 5e5f0f87a79..aa76f3620ac 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm @@ -13,7 +13,7 @@ return spawn() - var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant) if(!candidates.len) key_of_revenant = null return kill() diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm index d7e53bdae28..e58be201521 100644 --- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm +++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm @@ -157,7 +157,7 @@ /mob/living/simple_animal/slaughter/cult/New() ..() spawn(5) - var/list/demon_candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1, 100) + var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult) if(!demon_candidates.len) visible_message("[src] disappears in a flash of red light!") qdel(src) 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/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm index b46bc3c6c51..b51810e4967 100644 --- a/code/game/gamemodes/shadowling/shadowling.dm +++ b/code/game/gamemodes/shadowling/shadowling.dm @@ -74,7 +74,7 @@ Made by Xhuis required_enemies = 2 recommended_enemies = 2 restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") /datum/game_mode/shadowling/announce() to_chat(world, "The current game mode is - Shadowling!") diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index a81b12ed041..44dec855f82 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -50,7 +50,8 @@ to_chat(H, "You already used this contract!") return used = 1 - var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, 1) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard") + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, TRUE, source = source) if(candidates.len) var/mob/C = pick(candidates) new /obj/effect/particle_effect/smoke(H.loc) @@ -307,7 +308,8 @@ GLOBAL_LIST_EMPTY(multiverse) if(M.assigned == assigned) M.cooldown = cooldown - var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, 1, 100) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard") + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, TRUE, 10 SECONDS, source = source) if(candidates.len) var/mob/C = pick(candidates) spawn_copy(C.client, get_turf(user.loc), user) diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm index cc52b431249..d51b16d2cd3 100644 --- a/code/game/gamemodes/wizard/raginmages.dm +++ b/code/game/gamemodes/wizard/raginmages.dm @@ -118,7 +118,8 @@ return FALSE making_mage = TRUE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard") + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS, source = source) var/mob/dead/observer/harry = null message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.") diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 4bda254d930..aab4318c774 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -363,7 +363,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100) + var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index d5a59a36872..48ac91c72fa 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -510,3 +510,26 @@ proc/get_all_job_icons() //For all existing HUD icons return rankName return "Unknown" //Return unknown if none of the above apply + +proc/get_accesslist_static_data(num_min_region = REGION_GENERAL, num_max_region = REGION_COMMAND) + var/list/retval + for(var/i in num_min_region to num_max_region) + var/list/accesses = list() + var/list/available_accesses + if(i == REGION_CENTCOMM) // Override necessary, because get_region_accesses(REGION_CENTCOM) returns BOTH CC and crew accesses. + available_accesses = get_all_centcom_access() + else + available_accesses = get_region_accesses(i) + for(var/access in available_accesses) + var/access_desc = (i == REGION_CENTCOMM) ? get_centcom_access_desc(access) : get_access_desc(access) + if (access_desc) + accesses += list(list( + "desc" = replacetext(access_desc, " ", " "), + "ref" = access, + )) + retval += list(list( + "name" = get_region_accesses_name(i), + "regid" = i, + "accesses" = accesses + )) + return retval diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm index f0e6f9bff42..aa9114c2963 100644 --- a/code/game/jobs/job_exp.dm +++ b/code/game/jobs/job_exp.dm @@ -224,19 +224,17 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list( else return "none" -/proc/update_exp(var/mins, var/ann = 0) - if(!establish_db_connection()) - return -1 - spawn(0) - for(var/client/L in GLOB.clients) - if(L.inactivity >= (10 MINUTES)) - continue - spawn(0) - L.update_exp_client(mins, ann) - sleep(10) +/proc/update_exp(mins = 0, ann = 0) + if(!GLOB.dbcon.IsConnected()) + return + for(var/client/L in GLOB.clients) + if(L.inactivity >= (10 MINUTES)) + continue + L.update_exp_client(mins, ann) + CHECK_TICK -/client/proc/update_exp_client(var/minutes, var/announce_changes = 0) - if(!src ||!ckey) +/client/proc/update_exp_client(minutes = 0, announce_changes = 0) + if(!src || !ckey || !GLOB.dbcon.IsConnected()) return var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'") if(!exp_read.Execute()) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 7c5a03e29e6..d33518be857 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -125,7 +125,7 @@ return attack_hand(user) /obj/machinery/sleeper/attack_ghost(mob/user) - return attack_hand(user) + tgui_interact(user) /obj/machinery/sleeper/attack_hand(mob/user) if(stat & (NOPOWER|BROKEN)) @@ -135,17 +135,17 @@ to_chat(user, "Close the maintenance panel first.") return - ui_interact(user) + tgui_interact(user) -/obj/machinery/sleeper/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/sleeper/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, "sleeper.tmpl", "Sleeper", 550, 770) + ui = new(user, src, ui_key, "Sleeper", "Sleeper", 550, 775) ui.open() - ui.set_auto_update(1) -/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/sleeper/tgui_data(mob/user) var/data[0] + data["amounts"] = amounts data["hasOccupant"] = occupant ? 1 : 0 var/occupantData[0] var/crisis = 0 @@ -210,9 +210,13 @@ if(beaker) data["isBeakerLoaded"] = 1 if(beaker.reagents) + data["beakerMaxSpace"] = beaker.reagents.maximum_volume data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) else + data["beakerMaxSpace"] = 0 data["beakerFreeSpace"] = 0 + else + data["isBeakerLoaded"] = FALSE var/chemicals[0] for(var/re in possible_chems) @@ -234,51 +238,52 @@ if(temp.id in occupant.reagents.overdose_list()) overdosing = 1 - // Because I don't know how to do this on the nano side pretty_amount = round(reagent_amount, 0.05) chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) data["chemicals"] = chemicals return data -/obj/machinery/sleeper/Topic(href, href_list) - if(!controls_inside && usr == occupant) - return 0 - +/obj/machinery/sleeper/tgui_act(action, params) if(..()) - return 1 - + return + if(!controls_inside && usr == occupant) + return if(panel_open) to_chat(usr, "Close the maintenance panel first.") - return 0 + return + if(stat & (NOPOWER|BROKEN)) + return - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - if(href_list["chemical"]) - if(occupant) - if(occupant.stat == DEAD) - to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") - else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) - inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - - if(href_list["removebeaker"]) + . = TRUE + switch(action) + if("chemical") + if(!occupant) + return + if(occupant.stat == DEAD) + to_chat(usr, "This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") + return + var/chemical = params["chemid"] + var/amount = text2num(params["amount"]) + if(!length(chemical) || amount <= 0) + return + if(occupant.health > min_health || (chemical in emergency_chems)) + inject_chemical(usr, chemical, amount) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + if("removebeaker") remove_beaker() - - if(href_list["togglefilter"]) + if("togglefilter") toggle_filter() - - if(href_list["ejectify"]) + if("ejectify") eject() - - if(href_list["auto_eject_dead_on"]) + if("auto_eject_dead_on") auto_eject_dead = TRUE - - if(href_list["auto_eject_dead_off"]) + if("auto_eject_dead_off") auto_eject_dead = FALSE - - add_fingerprint(usr) - return 1 + else + return FALSE + add_fingerprint(usr) /obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/reagent_containers/glass)) @@ -290,6 +295,7 @@ beaker = I I.forceMove(src) user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") + SStgui.update_uis(src) return else @@ -328,6 +334,7 @@ to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") add_fingerprint(user) qdel(G) + SStgui.update_uis(src) return return ..() @@ -374,9 +381,11 @@ occupant = null updateUsrDialog() update_icon() + SStgui.update_uis(src) if(A == beaker) beaker = null updateUsrDialog() + SStgui.update_uis(src) /obj/machinery/sleeper/emp_act(severity) if(filtering) @@ -394,7 +403,7 @@ qdel(src) /obj/machinery/sleeper/proc/toggle_filter() - if(filtering) + if(filtering || !beaker) filtering = 0 else filtering = 1 @@ -410,29 +419,28 @@ // eject trash the occupant dropped for(var/atom/movable/A in contents - component_parts - list(beaker)) A.forceMove(loc) + SStgui.update_uis(src) /obj/machinery/sleeper/force_eject_occupant() go_out() -/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) +/obj/machinery/sleeper/proc/inject_chemical(mob/living/user, chemical, amount) if(!(chemical in possible_chems)) to_chat(user, "The sleeper does not offer that chemical!") return + if(!(amount in amounts)) + return if(occupant) if(occupant.reagents) if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) occupant.reagents.add_reagent(chemical, amount) - return else to_chat(user, "You can not inject any more of this chemical.") - return else to_chat(user, "The patient rejects the chemicals!") - return else to_chat(user, "There's no occupant in the sleeper!") - return /obj/machinery/sleeper/verb/eject() set name = "Eject Sleeper" @@ -459,6 +467,7 @@ filtering = 0 beaker.forceMove(usr.loc) beaker = null + SStgui.update_uis(src) add_fingerprint(usr) return @@ -511,6 +520,7 @@ add_fingerprint(user) if(user.pulling == L) user.stop_pulling() + SStgui.update_uis(src) return return @@ -547,6 +557,7 @@ for(var/obj/O in src) qdel(O) add_fingerprint(usr) + SStgui.update_uis(src) return return diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 3fa78dd02e6..97ab80fc1d7 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -65,6 +65,7 @@ icon_state = "body_scanner_1" add_fingerprint(user) qdel(TYPECAST_YOUR_SHIT) + SStgui.update_uis(src) return return ..() @@ -127,12 +128,13 @@ occupant = H icon_state = "bodyscanner" add_fingerprint(user) + SStgui.update_uis(src) /obj/machinery/bodyscanner/attack_ai(user) return attack_hand(user) /obj/machinery/bodyscanner/attack_ghost(user) - return attack_hand(user) + tgui_interact(user) /obj/machinery/bodyscanner/attack_hand(user) if(stat & (NOPOWER|BROKEN)) @@ -145,7 +147,7 @@ to_chat(user, "Close the maintenance panel first.") return - ui_interact(user) + tgui_interact(user) /obj/machinery/bodyscanner/relaymove(mob/user) if(user.incapacitated()) @@ -171,6 +173,7 @@ // eject trash the occupant dropped for(var/atom/movable/A in contents - component_parts) A.forceMove(loc) + SStgui.update_uis(src) /obj/machinery/bodyscanner/force_eject_occupant() go_out() @@ -192,17 +195,16 @@ new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! qdel(src) -/obj/machinery/bodyscanner/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/bodyscanner/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, "adv_med.tmpl", "Body Scanner", 690, 600) + ui = new(user, src, ui_key, "BodyScanner", "Body Scanner", 690, 600) ui.open() - ui.set_auto_update(1) -/obj/machinery/bodyscanner/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/bodyscanner/tgui_data(mob/user) var/data[0] - data["occupied"] = occupant ? 1 : 0 + data["occupied"] = occupant ? TRUE : FALSE var/occupantData[0] if(occupant) @@ -236,9 +238,9 @@ occupantData["hasBorer"] = occupant.has_brain_worms() var/bloodData[0] - bloodData["hasBlood"] = 0 + bloodData["hasBlood"] = FALSE if(!(NO_BLOOD in occupant.dna.species.species_traits)) - bloodData["hasBlood"] = 1 + bloodData["hasBlood"] = TRUE bloodData["volume"] = occupant.blood_volume bloodData["percent"] = round(((occupant.blood_volume / BLOOD_VOLUME_NORMAL)*100)) bloodData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) @@ -282,19 +284,19 @@ if(E.status & ORGAN_BROKEN) organStatus["broken"] = E.broken_description if(E.is_robotic()) - organStatus["robotic"] = 1 + organStatus["robotic"] = TRUE if(E.status & ORGAN_SPLINTED) - organStatus["splinted"] = 1 + organStatus["splinted"] = TRUE if(E.status & ORGAN_DEAD) - organStatus["dead"] = 1 + organStatus["dead"] = TRUE organData["status"] = organStatus if(istype(E, /obj/item/organ/external/chest) && occupant.is_lung_ruptured()) - organData["lungRuptured"] = 1 + organData["lungRuptured"] = TRUE if(E.internal_bleeding) - organData["internalBleeding"] = 1 + organData["internalBleeding"] = TRUE extOrganData.Add(list(organData)) @@ -319,27 +321,33 @@ occupantData["blind"] = (BLINDNESS in occupant.mutations) occupantData["colourblind"] = (COLOURBLIND in occupant.mutations) - occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations) + occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations) data["occupant"] = occupantData return data -/obj/machinery/bodyscanner/Topic(href, href_list) +/obj/machinery/bodyscanner/tgui_act(action, params) if(..()) - return 1 + return + if(stat & (NOPOWER|BROKEN)) + return - if(href_list["ejectify"]) - eject() - - if(href_list["print_p"]) - visible_message("[src] rattles and prints out a sheet of paper.") - var/obj/item/paper/P = new /obj/item/paper(loc) - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - P.info = "
Body Scan - [href_list["name"]]

" - P.info += "Time of scan: [station_time_timestamp()]

" - P.info += "[generate_printing_text()]" - P.info += "

Notes:
" - P.name = "Body Scan - [href_list["name"]]" + . = TRUE + switch(action) + if("ejectify") + eject() + if("print_p") + visible_message("[src] rattles and prints out a sheet of paper.") + var/obj/item/paper/P = new /obj/item/paper(loc) + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + var/name = occupant ? occupant.name : "Unknown" + P.info = "
Body Scan - [name]

" + P.info += "Time of scan: [station_time_timestamp()]

" + P.info += "[generate_printing_text()]" + P.info += "

Notes:
" + P.name = "Body Scan - [name]" + else + return FALSE /obj/machinery/bodyscanner/proc/generate_printing_text() var/dat = "" 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/canister.dm b/code/game/machinery/atmoalter/canister.dm index e927415c759..3c09da548f5 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -11,6 +11,7 @@ list("name" = "\[SPECIAL\]", "icon" = "whiters") ) possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists + list("name" = "\[None\]", "icon" = "none"), list("name" = "\[N2\]", "icon" = "red-c"), list("name" = "\[O2\]", "icon" = "blue-c"), list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), @@ -19,6 +20,7 @@ list("name" = "\[CAUTION\]", "icon" = "yellow-c") ) possibletertcolor = list( + list("name" = "\[None\]", "icon" = "none"), list("name" = "\[N2\]", "icon" = "red-c-1"), list("name" = "\[O2\]", "icon" = "blue-c-1"), list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), @@ -27,6 +29,7 @@ list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") ) possiblequartcolor = list( + list("name" = "\[None\]", "icon" = "none"), list("name" = "\[N2\]", "icon" = "red-c-2"), list("name" = "\[O2\]", "icon" = "blue-c-2"), list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), @@ -34,12 +37,6 @@ list("name" = "\[Air\]", "icon" = "grey-c-2"), list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") ) - - possibledecals = list( //var that stores all possible decals, used by ui - list("name" = "Low temperature canister", "icon" = "cold"), - list("name" = "High temperature canister", "icon" = "hot"), - list("name" = "Plasma containing canister", "icon" = "plasma") - ) GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) /obj/machinery/portable_atmospherics/canister @@ -52,22 +49,17 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) max_integrity = 250 integrity_failure = 100 - var/menu = 0 - //used by nanoui: 0 = main menu, 1 = relabel - var/valve_open = 0 var/release_pressure = ONE_ATMOSPHERE var/list/canister_color //variable that stores colours - var/list/decals //list that stores the decals + var/list/color_index // list which stores tgui color indexes for the recoloring options, to enable previously-set colors to show up right //lists for check_change() var/list/oldcolor - var/list/olddecals //passed to the ui to render the color lists var/list/colorcontainer - var/list/possibledecals var/can_label = 1 var/filled = 0.5 @@ -82,18 +74,12 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) /obj/machinery/portable_atmospherics/canister/New() ..() canister_color = list( - "prim" = "yellow", - "sec" = "none", - "ter" = "none", - "quart" = "none") + "prim" = "yellow", + "sec" = "none", + "ter" = "none", + "quart" = "none" + ) oldcolor = new /list() - decals = list("cold" = 0, "hot" = 0, "plasma" = 0) - colorcontainer = list() - possibledecals = list() - update_icon() - -/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() - //passed to the ui to render the color lists colorcontainer = list( "prim" = list( "options" = GLOB.canister_icon_container.possiblemaincolor, @@ -112,26 +98,8 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) "name" = "Quaternary color", ) ) - - //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied - for(var/C in colorcontainer) - if(C == "prim") continue - var/list/L = colorcontainer[C] - if(!(canister_color[C]) || (canister_color[C] == "none")) - L.Add(list("anycolor" = 0)) - else - L.Add(list("anycolor" = 1)) - colorcontainer[C] = L - - possibledecals = list() - - var/i - var/list/L = GLOB.canister_icon_container.possibledecals - for(i=1;i<=L.len;i++) - var/list/LL = L[i] - LL = LL.Copy() //make sure we don't edit the datum list - LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI - possibledecals.Add(LL) + color_index = list() + update_icon() /obj/machinery/portable_atmospherics/canister/proc/check_change() var/old_flag = update_flag @@ -155,10 +123,6 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) update_flag |= 64 oldcolor = canister_color.Copy() - if(list2params(olddecals) != list2params(decals)) - update_flag |= 128 - olddecals = decals.Copy() - if(update_flag == old_flag) return 1 else @@ -174,17 +138,16 @@ update_flag 16 = tank_pressure < 15*ONE_ATMOS 32 = tank_pressure go boom. 64 = colors -128 = decals -(note: colors and decals has to be applied every icon update) +(note: colors has to be applied every icon update) */ - if(src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. + if(destroyed) + overlays = 0 + icon_state = text("[]-1", canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. return - if(icon_state != src.canister_color["prim"]) - icon_state = src.canister_color["prim"] + if(icon_state != canister_color["prim"]) + icon_state = canister_color["prim"] if(check_change()) //Returns 1 if no change needed to icons. return @@ -192,14 +155,12 @@ update_flag overlays.Cut() for(var/C in canister_color) - if(C == "prim") continue - if(canister_color[C] == "none") continue + if(C == "prim") + continue + if(canister_color[C] == "none") + continue overlays.Add(canister_color[C]) - for(var/D in decals) - if(decals[D]) - overlays.Add("decal-" + D) - if(update_flag & 1) overlays += "can-open" if(update_flag & 2) @@ -213,35 +174,9 @@ update_flag else if(update_flag & 32) overlays += "can-o3" - update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. + update_flag &= ~68 //the flag 64 represents change, not states. As such, we have to reset them to be able to detect a change on the next go. return -//template modification exploit prevention, used in Topic() -/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") - if(checkColor == "prim" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possiblemaincolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "sec" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possibleseccolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "ter" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possibletertcolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "quart" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possiblequartcolor) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) - for(var/list/L in GLOB.canister_icon_container.possibledecals) - if(L["icon"] == inputVar) - return 1 - return 0 - /obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) ..() if(exposed_temperature > temperature_resistance) @@ -271,7 +206,7 @@ update_flag stat |= BROKEN density = FALSE - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) + playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3) update_icon() if(holding) @@ -297,7 +232,7 @@ update_flag var/transfer_moles = 0 if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + transfer_moles = pressure_delta * environment.volume / (air_contents.temperature * R_IDEAL_GAS_EQUATION) //Actually transfer the gas var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) @@ -307,7 +242,7 @@ update_flag else loc.assume_air(removed) air_update_turf() - src.update_icon() + update_icon() if(air_contents.return_pressure() < 1) @@ -315,20 +250,20 @@ update_flag else can_label = 0 - src.updateDialog() + updateDialog() return /obj/machinery/portable_atmospherics/canister/return_air() return air_contents /obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() + var/datum/gas_mixture/GM = return_air() if(GM && GM.volume>0) return GM.temperature return 0 /obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() + var/datum/gas_mixture/GM = return_air() if(GM && GM.volume>0) return GM.return_pressure() return 0 @@ -343,141 +278,113 @@ update_flag else if(valve_open && holding) investigate_log("[key_name(user)] started a transfer into [holding].
", "atmos") -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user) + add_hiddenprint(user) + return attack_hand(user) -/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) - return src.ui_interact(user) +/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user) + return tgui_interact(user) -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return src.ui_interact(user) +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user) + return tgui_interact(user) -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) - if(src.destroyed) - 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/portable_atmospherics/canister/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_physical_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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) - // open the new ui window + ui = new(user, src, ui_key, "Canister", name, 600, 350, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) - init_data_vars() //set up var/colorcontainer and var/possibledecals - - // this is the data which will be sent to the ui - var/data[0] - data["name"] = name - data["menu"] = menu ? 1 : 0 - data["canLabel"] = can_label ? 1 : 0 - data["canister_color"] = canister_color - data["colorContainer"] = colorcontainer.Copy() - colorcontainer.Cut() - data["possibleDecals"] = possibledecals.Copy() - possibledecals.Cut() +/obj/machinery/portable_atmospherics/canister/tgui_data() + var/data = list() data["portConnected"] = connected_port ? 1 : 0 data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["defaultReleasePressure"] = ONE_ATMOSPHERE + data["minReleasePressure"] = round(ONE_ATMOSPHERE / 10) + data["maxReleasePressure"] = round(ONE_ATMOSPHERE * 10) data["valveOpen"] = valve_open ? 1 : 0 - + data["name"] = name + data["canLabel"] = can_label ? 1 : 0 + data["colorContainer"] = colorcontainer.Copy() + data["color_index"] = color_index data["hasHoldingTank"] = holding ? 1 : 0 if(holding) data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) - return data -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) +/obj/machinery/portable_atmospherics/canister/tgui_act(action, params) if(..()) - return 1 - - if(href_list["choice"] == "menu") - menu = text2num(href_list["mode_target"]) - - if(href_list["toggle"]) - var/logmsg - if(valve_open) - if(holding) - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
" - else - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
" - else - if(holding) - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
" - else - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
" - if(air_contents.toxins > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") - if(air_contents.sleeping_agent > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") - investigate_log(logmsg, "atmos") - release_log += logmsg - valve_open = !valve_open - - if(href_list["remove_tank"]) - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
" - holding.loc = loc - holding = null - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - if(diff > 0) - release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) - else - release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) - - if(href_list["rename"]) - if(can_label) - var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) - if(can_label) //Exploit prevention - if(T) - name = T + return + var/can_min_release_pressure = round(ONE_ATMOSPHERE / 10) + var/can_max_release_pressure = round(ONE_ATMOSPHERE * 10) + . = TRUE + switch(action) + if("relabel") + if(can_label) + var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null, 1, MAX_NAME_LEN)) + if(can_label) //Exploit prevention + if(T) + name = T + else + name = "canister" else - name = "canister" + to_chat(usr, "As you attempted to rename it the pressure rose!") + . = FALSE + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = ONE_ATMOSPHERE + else if(pressure == "min") + pressure = can_min_release_pressure + else if(pressure == "max") + pressure = can_max_release_pressure + else if(pressure == "input") + pressure = input("New release pressure ([can_min_release_pressure]-[can_max_release_pressure] kPa):", name, release_pressure) as num|null + if(isnull(pressure)) + . = FALSE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + if(.) + release_pressure = clamp(round(pressure), can_min_release_pressure, can_max_release_pressure) + investigate_log("was set to [release_pressure] kPa by [key_name(usr)].", "atmos") + if("valve") + var/logmsg + valve_open = !valve_open + if(valve_open) + logmsg = "Valve was opened by [key_name(usr)], starting a transfer into the [holding || "air"].
" + if(!holding) + logmsg = "Valve was opened by [key_name(usr)], starting a transfer into the [holding || "air"].
" + if(air_contents.toxins > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") + if(air_contents.sleeping_agent > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") else - to_chat(usr, "As you attempted to rename it the pressure rose!") - - if(href_list["choice"] == "Primary color") - if(is_a_color(href_list["icon"],"prim")) - canister_color["prim"] = href_list["icon"] - if(href_list["choice"] == "Secondary color") - if(href_list["icon"] == "none") - canister_color["sec"] = "none" - else if(is_a_color(href_list["icon"],"sec")) - canister_color["sec"] = href_list["icon"] - if(href_list["choice"] == "Tertiary color") - if(href_list["icon"] == "none") - canister_color["ter"] = "none" - else if(is_a_color(href_list["icon"],"ter")) - canister_color["ter"] = href_list["icon"] - if(href_list["choice"] == "Quaternary color") - if(href_list["icon"] == "none") - canister_color["quart"] = "none" - else if(is_a_color(href_list["icon"],"quart")) - canister_color["quart"] = href_list["icon"] - - if(href_list["choice"] == "decals") - if(is_a_decal(href_list["icon"])) - decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) - - src.add_fingerprint(usr) + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding || "air"].
" + investigate_log(logmsg, "atmos") + release_log += logmsg + if("eject") + if(holding) + if(valve_open) + release_log += "[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air
" + message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the air.") + investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air.", "atmos") + replace_tank(usr, FALSE) + if("recolor") + if(can_label) + var/ctype = params["ctype"] + var/cnum = text2num(params["nc"]) + if(isnull(colorcontainer[ctype])) + message_admins("[key_name_admin(usr)] passed an invalid ctype var to a canister.") + return + var/newcolor = sanitize_integer(cnum, 0, length(colorcontainer[ctype]["options"])) + color_index[ctype] = newcolor + newcolor++ // javascript starts arrays at 0, byond (for some reason) starts them at 1, this converts JS values to byond values + canister_color[ctype] = colorcontainer[ctype]["options"][newcolor]["icon"] + add_fingerprint(usr) update_icon() - return 1 - /obj/machinery/portable_atmospherics/canister/toxins name = "Canister \[Toxin (Plasma)\]" @@ -513,19 +420,18 @@ update_flag ..() canister_color["prim"] = "orange" - decals["plasma"] = 1 - src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.toxins = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/oxygen/New() ..() canister_color["prim"] = "blue" - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.oxygen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/sleeping_agent/New() @@ -534,25 +440,25 @@ update_flag canister_color["prim"] = "redws" air_contents.sleeping_agent = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/nitrogen/New() ..() canister_color["prim"] = "red" - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.nitrogen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() ..() canister_color["prim"] = "black" - src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.carbon_dioxide = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 @@ -560,17 +466,17 @@ update_flag ..() canister_color["prim"] = "grey" - src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.oxygen = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + air_contents.nitrogen = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/custom_mix/New() ..() canister_color["prim"] = "whiters" - src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD + update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD return 1 /obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) 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/Operating.dm b/code/game/machinery/computer/Operating.dm index 21fddce64b7..1b083bd9c87 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -40,8 +40,7 @@ add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - ui_interact(user) - + tgui_interact(user) /obj/machinery/computer/operating/attack_hand(mob/user) if(..(user)) @@ -52,16 +51,15 @@ add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/operating/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, "op_computer.tmpl", "Patient Monitor", 650, 455) + ui = new(user, src, ui_key, "OperatingComputer", "Patient Monitor", 650, 455, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/operating/tgui_data(mob/user) var/data[0] var/mob/living/carbon/human/occupant if(table) @@ -136,38 +134,43 @@ return data -/obj/machinery/computer/operating/Topic(href, href_list) +/obj/machinery/computer/operating/tgui_act(action, params) if(..()) - return 1 + return + if(stat & (NOPOWER|BROKEN)) + return + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) usr.set_machine(src) - if(href_list["verboseOn"]) - verbose=1 - if(href_list["verboseOff"]) - verbose=0 - if(href_list["healthOn"]) - healthAnnounce=1 - if(href_list["healthOff"]) - healthAnnounce=0 - if(href_list["critOn"]) - crit=1 - if(href_list["critOff"]) - crit=0 - if(href_list["oxyOn"]) - oxy=1 - if(href_list["oxyOff"]) - oxy=0 - if(href_list["oxy_adj"]!=0) - oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) - if(href_list["choiceOn"]) - choice=1 - if(href_list["choiceOff"]) - choice=0 - if(href_list["health_adj"]!=0) - healthAlarm=healthAlarm+text2num(href_list["health_adj"]) - return - + . = TRUE + switch(action) + if("verboseOn") + verbose = TRUE + if("verboseOff") + verbose = FALSE + if("healthOn") + healthAnnounce = TRUE + if("healthOff") + healthAnnounce = FALSE + if("critOn") + crit = TRUE + if("critOff") + crit = FALSE + if("oxyOn") + oxy = TRUE + if("oxyOff") + oxy = FALSE + if("oxy_adj") + oxyAlarm = clamp(text2num(params["new"]), -100, 100) + if("choiceOn") + choice = TRUE + if("choiceOff") + choice = FALSE + if("health_adj") + healthAlarm = clamp(text2num(params["new"]), -100, 100) + else + return FALSE /obj/machinery/computer/operating/process() @@ -178,6 +181,7 @@ atom_say("New patient detected, loading stats") victim = table.victim atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + SStgui.update_uis(src) if(nextTick < world.time) nextTick=world.time + OP_COMPUTER_COOLDOWN if(crit && victim.health <= -50 ) 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 575115f8394..3115a4a235b 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -162,12 +162,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 @@ -233,7 +230,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/card.dm b/code/game/machinery/computer/card.dm index 8d6c545e0a8..98f56feeb6a 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -390,7 +390,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if(!job_in_department(SSjobs.GetJob(t1))) return 0 if(t1 == "Custom") - var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN)) + var/temp_t = sanitize(reject_bad_name(copytext(input("Enter a custom job assignment.", "Assignment"), 1, MAX_MESSAGE_LEN), TRUE)) //let custom jobs function as an impromptu alt title, mainly for sechuds if(temp_t && modify) SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name) @@ -419,7 +419,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name) - SSjobs.slot_job_transfer(modify.rank, t1) + if(modify.owner_uid) + SSjobs.slot_job_transfer(modify.rank, t1) var/mob/living/carbon/human/H = modify.getPlayer() if(istype(H)) @@ -436,7 +437,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if(is_authenticated(usr) && !target_dept) var/t2 = modify if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) - var/temp_name = reject_bad_name(href_list["reg"]) + var/temp_name = reject_bad_name(href_list["reg"], TRUE) if(temp_name) modify.registered_name = temp_name else @@ -465,6 +466,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if(is_authenticated(usr) && !target_dept) var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE) if(delcount) + message_admins("[key_name_admin(usr)] has wiped all ID computer logs.") + usr.create_log(MISC_LOG, "wiped all ID computer logs.") playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) SSnanoui.update_uis(src) @@ -504,9 +507,16 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if("terminate") if(is_authenticated(usr) && !target_dept) var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") - message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") + var/reason = sanitize(copytext(input("Enter legal reason for termination. Enter nothing to cancel.", "Employment Termination"), 1, MAX_MESSAGE_LEN)) + if(!reason || !is_authenticated(usr) || !modify) + return FALSE + var/m_ckey = modify.getPlayerCkey() + var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)" + log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".") + message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".") + usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"") SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name) + SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".") modify.assignment = "Terminated" modify.access = list() @@ -518,16 +528,20 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE)) visible_message("[src]: Heads may only demote members of their own department.") return 0 - + var/reason = sanitize(copytext(input("Enter legal reason for demotion. Enter nothing to cancel.","Legal Demotion"),1,MAX_MESSAGE_LEN)) + if(!reason || !is_authenticated(usr) || !modify) + return 0 var/list/access = list() var/datum/job/jobdatum = new /datum/job/civilian access = jobdatum.get_access() - var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".") - message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".") + var/m_ckey = modify.getPlayerCkey() + var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)" + log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".") + message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".") + usr.create_log(MISC_LOG, "demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"") SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name) - + SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".") modify.access = access modify.rank = "Civilian" modify.assignment = "Demoted" diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 5198a97a88c..04a4f7e0db4 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -1,3 +1,6 @@ +#define MENU_MAIN 1 +#define MENU_RECORDS 2 + /obj/machinery/computer/cloning name = "cloning console" icon = 'icons/obj/computer.dmi' @@ -6,11 +9,11 @@ circuit = /obj/item/circuitboard/cloning req_access = list(ACCESS_HEADS) //Only used for record deletion right now. var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning. - var/list/pods = list() //Linked cloning pods. - var/temp = "" - var/scantemp = "Scanner ready." - var/menu = 1 //Which menu screen to display - var/list/records = list() + var/list/pods = null //Linked cloning pods. + var/list/temp = null + var/list/scantemp = null + var/menu = MENU_MAIN //Which menu screen to display + var/list/records = null var/datum/dna2/record/active_record = null var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything. var/loading = 0 // Nice loading text @@ -24,6 +27,9 @@ /obj/machinery/computer/cloning/Initialize() ..() + pods = list() + records = list() + set_scan_temp("Scanner ready.", "good") updatemodules() /obj/machinery/computer/cloning/Destroy() @@ -91,7 +97,7 @@ W.loc = src src.diskette = W to_chat(user, "You insert [W].") - SSnanoui.update_uis(src) + SStgui.update_uis(src) return else if(istype(W, /obj/item/multitool)) var/obj/item/multitool/M = W @@ -117,19 +123,21 @@ return updatemodules() - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/cloning/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/computer/cloning/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) if(stat & (NOPOWER|BROKEN)) return - // Set up the Nano UI - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + var/datum/asset/cloning/assets = get_asset_datum(/datum/asset/cloning) + assets.send(user) + + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520) + ui = new(user, src, ui_key, "CloningConsole", "Cloning Console", 640, 520) ui.open() -/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/cloning/tgui_data(mob/user) var/data[0] data["menu"] = src.menu data["scanner"] = sanitize("[src.scanner]") @@ -143,7 +151,18 @@ if(pod.efficiency > 5) canpodautoprocess = 1 - tempods.Add(list(list("pod" = "\ref[pod]", "name" = sanitize(capitalize(pod.name)), "biomass" = pod.biomass))) + var/status = "idle" + if(pod.mess) + status = "mess" + else if(pod.occupant && !(pod.stat & NOPOWER)) + status = "cloning" + tempods.Add(list(list( + "pod" = "\ref[pod]", + "name" = sanitize(capitalize(pod.name)), + "biomass" = pod.biomass, + "status" = status, + "progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0 + ))) data["pods"] = tempods data["loading"] = loading @@ -168,188 +187,190 @@ temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName)))) data["records"] = temprecords - if(src.menu == 3) - if(src.active_record) - data["activerecord"] = "\ref[src.active_record]" - var/obj/item/implant/health/H = null - if(src.active_record.implant) - H = locate(src.active_record.implant) + if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS) + data["podready"] = 1 + else + data["podready"] = 0 - if((H) && (istype(H))) - data["health"] = H.sensehealth() - data["realname"] = sanitize(src.active_record.dna.real_name) - data["unidentity"] = src.active_record.dna.uni_identity - data["strucenzymes"] = src.active_record.dna.struc_enzymes - if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS) - data["podready"] = 1 - else - data["podready"] = 0 + data["modal"] = tgui_modal_data(src) return data -/obj/machinery/computer/cloning/Topic(href, href_list) +/obj/machinery/computer/cloning/tgui_act(action, params) if(..()) - return 1 - - if(loading) + return + if(stat & (NOPOWER|BROKEN)) return - if(href_list["scan"] && scanner && scanner.occupant) - scantemp = "Scanner ready." - - loading = 1 - - spawn(20) - if(can_brainscan() && scan_mode) - scan_mob(scanner.occupant, scan_brain = 1) - else - scan_mob(scanner.occupant) - - loading = 0 - SSnanoui.update_uis(src) - - if(href_list["task"]) - switch(href_list["task"]) - if("autoprocess") - autoprocess = 1 - SSnanoui.update_uis(src) - if("stopautoprocess") - autoprocess = 0 - SSnanoui.update_uis(src) - - //No locking an open scanner. - else if((href_list["lock"]) && (!isnull(src.scanner))) - if((!src.scanner.locked) && (src.scanner.occupant)) - src.scanner.locked = 1 - else - src.scanner.locked = 0 - - else if(href_list["view_rec"]) - src.active_record = locate(href_list["view_rec"]) - if(istype(src.active_record,/datum/dna2/record)) - if((isnull(src.active_record.ckey))) - qdel(src.active_record) - src.temp = "Error: Record corrupt." - else - src.menu = 3 - else - src.active_record = null - src.temp = "Error: Record missing." - - else if(href_list["del_rec"]) - if((!src.active_record) || (src.menu < 3)) - return - if(src.menu == 3) //If we are viewing a record, confirm deletion - src.temp = "Please confirm that you want to delete the record?" - src.menu = 4 - - else if(src.menu == 4) - var/obj/item/card/id/C = usr.get_active_hand() - if(istype(C)||istype(C, /obj/item/pda)) - if(src.check_access(C)) - src.records.Remove(src.active_record) - qdel(src.active_record) - src.temp = "Record deleted." - src.menu = 2 + . = TRUE + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_ANSWER) + if(params["id"] == "del_rec" && active_record) + var/obj/item/card/id/C = usr.get_active_hand() + if(!istype(C) && !istype(C, /obj/item/pda)) + set_temp("ID not in hand.", "danger") + return + if(check_access(C)) + records.Remove(active_record) + qdel(active_record) + set_temp("Record deleted.", "success") + menu = MENU_RECORDS else - src.temp = "Error: Access denied." - - else if(href_list["disk"]) //Load or eject. - switch(href_list["disk"]) - if("load") - if((isnull(src.diskette)) || isnull(src.diskette.buf)) - src.temp = "Error: The disk's data could not be read." - SSnanoui.update_uis(src) - return - if(isnull(src.active_record)) - src.temp = "Error: No active record was found." - src.menu = 1 - SSnanoui.update_uis(src) - return - - src.active_record = src.diskette.buf.copy() - - src.temp = "Load successful." - - if("eject") - if(!isnull(src.diskette)) - src.diskette.loc = src.loc - src.diskette = null - - else if(href_list["save_disk"]) //Save to disk! - if((isnull(src.diskette)) || (src.diskette.read_only) || (isnull(src.active_record))) - src.temp = "Error: The data could not be saved." - SSnanoui.update_uis(src) + set_temp("Access denied.", "danger") return - // DNA2 makes things a little simpler. - src.diskette.buf=src.active_record.copy() - src.diskette.buf.types=0 - switch(href_list["save_disk"]) //Save as Ui/Ui+Ue/Se - if("ui") - src.diskette.buf.types=DNA2_BUF_UI - if("ue") - src.diskette.buf.types=DNA2_BUF_UI|DNA2_BUF_UE - if("se") - src.diskette.buf.types=DNA2_BUF_SE - src.diskette.name = "data disk - '[src.active_record.dna.real_name]'" - src.temp = "Save \[[href_list["save_disk"]]\] successful." + switch(action) + if("scan") + if(!scanner || !scanner.occupant || loading) + return + set_scan_temp("Scanner ready.", "good") + loading = TRUE - else if(href_list["refresh"]) - SSnanoui.update_uis(src) - - else if(href_list["selectpod"]) - var/obj/machinery/clonepod/selected = locate(href_list["selectpod"]) - if(istype(selected) && (selected in pods)) - selected_pod = selected - - else if(href_list["clone"]) - var/datum/dna2/record/C = locate(href_list["clone"]) - //Look for that player! They better be dead! - if(istype(C)) - //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. - if(!pods.len) - temp = "Error: No cloning pod detected." - else - var/obj/machinery/clonepod/pod = selected_pod - var/cloneresult - if(!selected_pod) - temp = "Error: No cloning pod selected." - else if(pod.occupant) - temp = "Error: The cloning pod is currently occupied." - else if(pod.biomass < CLONE_BIOMASS) - temp = "Error: Not enough biomass." - else if(pod.mess) - temp = "Error: The cloning pod is malfunctioning." - else if(!config.revival_cloning) - temp = "Error: Unable to initiate cloning cycle." + spawn(20) + if(can_brainscan() && scan_mode) + scan_mob(scanner.occupant, scan_brain = TRUE) else - cloneresult = pod.growclone(C) - if(cloneresult) - if(cloneresult > 0) - temp = "Initiating cloning cycle..." - records.Remove(C) - qdel(C) - menu = 1 + scan_mob(scanner.occupant) + loading = FALSE + SStgui.update_uis(src) + if("autoprocess") + autoprocess = text2num(params["on"]) > 0 + if("lock") + if(isnull(scanner) || !scanner.occupant) //No locking an open scanner. + return + scanner.locked = !scanner.locked + if("view_rec") + var/ref = params["ref"] + if(!length(ref)) + return + active_record = locate(ref) + if(istype(active_record)) + if(isnull(active_record.ckey)) + qdel(active_record) + set_temp("Error: Record corrupt.", "danger") + else + var/obj/item/implant/health/H = null + if(active_record.implant) + H = locate(active_record.implant) + var/list/payload = list( + activerecord = "\ref[active_record]", + health = (H && istype(H)) ? H.sensehealth() : "", + realname = sanitize(active_record.dna.real_name), + unidentity = active_record.dna.uni_identity, + strucenzymes = active_record.dna.struc_enzymes, + ) + tgui_modal_message(src, action, "", null, payload) + else + active_record = null + set_temp("Error: Record missing.", "danger") + if("del_rec") + if(!active_record) + return + tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel") + if("disk") // Disk management. + if(!length(params["option"])) + return + switch(params["option"]) + if("load") + if(isnull(diskette) || isnull(diskette.buf)) + set_temp("Error: The disk's data could not be read.", "danger") + return + else if(isnull(active_record)) + set_temp("Error: No active record was found.", "danger") + menu = MENU_MAIN + return + + active_record = diskette.buf.copy() + set_temp("Successfully loaded from disk.", "success") + if("save") + if(isnull(diskette) || diskette.read_only || isnull(active_record)) + set_temp("Error: The data could not be saved.", "danger") + return + + // DNA2 makes things a little simpler. + var/types + switch(params["savetype"]) // Save as Ui/Ui+Ue/Se + if("ui") + types = DNA2_BUF_UI + if("ue") + types = DNA2_BUF_UI|DNA2_BUF_UE + if("se") + types = DNA2_BUF_SE + else + set_temp("Error: Invalid save format.", "danger") + return + diskette.buf = active_record.copy() + diskette.buf.types = types + diskette.name = "data disk - '[active_record.dna.real_name]'" + set_temp("Successfully saved to disk.", "success") + if("eject") + if(!isnull(diskette)) + diskette.loc = loc + diskette = null + if("refresh") + SStgui.update_uis(src) + if("selectpod") + var/ref = params["ref"] + if(!length(ref)) + return + var/obj/machinery/clonepod/selected = locate(ref) + if(istype(selected) && (selected in pods)) + selected_pod = selected + if("clone") + var/ref = params["ref"] + if(!length(ref)) + return + var/datum/dna2/record/C = locate(ref) + //Look for that player! They better be dead! + if(istype(C)) + tgui_modal_clear(src) + //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. + if(!length(pods)) + set_temp("Error: No cloning pod detected.", "danger") + else + var/obj/machinery/clonepod/pod = selected_pod + var/cloneresult + if(!selected_pod) + set_temp("Error: No cloning pod selected.", "danger") + else if(pod.occupant) + set_temp("Error: The cloning pod is currently occupied.", "danger") + else if(pod.biomass < CLONE_BIOMASS) + set_temp("Error: Not enough biomass.", "danger") + else if(pod.mess) + set_temp("Error: The cloning pod is malfunctioning.", "danger") + else if(!config.revival_cloning) + set_temp("Error: Unable to initiate cloning cycle.", "danger") else - temp = "[C.name] => Initialisation failure." - + cloneresult = pod.growclone(C) + if(cloneresult) + set_temp("Initiating cloning cycle...", "success") + records.Remove(C) + qdel(C) + menu = MENU_MAIN + else + set_temp("Error: Initialisation failure.", "danger") + else + set_temp("Error: Data corruption.", "danger") + if("menu") + menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_RECORDS) + if("toggle_mode") + if(loading) + return + if(can_brainscan()) + scan_mode = !scan_mode + else + scan_mode = FALSE + if("eject") + if(usr.incapacitated() || !scanner || loading) + return + scanner.eject_occupant(usr) + scanner.add_fingerprint(usr) + if("cleartemp") + temp = null else - temp = "Error: Data corruption." - - else if(href_list["menu"]) - src.menu = text2num(href_list["menu"]) - temp = "" - scantemp = "Scanner ready." - else if(href_list["toggle_mode"]) - if(can_brainscan()) - scan_mode = !scan_mode - else - scan_mode = 0 + return FALSE src.add_fingerprint(usr) - SSnanoui.update_uis(src) - return /obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0) if(stat & NOPOWER) @@ -360,46 +381,46 @@ return if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna)) if(isalien(subject)) - scantemp = "Error: Xenomorphs are not scannable." - SSnanoui.update_uis(src) + set_scan_temp("Xenomorphs are not scannable.", "bad") + SStgui.update_uis(src) return // can add more conditions for specific non-human messages here else - scantemp = "Error: Subject species is not scannable." - SSnanoui.update_uis(src) + set_scan_temp("Subject species is not scannable.", "bad") + SStgui.update_uis(src) return if(subject.get_int_organ(/obj/item/organ/internal/brain)) var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain) if(istype(Brn)) if(NO_SCAN in Brn.dna.species.species_traits) - scantemp = "Error: [Brn.dna.species.name_plural] are not scannable." - SSnanoui.update_uis(src) + set_scan_temp("[Brn.dna.species.name_plural] are not scannable.", "bad") + SStgui.update_uis(src) return if(!subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No brain detected in subject." - SSnanoui.update_uis(src) + set_scan_temp("No brain detected in subject.", "bad") + SStgui.update_uis(src) return if(subject.suiciding) - scantemp = "Error: Subject has committed suicide and is not scannable." - SSnanoui.update_uis(src) + set_scan_temp("Subject has committed suicide and is not scannable.", "bad") + SStgui.update_uis(src) return if((!subject.ckey) || (!subject.client)) - scantemp = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed." - SSnanoui.update_uis(src) + set_scan_temp("Subject's brain is not responding. Further attempts after a short delay may succeed.", "bad") + SStgui.update_uis(src) return if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2) - scantemp = "Error: Subject has incompatible genetic mutations." - SSnanoui.update_uis(src) + set_scan_temp("Subject has incompatible genetic mutations.", "bad") + SStgui.update_uis(src) return if(!isnull(find_record(subject.ckey))) - scantemp = "Subject already in database." - SSnanoui.update_uis(src) + set_scan_temp("Subject already in database.") + SStgui.update_uis(src) return for(var/obj/machinery/clonepod/pod in pods) if(pod.occupant && pod.clonemind == subject.mind) - scantemp = "Subject already getting cloned." - SSnanoui.update_uis(src) + set_scan_temp("Subject already getting cloned.") + SStgui.update_uis(src) return subject.dna.check_integrity() @@ -434,8 +455,8 @@ R.mind = "\ref[subject.mind]" src.records += R - scantemp = "Subject successfully scanned. " + extra_info - SSnanoui.update_uis(src) + set_scan_temp("Subject successfully scanned. [extra_info]", "good") + SStgui.update_uis(src) //Find a specific record by key. /obj/machinery/computer/cloning/proc/find_record(var/find_key) @@ -451,3 +472,30 @@ /obj/machinery/computer/cloning/proc/can_brainscan() return (scanner && scanner.scan_level > 3) + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger + */ +/obj/machinery/computer/cloning/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + +/** + * Sets a temporary scan message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * color - The color of the message: (color name) + */ +/obj/machinery/computer/cloning/proc/set_scan_temp(text = "", color = "", update_now = FALSE) + scantemp = list(text = text, color = color) + if(update_now) + SStgui.update_uis(src) + +#undef MENU_MAIN +#undef MENU_RECORDS diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 3674358f07f..c733a7c92c1 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,10 +1,12 @@ -#define MED_DATA_MAIN 1 // Main menu #define MED_DATA_R_LIST 2 // Record list #define MED_DATA_MAINT 3 // Records maintenance #define MED_DATA_RECORD 4 // Record #define MED_DATA_V_DATA 5 // Virus database #define MED_DATA_MEDBOT 6 // Medbot monitor +#define FIELD(N, V, E) list(field = N, value = V, edit = E) +#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) + /obj/machinery/computer/med_data //TODO:SANITY name = "medical records console" desc = "This can be used to check medical records." @@ -18,11 +20,45 @@ var/screen = null var/datum/data/record/active1 = null var/datum/data/record/active2 = null - var/temp = null + var/list/temp = null var/printing = null + // The below are used to make modal generation more convenient + var/static/list/field_edit_questions + var/static/list/field_edit_choices light_color = LIGHT_COLOR_DARKBLUE +/obj/machinery/computer/med_data/Initialize() + ..() + field_edit_questions = list( + // General + "sex" = "Please select new sex:", + "age" = "Please input new age:", + "fingerprint" = "Please input new fingerprint hash:", + "p_stat" = "Please select new physical status:", + "m_stat" = "Please select new mental status:", + // Medical + "blood_type" = "Please select new blood type:", + "b_dna" = "Please input new DNA:", + "mi_dis" = "Please input new minor disabilities:", + "mi_dis_d" = "Please summarize minor disabilities:", + "ma_dis" = "Please input new major disabilities:", + "ma_dis_d" = "Please summarize major disabilities:", + "alg" = "Please input new allergies:", + "alg_d" = "Please summarize allergies:", + "cdi" = "Please input new current diseases:", + "cdi_d" = "Please summarize current diseases:", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = list("Male", "Female"), + "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), + "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), + // Medical + "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"), + ) + /obj/machinery/computer/med_data/Destroy() active1 = null active2 = null @@ -33,7 +69,7 @@ usr.drop_item() O.forceMove(src) scan = O - ui_interact(user) + tgui_interact(user) return return ..() @@ -44,20 +80,25 @@ to_chat(user, "Unable to establish a connection: You're too far away from the station!") return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/med_data/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/med_data/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, "med_data.tmpl", name, 800, 380) + ui = new(user, src, ui_key, "MedicalRecords", "Medical Records", 800, 380, master_ui, state) ui.open() + ui.set_autoupdate(FALSE) -/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/med_data/tgui_data(mob/user) var/data[0] data["temp"] = temp data["scan"] = scan ? scan.name : null data["authenticated"] = authenticated + data["rank"] = rank data["screen"] = screen + data["printing"] = printing + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) if(authenticated) switch(screen) if(MED_DATA_R_LIST) @@ -72,17 +113,17 @@ if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) var/list/fields = list() general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") + fields[++fields.len] = FIELD("Name", active1.fields["name"], null) + fields[++fields.len] = FIELD("ID", active1.fields["id"], null) + fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") + fields[++fields.len] = FIELD("Age", active1.fields["age"], "age") + fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") + fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat") + fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat") var/list/photos = list() general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) + photos[++photos.len] = active1.fields["photo-south"] + photos[++photos.len] = active1.fields["photo-west"] general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) general["empty"] = 0 else @@ -93,17 +134,17 @@ if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) var/list/fields = list() medical["fields"] = fields - fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) - fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) + fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["blood_type"], "blood_type", FALSE) + fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE) + fields[++fields.len] = MED_FIELD("Minor Disabilities", active2.fields["mi_dis"], "mi_dis", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["mi_dis_d"], "mi_dis_d", TRUE) + fields[++fields.len] = MED_FIELD("Major Disabilities", active2.fields["ma_dis"], "ma_dis", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["ma_dis_d"], "ma_dis_d", TRUE) + fields[++fields.len] = MED_FIELD("Allergies", active2.fields["alg"], "alg", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["alg_d"], "alg_d", TRUE) + fields[++fields.len] = MED_FIELD("Current Diseases", active2.fields["cdi"], "cdi", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["cdi_d"], "cdi_d", TRUE) + fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE) if(!active2.fields["comments"] || !islist(active2.fields["comments"])) active2.fields["comments"] = list() medical["comments"] = active2.fields["comments"] @@ -127,7 +168,9 @@ var/turf/T = get_turf(M) if(T) var/medbot = list() + var/area/A = get_area(T) medbot["name"] = M.name + medbot["area"] = A.name medbot["x"] = T.x medbot["y"] = T.y medbot["on"] = M.on @@ -138,395 +181,290 @@ else medbot["use_beaker"] = 0 data["medbots"] += list(medbot) + + data["modal"] = tgui_modal_data(src) return data -/obj/machinery/computer/med_data/Topic(href, href_list) +/obj/machinery/computer/med_data/tgui_act(action, params) if(..()) - return 1 + return + if(stat & (NOPOWER|BROKEN)) + return if(!GLOB.data_core.general.Find(active1)) active1 = null if(!GLOB.data_core.medical.Find(active2)) active2 = null - if(href_list["temp"]) - temp = null + . = TRUE + if(tgui_act_modal(action, params)) + return - if(href_list["temp_action"]) - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in GLOB.data_core.medical) - qdel(R) - setTemp("

All records deleted.

") - if("p_stat") - if(active1) - switch(temp_href[2]) - if("deceased") - active1.fields["p_stat"] = "*Deceased*" - if("ssd") - active1.fields["p_stat"] = "*SSD*" - if("active") - active1.fields["p_stat"] = "Active" - if("unfit") - active1.fields["p_stat"] = "Physically Unfit" - if("disabled") - active1.fields["p_stat"] = "Disabled" - if("m_stat") - if(active1) - switch(temp_href[2]) - if("insane") - active1.fields["m_stat"] = "*Insane*" - if("unstable") - active1.fields["m_stat"] = "*Unstable*" - if("watch") - active1.fields["m_stat"] = "*Watch*" - if("stable") - active1.fields["m_stat"] = "Stable" - if("blood_type") - if(active2) - switch(temp_href[2]) - if("an") - active2.fields["blood_type"] = "A-" - if("bn") - active2.fields["blood_type"] = "B-" - if("abn") - active2.fields["blood_type"] = "AB-" - if("on") - active2.fields["blood_type"] = "O-" - if("ap") - active2.fields["blood_type"] = "A+" - if("bp") - active2.fields["blood_type"] = "B+" - if("abp") - active2.fields["blood_type"] = "AB+" - if("op") - active2.fields["blood_type"] = "O+" - if("del_r2") - QDEL_NULL(active2) - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null + switch(action) + if("cleartemp") + temp = null + if("scan") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + if("login") + var/login_type = text2num(params["login_type"]) + if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + if(authenticated) + active1 = null + active2 = null + screen = MED_DATA_R_LIST else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I + . = FALSE - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - - if(authenticated) - active1 = null - active2 = null - screen = MED_DATA_MAIN + if(.) + return if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null + . = TRUE + switch(action) + if("logout") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + authenticated = null + screen = null + active1 = null + active2 = null + if("screen") + screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT) + active1 = null + active2 = null + if("vir") + var/type = text2path(params["vir"] || "") + if(!ispath(type, /datum/disease)) + return - if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = MED_DATA_MAIN + var/datum/disease/D = new type(0) + var/list/payload = list( + name = D.name, + max_stages = D.max_stages, + spread_text = D.spread_text, + cure = D.cure_text || "None", + desc = D.desc, + severity = D.severity + ); + tgui_modal_message(src, "virus", "", null, payload) + qdel(D) + if("del_all") + for(var/datum/data/record/R in GLOB.data_core.medical) + qdel(R) + set_temp("All medical records deleted.") + if("del_r") + if(active2) + set_temp("Medical record deleted.") + qdel(active2) + if("d_rec") + var/datum/data/record/general_record = locate(params["d_rec"] || "") + if(!GLOB.data_core.general.Find(general_record)) + set_temp("Record not found.", "danger") + return - active1 = null - active2 = null + var/datum/data/record/medical_record + for(var/datum/data/record/M in GLOB.data_core.medical) + if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) + medical_record = M + break - if(href_list["vir"]) - var/type = href_list["vir"] - var/datum/disease/D = new type(0) - var/afs = "" - for(var/mob/M in D.viable_mobtypes) - afs += "[initial(M.name)];" - var/severity = D.severity - switch(severity) - if("Harmful", "Minor") - severity = "[severity]" - if("Medium") - severity = "[severity]" - if("Dangerous!") - severity = "[severity]" - if("BIOHAZARD THREAT!") - severity = "

[severity]

" - setTemp({"Name: [D.name] -
Number of stages: [D.max_stages] -
Spread: [D.spread_text] Transmission -
Possible Cure: [(D.cure_text||"none")] -
Affected Lifeforms:[afs]
-
Notes: [D.desc]
-
Severity: [severity]"}) - qdel(D) - - if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1") - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null) - setTemp("

Are you sure you wish to delete all records?

", buttons) - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis"] = t1 - if("mi_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis_d"] = t1 - if("ma_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis"] = t1 - if("ma_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis_d"] = t1 - if("alg") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg"] = t1 - if("alg_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg_d"] = t1 - if("cdi") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi"] = t1 - if("cdi_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("p_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null)) - setTemp("

Physical Condition

", buttons) - if("m_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null)) - setTemp("

Mental Condition

", buttons) - if("blood_type") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null)) - setTemp("

Blood Type

", buttons) - if("b_dna") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["b_dna"] = t1 - if("vir_name") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["name"] = t1 - if("vir_desc") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["description"] = t1 - - if(href_list["del_r"]) - if(active2) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

Are you sure you wish to delete the record (Medical Portion Only)?

", buttons) - - if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!GLOB.data_core.general.Find(R)) - setTemp("

Record not found!

") - return 1 - for(var/datum/data/record/E in GLOB.data_core.medical) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = MED_DATA_RECORD - - if(href_list["new"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Medical Record #[R.fields["id"]]" - R.fields["blood_type"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - GLOB.data_core.medical += R - active2 = R + active1 = general_record + active2 = medical_record screen = MED_DATA_RECORD - - if(href_list["add_c"]) - if(!istype(active2, /datum/data/record)) - return 1 - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
[t1]" - - if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["search"]) - var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null) - if(!t1 || ..()) - return 1 - active1 = null - active2 = null - t1 = lowertext(t1) - for(var/datum/data/record/R in GLOB.data_core.medical) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + if("new") + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Medical Record #[R.fields["id"]]" + R.fields["blood_type"] = "Unknown" + R.fields["b_dna"] = "Unknown" + R.fields["mi_dis"] = "None" + R.fields["mi_dis_d"] = "No minor disabilities have been declared." + R.fields["ma_dis"] = "None" + R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + R.fields["alg"] = "None" + R.fields["alg_d"] = "No allergies have been detected in this patient." + R.fields["cdi"] = "None" + R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + R.fields["notes"] = "No notes." + GLOB.data_core.medical += R active2 = R - if(!active2) - setTemp("

Could not locate record [t1].

") - else + screen = MED_DATA_RECORD + set_temp("Medical record created.", "success") + if("del_c") + var/index = text2num(params["del_c"] || "") + if(!index || !istype(active2, /datum/data/record)) + return + + var/list/comments = active2.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("search") + active1 = null + active2 = null + var/t1 = lowertext(params["t1"] || "") + if(!length(t1)) + return + + for(var/datum/data/record/R in GLOB.data_core.medical) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + active2 = R + break + if(!active2) + set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger") + return for(var/datum/data/record/E in GLOB.data_core.general) if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) active1 = E + break screen = MED_DATA_RECORD + if("print_p") + if(!printing) + printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + SStgui.update_uis(src) + addtimer(CALLBACK(src, .proc/print_finish), 5 SECONDS) + else + return FALSE - if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
Medical Record

" - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
\nSex: [active1.fields["sex"]] -
\nAge: [active1.fields["age"]] -
\nFingerprint: [active1.fields["fingerprint"]] -
\nPhysical Status: [active1.fields["p_stat"]] -
\nMental Status: [active1.fields["m_stat"]]
"} +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) + else + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("add_c") + tgui_modal_input(src, id, "Please enter your message:") else - P.info += "General Record Lost!
" - if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) - P.info += {"
\n
Medical Data
-
\nBlood Type: [active2.fields["blood_type"]] -
\nDNA: [active2.fields["b_dna"]]
\n -
\nMinor Disabilities: [active2.fields["mi_dis"]] -
\nDetails: [active2.fields["mi_dis_d"]]
\n -
\nMajor Disabilities: [active2.fields["ma_dis"]] -
\nDetails: [active2.fields["ma_dis_d"]]
\n -
\nAllergies: [active2.fields["alg"]] -
\nDetails: [active2.fields["alg_d"]]
\n -
\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
\nDetails: [active2.fields["cdi_d"]]
\n -
\nImportant Notes: -
\n\t[active2.fields["notes"]]
\n -
\n -
Comments/Log

"} - for(var/c in active2.fields["comments"]) - P.info += "[c]
" - else - P.info += "Medical Record Lost!
" - P.info += "" - P.name = "paper- 'Medical Record: [active1.fields["name"]]'" - printing = 0 - return 1 + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return -/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + if(field == "age") + var/new_age = text2num(answer) + if(new_age < AGE_MIN || new_age > AGE_MAX) + set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger") + return + answer = new_age + + if(istype(active2) && (field in active2.fields)) + active2.fields[field] = answer + else if(istype(active1) && (field in active1.fields)) + active1.fields[field] = answer + if("add_c") + if(!length(answer) || !istype(active2) || !length(authenticated)) + return + active2.fields["comments"] += list(list( + header = "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]", + text = answer + )) + else + return FALSE + else + return FALSE + +/** + * Called when the print timer finishes + */ +/obj/machinery/computer/med_data/proc/print_finish() + var/obj/item/paper/P = new /obj/item/paper(loc) + P.info = "
Medical Record

" + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
\nSex: [active1.fields["sex"]] +
\nAge: [active1.fields["age"]] +
\nFingerprint: [active1.fields["fingerprint"]] +
\nPhysical Status: [active1.fields["p_stat"]] +
\nMental Status: [active1.fields["m_stat"]]
"} + else + P.info += "General Record Lost!
" + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + P.info += {"
\n
Medical Data
+
\nBlood Type: [active2.fields["blood_type"]] +
\nDNA: [active2.fields["b_dna"]]
\n +
\nMinor Disabilities: [active2.fields["mi_dis"]] +
\nDetails: [active2.fields["mi_dis_d"]]
\n +
\nMajor Disabilities: [active2.fields["ma_dis"]] +
\nDetails: [active2.fields["ma_dis_d"]]
\n +
\nAllergies: [active2.fields["alg"]] +
\nDetails: [active2.fields["alg_d"]]
\n +
\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
\nDetails: [active2.fields["cdi_d"]]
\n +
\nImportant Notes: +
\n\t[active2.fields["notes"]]
\n +
\n +
Comments/Log

"} + for(var/c in active2.fields["comments"]) + P.info += "[c]
" + else + P.info += "Medical Record Lost!
" + P.info += "" + P.name = "paper - 'Medical Record: [active1.fields["name"]]'" + printing = FALSE + SStgui.update_uis(src) + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger, virus + */ +/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) /obj/machinery/computer/med_data/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -564,9 +502,10 @@ icon_screen = "medlaptop" density = 0 -#undef MED_DATA_MAIN #undef MED_DATA_R_LIST #undef MED_DATA_MAINT #undef MED_DATA_RECORD #undef MED_DATA_V_DATA #undef MED_DATA_MEDBOT +#undef FIELD +#undef MED_FIELD diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 6134860733a..ed3b3d95099 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -20,221 +20,210 @@ return if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) +/obj/machinery/computer/robotics/proc/is_authenticated(mob/user) + if(!istype(user)) + return FALSE if(user.can_admin_interact()) - return 1 - else if(allowed(user)) - return 1 - return 0 + return TRUE + if(allowed(user)) + return TRUE + return FALSE -/obj/machinery/computer/robotics/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) +/** + * Does this borg show up in the console + * + * Returns TRUE if a robot will show up in the console + * Returns FALSE if a robot will not show up in the console + * Arguments: + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) + if(!istype(R)) + return FALSE + if(istype(R, /mob/living/silicon/robot/drone)) + return FALSE + if(R.scrambledcodes) + return FALSE + if(!atoms_share_level(src, R)) + return FALSE + return TRUE + +/** + * Check if a user can send a lockdown/detonate command to a specific borg + * + * Returns TRUE if a user can send the command (does not guarantee it will work) + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access + */ +/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) + if(!istype(user)) + return FALSE + if(!console_shows(R)) + return FALSE + if(isAI(user)) + if(R.connected_ai != user) + if(telluserwhy) + to_chat(user, "AIs can only control cyborgs which are linked to them.") + return FALSE + if(isrobot(user)) + if(R != user) + if(telluserwhy) + to_chat(user, "Cyborgs cannot control other cyborgs.") + return FALSE + return TRUE + +/** + * Check if the user is the right kind of entity to be able to hack borgs + * + * Returns TRUE if a user is a traitor AI, or aghost + * Returns FALSE otherwise + * Arguments: + * * user - The [mob/user] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack_any(mob/user) + if(!istype(user)) + return FALSE + if(user.can_admin_interact()) + return TRUE + if(!isAI(user)) + return FALSE + return (user.mind.special_role && user.mind.original == user) + +/** + * Check if the user is allowed to hack a specific borg + * + * Returns TRUE if a user can hack the specific cyborg + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) + if(!can_hack_any(user)) + return FALSE + if(!istype(R)) + return FALSE + if(R.emagged) + return FALSE + if(R.connected_ai != user) + return FALSE + return TRUE + +/obj/machinery/computer/robotics/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, "robot_control.tmpl", "Robotic Control Console", 400, 500) + ui = new(user, src, ui_key, "RoboticsControlConsole", name, 500, 460, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/list/robots = get_cyborgs(user) - if(robots.len) - data["robots"] = robots +/obj/machinery/computer/robotics/tgui_data(mob/user) + var/list/data = list() + data["auth"] = is_authenticated(user) + data["can_hack"] = can_hack_any(user) + data["cyborgs"] = list() data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - data["allowed"] = is_authenticated(user) + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(!console_shows(R)) + continue + var/area/A = get_area(R) + var/turf/T = get_turf(R) + var/list/cyborg_data = list( + name = R.name, + uid = R.UID(), + locked_down = R.lockcharge, + locstring = "[A.name] ([T.x], [T.y])", + status = R.stat, + health = round(R.health * 100 / R.maxHealth, 0.1), + charge = R.cell ? round(R.cell.percent()) : null, + cell_capacity = R.cell ? R.cell.maxcharge : null, + module = R.module ? R.module.name : "No Module Detected", + synchronization = R.connected_ai, + is_hacked = R.connected_ai && R.emagged, + hackable = can_hack(user, R), + ) + data["cyborgs"] += list(cyborg_data) + data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) return data -/obj/machinery/computer/robotics/Topic(href, href_list) +/obj/machinery/computer/robotics/tgui_act(action, params) if(..()) - return 1 - - var/mob/user = usr - if(!is_authenticated(user)) - to_chat(user, "Access denied.") return - - // Destroys the cyborg - if(href_list["detonate"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) - if(!target || !istype(target)) - return - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - // Cyborgs may blow up themselves via the console - if((isrobot(user) && user != target) || !is_authenticated(user)) - to_chat(user, "Access Denied.") - return - var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") - if(choice != "Yes") - return - if(!target || !istype(target)) - return - - // Antagonistic cyborgs? Left here for downstream - if(target.mind && target.mind.special_role && target.emagged) - to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - target.ResetSecurityCodes() - else - message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") - log_game("\[key_name(usr)] detonated [key_name(target)]!") - to_chat(target, "Self-destruct command received.") - if(target.connected_ai) - to_chat(target.connected_ai, "

ALERT - Cyborg detonation detected: [target.name]
") - spawn(10) - target.self_destruct() - - // Locks or unlocks the cyborg - else if(href_list["lockdown"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) - if(!target || !istype(target)) - return - - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(isrobot(user)) - to_chat(user, "Access Denied.") - return - - var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!") - log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!") - target.SetLockdown(!target.lockcharge) - to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") - if(target.connected_ai) - to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name]
") - - // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. - else if(href_list["hack"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) - if(!target || !istype(target)) - return - - // Antag AI checks - if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) - to_chat(user, "Access Denied.") - return - - if(target.connected_ai != user) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(target.emagged) - to_chat(user, "Robot is already hacked.") - return - - var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] emagged [key_name_admin(target)] using robotic console!") - log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!") - target.emagged = 1 - to_chat(target, "Failsafe protocols overriden. New tools available.") - - // Arms the emergency self-destruct system - else if(href_list["arm"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied.") - return - - safety = !safety - to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct.") - - // Destroys all accessible cyborgs if safety is disabled - else if(href_list["nuke"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied") - return - if(safety) - to_chat(user, "Self-destruct aborted - safety active") - return - - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("\[key_name(usr)] detonated all cyborgs!") - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue + . = FALSE + if(!is_authenticated(usr)) + to_chat(usr, "Access denied.") + return + switch(action) + if("arm") // Arms the emergency self-destruct system + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + safety = !safety + to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.") + . = TRUE + if("nuke") // Destroys all accessible cyborgs if safety is disabled + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + if(safety) + to_chat(usr, "Self-destruct aborted - safety active") + return + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("\[key_name(usr)] detonated all cyborgs!") + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

ALERT - Cyborg detonation detected: [R.name]
") + R.self_destruct() + . = TRUE + if("killbot") // destroys one specific cyborg + var/mob/living/silicon/robot/R = locateUID(params["uid"]) + if(!can_control(usr, R, TRUE)) + return + if(R.mind && R.mind.special_role && R.emagged) + to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + R.ResetSecurityCodes() + . = TRUE + return + var/turf/T = get_turf(R) + message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!") + log_game("\[key_name(usr)] detonated [key_name(R)]!") to_chat(R, "Self-destruct command received.") if(R.connected_ai) to_chat(R.connected_ai, "

ALERT - Cyborg detonation detected: [R.name]
") - spawn(10) - R.self_destruct() - -// Proc: get_cyborgs() -// Parameters: 1 (operator - mob which is operating the console.) -// Description: Returns NanoUI-friendly list of accessible cyborgs. -/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) - var/list/robots = list() - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - // Ignore drones - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - - var/list/robot = list() - robot["name"] = R.name - if(R.stat) - robot["status"] = "Not Responding" - else if(!R.canmove) - robot["status"] = "Lockdown" - else - robot["status"] = "Operational" - - if(R.cell) - robot["cell"] = 1 - robot["cell_capacity"] = R.cell.maxcharge - robot["cell_current"] = R.cell.charge - robot["cell_percentage"] = round(R.cell.percent()) - else - robot["cell"] = 0 - - var/turf/pos = get_turf(R) - var/area/bot_area = get_area(R) - robot["xpos"] = pos.x - robot["ypos"] = pos.y - robot["zpos"] = pos.z - robot["area"] = format_text(bot_area.name) - - robot["health"] = round(R.health * 100 / R.maxHealth,0.1) - - robot["module"] = R.module ? R.module.name : "None" - robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" - robot["hackable"] = 0 - // Antag AIs know whether linked cyborgs are hacked or not. - if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) - robot["hacked"] = R.emagged ? 1 : 0 - robot["hackable"] = R.emagged? 0 : 1 - robots.Add(list(robot)) - return robots - -// Proc: get_cyborg_by_name() -// Parameters: 1 (name - Cyborg we are trying to find) -// Description: Helper proc for finding cyborg by name -/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) - if(!name) - return - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.name == name) - return R + R.self_destruct() + . = TRUE + if("stopbot") // lock or unlock the borg + if(isrobot(usr)) + to_chat(usr, "Access Denied.") + return + var/mob/living/silicon/robot/R = locateUID(params["uid"]) + if(!can_control(usr, R, TRUE)) + return + message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") + log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") + R.SetLockdown(!R.lockcharge) + to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") + if(R.connected_ai) + to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
") + . = TRUE + if("hackbot") // AIs hacking/emagging a borg + var/mob/living/silicon/robot/R = locateUID(params["uid"]) + if(!can_hack(usr, R)) + return + var/choice = input("Really hack [R.name]? This cannot be undone.") in list("Yes", "No") + if(choice != "Yes") + return + log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") + message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") + R.emagged = TRUE + to_chat(R, "Failsafe protocols overriden. New tools available.") + . = TRUE 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/cryo.dm b/code/game/machinery/cryo.dm index 8be09ef30b7..ba8f430937c 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -103,7 +103,7 @@ beaker.forceMove(drop_location()) beaker = null -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O, mob/living/user) if(O.loc == user) //no you can't pull things out of your ass return if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other @@ -140,6 +140,7 @@ add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL) if(user.pulling == L) user.stop_pulling() + SStgui.update_uis(src) /obj/machinery/atmospherics/unary/cryo_cell/process() ..() @@ -177,14 +178,14 @@ return FALSE -/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) +/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user) if(user.stat) return go_out() return /obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) - return attack_hand(user) + tgui_interact(user) /obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) if(user == occupant) @@ -194,36 +195,18 @@ to_chat(usr, "Close the maintenance panel first.") return - ui_interact(user) + tgui_interact(user) - - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui - * - * @return nothing - */ -/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // 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/atmospherics/unary/cryo_cell/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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 480) - // open the new ui window + ui = new(user, src, ui_key, "Cryo", "Cryo Cell", 520, 490) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user) var/data[0] data["isOperating"] = on - data["hasOccupant"] = occupant ? 1 : 0 + data["hasOccupant"] = occupant ? TRUE : FALSE var/occupantData[0] if(occupant) @@ -246,7 +229,7 @@ else if(air_contents.temperature > TCRYO) data["cellTemperatureStatus"] = "average" - data["isBeakerLoaded"] = beaker ? 1 : 0 + data["isBeakerLoaded"] = beaker ? TRUE : FALSE data["beakerLabel"] = null data["beakerVolume"] = 0 if(beaker) @@ -259,48 +242,44 @@ data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE return data -/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) - if(usr == occupant) - return 0 // don't update UIs attached to this object +/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params) + if(..() || usr == occupant) + return + if(stat & (NOPOWER|BROKEN)) + return - if(..()) - return 0 // don't update UIs attached to this object - - if(href_list["switchOn"]) - on = TRUE - update_icon() - - if(href_list["switchOff"]) - on = FALSE - update_icon() - - if(href_list["auto_eject_healthy_on"]) - auto_eject_prefs |= AUTO_EJECT_HEALTHY - - if(href_list["auto_eject_healthy_off"]) - auto_eject_prefs &= ~AUTO_EJECT_HEALTHY - - if(href_list["auto_eject_dead_on"]) - auto_eject_prefs |= AUTO_EJECT_DEAD - - if(href_list["auto_eject_dead_off"]) - auto_eject_prefs &= ~AUTO_EJECT_DEAD - - if(href_list["ejectBeaker"]) - if(beaker) + . = TRUE + switch(action) + if("switchOn") + on = TRUE + update_icon() + if("switchOff") + on = FALSE + update_icon() + if("auto_eject_healthy_on") + auto_eject_prefs |= AUTO_EJECT_HEALTHY + if("auto_eject_healthy_off") + auto_eject_prefs &= ~AUTO_EJECT_HEALTHY + if("auto_eject_dead_on") + auto_eject_prefs |= AUTO_EJECT_DEAD + if("auto_eject_dead_off") + auto_eject_prefs &= ~AUTO_EJECT_DEAD + if("ejectBeaker") + if(!beaker) + return beaker.forceMove(get_step(loc, SOUTH)) beaker = null - - if(href_list["ejectOccupant"]) - if(!occupant || isslime(usr) || ispAI(usr)) - return 0 // don't update UIs attached to this object - add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) - go_out() + if("ejectOccupant") + if(!occupant || isslime(usr) || ispAI(usr)) + return + add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) + go_out() + else + return FALSE add_fingerprint(usr) - return 1 // update UIs attached to this object -/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params) +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G, var/mob/user, params) if(istype(G, /obj/item/reagent_containers/glass)) var/obj/item/reagent_containers/B = G if(beaker) @@ -313,6 +292,7 @@ beaker = B add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]") user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!") + SStgui.update_uis(src) return if(exchange_parts(user, G)) @@ -357,7 +337,7 @@ return if(occupant) - var/image/pickle = image(occupant.icon, occupant.icon_state) + var/mutable_appearance/pickle = mutable_appearance(occupant.icon, occupant.icon_state) pickle.overlays = occupant.overlays pickle.pixel_y = 22 @@ -455,8 +435,9 @@ playsound(loc, 'sound/machines/ding.ogg', 50, 1) if(AUTO_EJECT_DEAD) playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) + SStgui.update_uis(src) -/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) +/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M) if(!istype(M)) to_chat(usr, "The cryo cell cannot handle such a lifeform!") return @@ -530,7 +511,7 @@ /datum/data/function/proc/reset() return -/datum/data/function/proc/r_input(href, href_list, mob/user as mob) +/datum/data/function/proc/r_input(href, href_list, mob/user) return /datum/data/function/proc/display() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 42ff08ff00f..3f074c9051d 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 = FALSE + to_chat(usr, "The door bolt lights have been disabled.") + else if(!lights) + lights = TRUE + 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/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 9970974e4ff..36eba1a6ee5 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -86,8 +86,8 @@ Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]" Radio.autosay(announcetext, name, "Security", list(z)) - if(prisoner_trank != "unknown") - notify_dept_head(prisoner_trank, announcetext) + if(prisoner_trank != "unknown" && prisoner_trank != "Civilian") + SSjobs.notify_dept_head(prisoner_trank, announcetext) if(R) prisoner = R @@ -104,31 +104,6 @@ update_all_mob_security_hud() return 1 - -/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext) - if(!jobtitle || !antext) - return - if(jobtitle == "Civilian") - // Don't notify the HoP about greytiding civilians - return - var/datum/job/brigged_job = SSjobs.GetJob(jobtitle) - if(!brigged_job) - return - if(!brigged_job.department_head[1]) - return - var/boss_title = brigged_job.department_head[1] - - var/obj/item/pda/target_pda - for(var/obj/item/pda/check_pda in GLOB.PDAs) - if(check_pda.ownrank == boss_title) - target_pda = check_pda - if(!target_pda) - return - var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) - if(PM && PM.can_receive()) - PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)") - - /obj/machinery/door_timer/Initialize() ..() 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/portable_tag_turret.dm b/code/game/machinery/portable_tag_turret.dm index 12bae1bbf5a..4b22315f387 100644 --- a/code/game/machinery/portable_tag_turret.dm +++ b/code/game/machinery/portable_tag_turret.dm @@ -6,6 +6,14 @@ // Reasonable defaults, in case someone manually spawns us var/lasercolor = "r" //Something to do with lasertag turrets, blame Sieve for not adding a comment. installation = /obj/item/gun/energy/laser/tag/red + targetting_is_configurable = FALSE + lethal_is_configurable = FALSE + shot_delay = 30 + iconholder = 1 + has_cover = FALSE + always_up = TRUE + raised = TRUE + req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) /obj/machinery/porta_turret/tag/red @@ -18,43 +26,15 @@ icon_state = "[lasercolor]grey_target_prism" /obj/machinery/porta_turret/tag/weapon_setup(var/obj/item/gun/energy/E) - switch(E.type) - if(/obj/item/gun/energy/laser/tag/blue) - eprojectile = /obj/item/gun/energy/laser/tag/blue - lasercolor = "b" - req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - check_arrest = 0 - check_records = 0 - check_weapons = 1 - check_access = 0 - check_anomalies = 0 - shot_delay = 30 + return - if(/obj/item/gun/energy/laser/tag/red) - eprojectile = /obj/item/gun/energy/laser/tag/red - lasercolor = "r" - req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - check_arrest = 0 - check_records = 0 - check_weapons = 1 - check_access = 0 - check_anomalies = 0 - shot_delay = 30 - iconholder = 1 - -/obj/machinery/porta_turret/tag/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, "turret_control.tmpl", "Turret Controls", 500, 300) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/porta_turret/tag/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - data["is_lethal"] = 0 +/obj/machinery/porta_turret/tag/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, // is turret turned on? + "lethal" = FALSE, + "lethal_is_configurable" = lethal_is_configurable + ) return data /obj/machinery/porta_turret/tag/update_icon() diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 79f1eb83e23..346008fce3f 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -7,18 +7,18 @@ name = "turret" icon = 'icons/obj/turrets.dmi' icon_state = "turretCover" - anchored = 1 - density = 0 + anchored = TRUE + density = FALSE use_power = IDLE_POWER_USE //this turret uses and requires power idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power power_channel = EQUIP //drains power from the EQUIPMENT channel armor = list(melee = 50, bullet = 30, laser = 30, energy = 30, bomb = 30, bio = 0, rad = 0, fire = 90, acid = 90) - var/raised = 0 //if the turret cover is "open" and the turret is raised - var/raising= 0 //if the turret is currently opening or closing its cover + var/raised = FALSE //if the turret cover is "open" and the turret is raised + var/raising= FALSE //if the turret is currently opening or closing its cover var/health = 80 //the turret's health - var/locked = 1 //if the turret's behaviour control access is locked - var/controllock = 0 //if the turret responds to control panels + var/locked = TRUE //if the turret's behaviour control access is locked + var/controllock = FALSE //if the turret responds to control panels. TRUE = does NOT respond var/installation = /obj/item/gun/energy/gun/turret //the type of weapon installed var/gun_charge = 0 //the charge of the gun inserted @@ -31,68 +31,49 @@ var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire var/shot_delay = 15 //1.5 seconds between each shot - var/check_arrest = 1 //checks if the perp is set to arrest - var/check_records = 1 //checks if a security record exists at all - var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg - var/ailock = 0 // AI cannot use this + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_borgs = FALSE //if TRUE, target all cyborgs. + var/ailock = FALSE // if TRUE, AI cannot use this - var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!) + var/attacked = FALSE //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!) - var/enabled = 1 //determines if the turret is on - var/lethal = 0 //whether in lethal or stun mode - var/disabled = 0 + var/enabled = TRUE //determines if the turret is on + var/lethal = FALSE //whether in lethal or stun mode + var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed + var/disabled = FALSE var/shot_sound //what sound should play when the turret fires var/eshot_sound //what sound should play when the emagged turret fires var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks? - var/wrenching = 0 + var/wrenching = FALSE var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range - var/screen = 0 // Screen 0: main control, screen 1: access levels - var/one_access = 0 // Determines if access control is set to req_one_access or req_access + var/one_access = FALSE // Determines if access control is set to req_one_access or req_access + var/region_min = REGION_GENERAL + var/region_max = REGION_COMMAND - var/syndicate = 0 //is the turret a syndicate turret? + var/syndicate = FALSE //is the turret a syndicate turret? var/faction = "" - var/emp_vulnerable = 1 // Can be empd + var/emp_vulnerable = TRUE // Can be empd var/scan_range = 7 - var/always_up = 0 //Will stay active - var/has_cover = 1 //Hides the cover + var/always_up = FALSE //Will stay active + var/has_cover = TRUE //Hides the cover -/obj/machinery/porta_turret/centcom - name = "Centcom Turret" - enabled = 0 - ailock = 1 - check_synth = 0 - check_access = 1 - check_arrest = 1 - check_records = 1 - check_weapons = 1 - check_anomalies = 1 - -/obj/machinery/porta_turret/centcom/pulse - name = "Pulse Turret" - health = 200 - enabled = 1 - lethal = 1 - req_access = list(ACCESS_CENT_COMMANDER) - installation = /obj/item/gun/energy/pulse/turret - -/obj/machinery/porta_turret/stationary - ailock = 1 - lethal = 1 - installation = /obj/item/gun/energy/laser /obj/machinery/porta_turret/Initialize(mapload) . = ..() if(req_access && req_access.len) req_access.Cut() req_one_access = list(ACCESS_SECURITY, ACCESS_HEADS) - one_access = 1 + one_access = TRUE //Sets up a spark system spark_system = new /datum/effect_system/spark_spread @@ -110,7 +91,7 @@ if(req_one_access && req_one_access.len) req_one_access.Cut() req_access = list(ACCESS_CENT_SPECOPS) - one_access = 0 + one_access = FALSE /obj/machinery/porta_turret/proc/setup() var/obj/item/gun/energy/E = new installation //All energy-based weapons are applicable @@ -185,152 +166,151 @@ GLOBAL_LIST_EMPTY(turret_icons) else icon_state = "turretCover" -/obj/machinery/porta_turret/proc/isLocked(mob/user) - if(ailock && (isrobot(user) || isAI(user))) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return 1 - - if(locked && !(isrobot(user) || isAI(user) || isobserver(user))) - to_chat(user, "Access denied.") - return 1 - - return 0 - -/obj/machinery/porta_turret/attack_ai(mob/user) - if(isLocked(user)) - return - - ui_interact(user) - -/obj/machinery/porta_turret/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/porta_turret/attack_hand(mob/user) - if(isLocked(user)) - return - - ui_interact(user) - -/obj/machinery/porta_turret/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, "turret_control.tmpl", "Turret Controls", 500, 320) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/porta_turret/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["access"] = !isLocked(user) - data["screen"] = screen - data["locked"] = locked - data["enabled"] = enabled - data["lethal_control"] = !syndicate ? 1 : 0 - data["lethal"] = lethal - - if(data["access"] && !syndicate) - var/settings[0] - settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth) - settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons) - settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records) - settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest) - settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) - settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) - data["settings"] = settings - - if(!syndicate) - data["one_access"] = one_access - var/accesses[0] - var/list/access_list = get_all_accesses() - for(var/access in access_list) - var/name = get_access_desc(access) - var/active - if(one_access) - active = (access in req_one_access) - else - active = (access in req_access) - accesses[++accesses.len] = list("name" = name, "active" = active, "number" = access) - data["accesses"] = accesses - return data - /obj/machinery/porta_turret/proc/HasController() var/area/A = get_area(src) return A && A.turret_controls.len > 0 -/obj/machinery/porta_turret/CanUseTopic(var/mob/user) +/obj/machinery/porta_turret/proc/access_is_configurable() + return targetting_is_configurable && !HasController() + +/obj/machinery/porta_turret/proc/isLocked(mob/user) if(HasController()) - to_chat(user, "Turrets can only be controlled using the assigned turret controller.") - return STATUS_CLOSE + return TRUE + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE + if(isobserver(user)) + if(user.can_admin_interact()) + return FALSE + else + return TRUE + if(locked) + return TRUE + return FALSE - if(isLocked(user)) - return STATUS_CLOSE +/obj/machinery/porta_turret/attack_ai(mob/user) + tgui_interact(user) - if(!anchored) - to_chat(usr, "\The [src] has to be secured first!") - return STATUS_CLOSE +/obj/machinery/porta_turret/attack_ghost(mob/user) + tgui_interact(user) - return ..() +/obj/machinery/porta_turret/attack_hand(mob/user) + tgui_interact(user) -/obj/machinery/porta_turret/Topic(href, href_list, var/nowindow = 0) - if(..()) - return 1 - - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - else if(syndicate) - return 1 - else if(href_list["command"] == "screen") - screen = value - else if(href_list["command"] == "lethal") - lethal = value - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - else if(href_list["command"] == "check_records") - check_records = value - else if(href_list["command"] == "check_arrest") - check_arrest = value - else if(href_list["command"] == "check_access") - check_access = value - else if(href_list["command"] == "check_anomalies") - check_anomalies = value - - if(!syndicate) - if(href_list["one_access"]) - toggle_one_access(href_list["one_access"]) - - if(href_list["access"]) - toggle_access(href_list["access"]) - - return 1 - -/obj/machinery/porta_turret/proc/toggle_one_access(var/access) - one_access = text2num(access) - - if(one_access == 1) - req_one_access = req_access.Copy() - req_access.Cut() - else if(one_access == 0) - req_access = req_one_access.Copy() - req_one_access.Cut() - -/obj/machinery/porta_turret/proc/toggle_access(var/access) - var/required = text2num(access) - if(!(required in get_all_accesses())) +/obj/machinery/porta_turret/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) + if(HasController()) + to_chat(user, "[src] can only be controlled using the assigned turret controller.") return + if(!anchored) + to_chat(user, "[src] has to be secured first!") + return + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "PortableTurret", name, 500, access_is_configurable() ? 800 : 400) + ui.open() - if(one_access) - if((required in req_one_access)) - req_one_access -= required - else - req_one_access += required - else - if((required in req_access)) - req_access -= required - else - req_access += required +/obj/machinery/porta_turret/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "one_access" = one_access, + "selectedAccess" = one_access ? req_one_access : req_access, + "access_is_configurable" = access_is_configurable(), + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_cyborgs" = check_borgs + ) + return data + +/obj/machinery/porta_turret/tgui_static_data(mob/user) + var/list/data = list() + data["regions"] = get_accesslist_static_data(region_min, region_max) + return data + +/obj/machinery/porta_turret/tgui_act(action, params) + if (..()) + return + if(isLocked(usr)) + return + . = TRUE + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authborgs") + check_borgs = !check_borgs + if("set") + var/access = text2num(params["access"]) + if(one_access) + if(!(access in req_one_access)) + req_one_access += access + else + req_one_access -= access + else + if(!(access in req_access)) + req_access += access + else + req_access -= access + if(access_is_configurable()) + switch(action) + if("grant_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + if(one_access) + req_one_access |= get_region_accesses(region) + else + req_access |= get_region_accesses(region) + if("deny_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + if(one_access) + req_one_access -= get_region_accesses(region) + else + req_access -= get_region_accesses(region) + if("clear_all") + if(one_access) + req_one_access = list() + else + req_access = list() + if("grant_all") + if(one_access) + req_one_access = get_all_accesses() + else + req_access = get_all_accesses() + if("one_access") + if(one_access) + req_one_access = list() + else + req_access = list() + one_access = !one_access /obj/machinery/porta_turret/power_change() if(powered() || !use_power) @@ -379,23 +359,25 @@ GLOBAL_LIST_EMPTY(turret_icons) "You begin [anchored ? "un" : ""]securing the turret." \ ) - wrenching = 1 + wrenching = TRUE if(do_after(user, 50 * I.toolspeed, target = src)) //This code handles moving the turret around. After all, it's a portable turret! if(!anchored) playsound(loc, I.usesound, 100, 1) - anchored = 1 + anchored = TRUE update_icon() to_chat(user, "You secure the exterior bolts on the turret.") else if(anchored) playsound(loc, I.usesound, 100, 1) - anchored = 0 + anchored = FALSE to_chat(user, "You unsecure the exterior bolts on the turret.") update_icon() - wrenching = 0 + wrenching = FALSE else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) - if(allowed(user)) + if(HasController()) + to_chat(user, "Turrets regulated by a nearby turret controller are not unlockable.") + else if(allowed(user)) locked = !locked to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") updateUsrDialog() @@ -409,9 +391,9 @@ GLOBAL_LIST_EMPTY(turret_icons) playsound(src.loc, 'sound/weapons/smash.ogg', 60, 1) if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off if(!attacked && !emagged) - attacked = 1 + attacked = TRUE spawn(60) - attacked = 0 + attacked = FALSE ..() @@ -445,12 +427,12 @@ GLOBAL_LIST_EMPTY(turret_icons) if(user) to_chat(user, "You short out [src]'s threat assessment circuits.") visible_message("[src] hums oddly...") - emagged = 1 + emagged = TRUE iconholder = 1 - controllock = 1 - enabled = 0 //turns off the turret temporarily + controllock = TRUE + enabled = FALSE //turns off the turret temporarily sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - enabled = 1 //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here + enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here /obj/machinery/porta_turret/take_damage(force) if(!raised && !raising) @@ -470,9 +452,9 @@ GLOBAL_LIST_EMPTY(turret_icons) if(enabled) if(!attacked && !emagged) - attacked = 1 + attacked = TRUE spawn(60) - attacked = 0 + attacked = FALSE ..() @@ -489,12 +471,12 @@ GLOBAL_LIST_EMPTY(turret_icons) check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on check_anomalies = prob(50) if(prob(5)) - emagged = 1 + emagged = TRUE enabled=0 - spawn(rand(60,600)) + spawn(rand(60, 600)) if(!enabled) - enabled=1 + enabled = TRUE ..() @@ -585,8 +567,8 @@ GLOBAL_LIST_EMPTY(turret_icons) if(get_turf(L) == get_turf(src)) return TURRET_NOT_TARGET - if(!emagged && !syndicate && (issilicon(L) || isbot(L))) // Don't target silica - return TURRET_NOT_TARGET + if(!emagged && !syndicate && (issilicon(L) || isbot(L))) + return (check_borgs && isrobot(L)) ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET if(L.stat && !emagged) //if the perp is dead/dying, no need to bother really return TURRET_NOT_TARGET //move onto next potential victim! @@ -643,7 +625,7 @@ GLOBAL_LIST_EMPTY(turret_icons) return if(stat & BROKEN) return - set_raised_raising(raised, 1) + set_raised_raising(raised, TRUE) playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1) update_icon() @@ -653,7 +635,7 @@ GLOBAL_LIST_EMPTY(turret_icons) sleep(10) qdel(flick_holder) - set_raised_raising(1, 0) + set_raised_raising(TRUE, FALSE) update_icon() /obj/machinery/porta_turret/proc/popDown() //pops the turret down @@ -664,7 +646,7 @@ GLOBAL_LIST_EMPTY(turret_icons) return if(stat & BROKEN) return - set_raised_raising(raised, 1) + set_raised_raising(raised, TRUE) playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1) update_icon() @@ -674,7 +656,7 @@ GLOBAL_LIST_EMPTY(turret_icons) sleep(10) qdel(flick_holder) - set_raised_raising(0, 0) + set_raised_raising(FALSE, FALSE) update_icon() /obj/machinery/porta_turret/on_assess_perp(mob/living/carbon/human/perp) @@ -739,6 +721,27 @@ GLOBAL_LIST_EMPTY(turret_icons) A.throw_at(target, scan_range, 1) return A +/obj/machinery/porta_turret/centcom + name = "Centcom Turret" + enabled = FALSE + ailock = TRUE + check_synth = FALSE + check_access = TRUE + check_arrest = TRUE + check_records = TRUE + check_weapons = TRUE + check_anomalies = TRUE + region_max = REGION_CENTCOMM // Non-turretcontrolled turrets at CC can have their access customized to check for CC accesses. + +/obj/machinery/porta_turret/centcom/pulse + name = "Pulse Turret" + health = 200 + enabled = TRUE + lethal = TRUE + lethal_is_configurable = FALSE + req_access = list(ACCESS_CENT_COMMANDER) + installation = /obj/item/gun/energy/pulse/turret + /datum/turret_checks var/enabled var/lethal @@ -748,6 +751,7 @@ GLOBAL_LIST_EMPTY(turret_icons) var/check_arrest var/check_weapons var/check_anomalies + var/check_borgs var/ailock /obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) @@ -763,6 +767,7 @@ GLOBAL_LIST_EMPTY(turret_icons) check_arrest = TC.check_arrest check_weapons = TC.check_weapons check_anomalies = TC.check_anomalies + check_borgs = TC.check_borgs ailock = TC.ailock power_change() @@ -791,7 +796,7 @@ GLOBAL_LIST_EMPTY(turret_icons) if(istype(I, /obj/item/wrench) && !anchored) playsound(loc, I.usesound, 100, 1) to_chat(user, "You secure the external bolts.") - anchored = 1 + anchored = TRUE build_step = 1 return @@ -816,7 +821,7 @@ GLOBAL_LIST_EMPTY(turret_icons) else if(istype(I, /obj/item/wrench)) playsound(loc, I.usesound, 75, 1) to_chat(user, "You unfasten the external bolts.") - anchored = 0 + anchored = FALSE build_step = 0 return @@ -841,8 +846,10 @@ GLOBAL_LIST_EMPTY(turret_icons) gun_charge = E.cell.charge //the gun's charge is stored in gun_charge to_chat(user, "You add [I] to the turret.") - if(istype(installation, /obj/item/gun/energy/laser/tag/blue) || istype(installation, /obj/item/gun/energy/laser/tag/red)) - target_type = /obj/machinery/porta_turret/tag + if(istype(E, /obj/item/gun/energy/laser/tag/blue)) + target_type = /obj/machinery/porta_turret/tag/blue + else if(istype(E, /obj/item/gun/energy/laser/tag/red)) + target_type = /obj/machinery/porta_turret/tag/red else target_type = /obj/machinery/porta_turret @@ -934,7 +941,7 @@ GLOBAL_LIST_EMPTY(turret_icons) Turret.name = finish_name Turret.installation = installation Turret.gun_charge = gun_charge - Turret.enabled = 0 + Turret.enabled = FALSE Turret.setup() qdel(src) @@ -981,25 +988,27 @@ GLOBAL_LIST_EMPTY(turret_icons) var/icon_state_active = "syndieturret1" var/icon_state_destroyed = "syndieturret2" - syndicate = 1 + syndicate = TRUE installation = null - always_up = 1 + always_up = TRUE use_power = NO_POWER_USE - has_cover = 0 - raised = 1 + has_cover = FALSE + raised = TRUE scan_range = 9 faction = "syndicate" - emp_vulnerable = 0 + emp_vulnerable = FALSE - lethal = 1 - check_arrest = 0 - check_records = 0 - check_weapons = 0 - check_access = 0 - check_anomalies = 1 - check_synth = 1 - ailock = 1 + lethal = TRUE + lethal_is_configurable = FALSE + targetting_is_configurable = FALSE + check_arrest = FALSE + check_records = FALSE + check_weapons = FALSE + check_access = FALSE + check_anomalies = TRUE + check_synth = TRUE + ailock = TRUE var/area/syndicate_depot/core/depotarea /obj/machinery/porta_turret/syndicate/die() @@ -1018,7 +1027,7 @@ GLOBAL_LIST_EMPTY(turret_icons) if(req_one_access && req_one_access.len) req_one_access.Cut() req_access = list(ACCESS_SYNDICATE) - one_access = 0 + one_access = FALSE /obj/machinery/porta_turret/syndicate/update_icon() if(stat & BROKEN) diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 82b473b50f0..6fb34254f3a 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -1,15 +1,17 @@ +#define RECHARGER_POWER_USAGE_GUN 250 +#define RECHARGER_POWER_USAGE_MISC 200 + /obj/machinery/recharger name = "recharger" icon = 'icons/obj/stationobjs.dmi' icon_state = "recharger0" desc = "A charging dock for energy based weaponry." - anchored = 1 + anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 4 active_power_usage = 200 pass_flags = PASSTABLE - var/obj/item/charging = null - var/using_power = FALSE + var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/modular_computer, /obj/item/rcs, /obj/item/bodyanalyzer) var/icon_state_off = "rechargeroff" var/icon_state_charged = "recharger2" @@ -17,6 +19,9 @@ var/icon_state_idle = "recharger0" var/recharge_coeff = 1 + var/obj/item/charging = null // The item that is being charged + var/using_power = FALSE // Whether the recharger is actually transferring power or not, used for icon + /obj/machinery/recharger/New() ..() component_parts = list() @@ -34,32 +39,32 @@ if(allowed) if(anchored) if(charging) - return 1 + return TRUE //Checks to make sure he's not in space doing it, and that the area got proper power. var/area/a = get_area(src) - if(!isarea(a) || a.power_equip == 0) + if(!isarea(a) || !a.power_equip) to_chat(user, "[src] blinks red as you try to insert [G].") - return 1 + return TRUE if(istype(G, /obj/item/gun/energy)) var/obj/item/gun/energy/E = G if(!E.can_charge) to_chat(user, "Your gun has no external power connector.") - return 1 + return TRUE if(!user.drop_item()) - return 1 + return TRUE G.forceMove(src) charging = G use_power = ACTIVE_POWER_USE + using_power = check_cell_needs_recharging(get_cell_from(G)) update_icon() else to_chat(user, "[src] isn't connected to anything!") - return 1 + return TRUE return ..() - /obj/machinery/recharger/crowbar_act(mob/user, obj/item/I) if(panel_open && !charging && default_deconstruction_crowbar(user, I)) return TRUE @@ -106,57 +111,15 @@ if(stat & (NOPOWER|BROKEN) || !anchored) return - using_power = FALSE - if(charging) - if(istype(charging, /obj/item/gun/energy)) - var/obj/item/gun/energy/E = charging - if(E.cell.charge < E.cell.maxcharge) - E.cell.give(E.cell.chargerate * recharge_coeff) - E.on_recharge() - use_power(250) - using_power = TRUE - - - if(istype(charging, /obj/item/melee/baton)) - var/obj/item/melee/baton/B = charging - if(B.cell) - if(B.cell.give(B.cell.chargerate)) - use_power(200) - using_power = TRUE - - if(istype(charging, /obj/item/modular_computer)) - var/obj/item/modular_computer/C = charging - var/obj/item/computer_hardware/battery/battery_module = C.all_components[MC_CELL] - if(battery_module) - var/obj/item/computer_hardware/battery/B = battery_module - if(B.battery) - if(B.battery.charge < B.battery.maxcharge) - B.battery.give(B.battery.chargerate) - use_power(200) - using_power = TRUE - - if(istype(charging, /obj/item/rcs)) - var/obj/item/rcs/R = charging - if(R.rcell) - if(R.rcell.give(R.rcell.chargerate)) - use_power(200) - using_power = TRUE - - if(istype(charging, /obj/item/bodyanalyzer)) - var/obj/item/bodyanalyzer/B = charging - if(B.cell) - if(B.cell.give(B.cell.chargerate)) - use_power(200) - using_power = TRUE - - update_icon(using_power) + using_power = try_recharging_if_possible() + update_icon() /obj/machinery/recharger/emp_act(severity) if(stat & (NOPOWER|BROKEN) || !anchored) ..(severity) return - if(istype(charging, /obj/item/gun/energy)) + if(istype(charging, /obj/item/gun/energy)) var/obj/item/gun/energy/E = charging if(E.cell) E.cell.emp_act(severity) @@ -167,7 +130,11 @@ B.cell.charge = 0 ..(severity) -/obj/machinery/recharger/update_icon(using_power = FALSE) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier. +/obj/machinery/recharger/power_change() + ..() + update_icon() + +/obj/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier. if(stat & (NOPOWER|BROKEN) || !anchored) icon_state = icon_state_off return @@ -179,6 +146,55 @@ return icon_state = icon_state_idle +/obj/machinery/recharger/proc/get_cell_from(obj/item/I) + if(istype(I, /obj/item/gun/energy)) + var/obj/item/gun/energy/E = I + return E.cell + + if(istype(I, /obj/item/melee/baton)) + var/obj/item/melee/baton/B = I + return B.cell + + if(istype(I, /obj/item/modular_computer)) + var/obj/item/modular_computer/C = I + var/obj/item/computer_hardware/battery/B = C.all_components[MC_CELL] + if(B) + return B.battery + + if(istype(I, /obj/item/rcs)) + var/obj/item/rcs/R = I + return R.rcell + + if(istype(I, /obj/item/bodyanalyzer)) + var/obj/item/bodyanalyzer/B = I + return B.cell + + return null + +/obj/machinery/recharger/proc/check_cell_needs_recharging(obj/item/stock_parts/cell/C) + if(!C || C.charge >= C.maxcharge) + return FALSE + return TRUE + +/obj/machinery/recharger/proc/recharge_cell(obj/item/stock_parts/cell/C, power_usage) + C.give(C.chargerate * recharge_coeff) + use_power(power_usage) + +/obj/machinery/recharger/proc/try_recharging_if_possible() + var/obj/item/stock_parts/cell/C = get_cell_from(charging) + if(!check_cell_needs_recharging(C)) + return FALSE + + if(istype(charging, /obj/item/gun/energy)) + recharge_cell(C, RECHARGER_POWER_USAGE_GUN) + + var/obj/item/gun/energy/E = charging + E.on_recharge() + else + recharge_cell(C, RECHARGER_POWER_USAGE_MISC) + + return TRUE + /obj/machinery/recharger/examine(mob/user) . = ..() if(charging && (!in_range(user, src) && !issilicon(user) && !isobserver(user))) @@ -204,3 +220,6 @@ icon_state_idle = "wrecharger0" icon_state_charging = "wrecharger1" icon_state_charged = "wrecharger2" + +#undef RECHARGER_POWER_USAGE_GUN +#undef RECHARGER_POWER_USAGE_MISC diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index e4e37ae1bcb..56019d22f7c 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -8,7 +8,7 @@ layer = MOB_LAYER+1 // Overhead anchored = 1 density = 1 - damage_deflection = 10 + damage_deflection = 15 var/safety_mode = 0 // Temporarily stops machine if it detects a mob var/icon_name = "grinder-o" var/blood = 0 @@ -92,7 +92,6 @@ if(AM) Bumped(AM) - /obj/machinery/recycler/Bumped(atom/movable/AM) if(stat & (BROKEN|NOPOWER)) 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/turret_control.dm b/code/game/machinery/turret_control.dm index 9dda3b193bb..71cb58c6478 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -11,51 +11,56 @@ desc = "Used to control a room's automated defenses." icon = 'icons/obj/machines/turret_control.dmi' icon_state = "control_standby" - anchored = 1 - density = 0 + anchored = TRUE + density = FALSE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/enabled = 0 - var/lethal = 0 - var/locked = 1 + var/enabled = FALSE + var/lethal = FALSE + var/lethal_is_configurable = TRUE + var/locked = TRUE var/area/control_area //can be area name, path or nothing. - var/check_arrest = 1 //checks if the perp is set to arrest - var/check_records = 1 //checks if a security record exists at all - var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg - var/ailock = 0 //Silicons cannot use this + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_borgs = FALSE //if TRUE, target all cyborgs. + var/ailock = FALSE //Silicons cannot use this - var/syndicate = 0 + var/syndicate = FALSE var/faction = "" // Turret controls can only access turrets that are in the same faction req_access = list(ACCESS_AI_UPLOAD) /obj/machinery/turretid/stun - enabled = 1 + enabled = TRUE icon_state = "control_stun" /obj/machinery/turretid/lethal - enabled = 1 - lethal = 1 + enabled = TRUE + lethal = TRUE icon_state = "control_kill" /obj/machinery/turretid/syndicate - enabled = 1 - lethal = 1 + enabled = TRUE + lethal = TRUE + lethal_is_configurable = FALSE + targetting_is_configurable = FALSE icon_state = "control_kill" - lethal = 1 - check_arrest = 0 - check_records = 0 - check_weapons = 0 - check_access = 0 - check_anomalies = 1 - check_synth = 1 - ailock = 1 + check_arrest = FALSE + check_records = FALSE + check_weapons = FALSE + check_access = FALSE + check_anomalies = TRUE + check_synth = TRUE + check_borgs = FALSE + ailock = TRUE - syndicate = 1 + syndicate = TRUE faction = "syndicate" req_access = list(ACCESS_SYNDICATE_LEADER) @@ -87,21 +92,23 @@ return /obj/machinery/turretid/proc/isLocked(mob/user) - if(ailock && (isrobot(user) || isAI(user))) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return 1 + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE - if(locked && !(isrobot(user) || isAI(user) || isobserver(user))) - to_chat(user, "Access denied.") - return 1 + if(isobserver(user)) + if(user.can_admin_interact()) + return FALSE + else + return TRUE - return 0 + if(locked) + return TRUE -/obj/machinery/turretid/CanUseTopic(mob/user) - if(isLocked(user)) - return STATUS_CLOSE - - return ..() + return FALSE /obj/machinery/turretid/attackby(obj/item/W, mob/user) if(stat & BROKEN) @@ -120,89 +127,82 @@ /obj/machinery/turretid/emag_act(user as mob) if(!emagged) to_chat(user, "You short out the turret controls' access analysis module.") - emagged = 1 - locked = 0 - ailock = 0 + emagged = TRUE + locked = FALSE + ailock = FALSE return /obj/machinery/turretid/attack_ai(mob/user as mob) - if(isLocked(user)) - return - - ui_interact(user) + tgui_interact(user) /obj/machinery/turretid/attack_ghost(mob/user as mob) - ui_interact(user) + tgui_interact(user) /obj/machinery/turretid/attack_hand(mob/user as mob) - if(isLocked(user)) - return + tgui_interact(user) - ui_interact(user) - -/obj/machinery/turretid/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/turretid/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, "turret_control.tmpl", "Turret Controls", 500, 350) + ui = new(user, src, ui_key, "PortableTurret", name, 500, 400) ui.open() - ui.set_auto_update(1) - -/obj/machinery/turretid/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - data["lethal_control"] = !syndicate ? 1 : 0 - data["lethal"] = lethal - - if(data["access"] && !syndicate) - var/settings[0] - settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth) - settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons) - settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records) - settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest) - settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) - settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) - data["settings"] = settings +/obj/machinery/turretid/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "one_access" = FALSE, + "selectedAccess" = list(), + "access_is_configurable" = FALSE, + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_cyborgs" = check_borgs + ) return data -/obj/machinery/turretid/Topic(href, href_list, var/nowindow = 0) - if(..()) - return 1 - +/obj/machinery/turretid/tgui_act(action, params) + if (..()) + return if(isLocked(usr)) - return 1 - - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - else if(syndicate) - return 1 - else if(href_list["command"] == "lethal") - lethal = value - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - else if(href_list["command"] == "check_records") - check_records = value - else if(href_list["command"] == "check_arrest") - check_arrest = value - else if(href_list["command"] == "check_access") - check_access = value - else if(href_list["command"] == "check_anomalies") - check_anomalies = value - - updateTurrets() - return 1 + return + . = TRUE + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authborgs") + check_borgs = !check_borgs + updateTurrets() /obj/machinery/turretid/proc/updateTurrets() var/datum/turret_checks/TC = new TC.enabled = enabled TC.lethal = lethal TC.check_synth = check_synth + TC.check_borgs = check_borgs TC.check_access = check_access TC.check_records = check_records TC.check_arrest = check_arrest diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 7aa5661febe..48b022a65f3 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -11,9 +11,6 @@ var/max_amount = 0 var/price = 0 // Price to buy one -/** - * A vending machine - */ /obj/machinery/vending name = "\improper Vendomat" desc = "A generic vending machine." @@ -25,8 +22,10 @@ max_integrity = 300 integrity_failure = 100 armor = list(melee = 20, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) - var/icon_vend //Icon_state when vending - var/icon_deny //Icon_state when denying access + /// Icon_state when vending + var/icon_vend + /// Icon_state when denying access + var/icon_deny // Power use_power = IDLE_POWER_USE @@ -34,12 +33,14 @@ var/vend_power_usage = 150 // Vending-related - var/active = 1 //No sales pitches if off! - var/vend_ready = 1 //Are we ready to vend?? Is it time?? - var/vend_delay = 10 //How long does it take to vend? - var/datum/data/vending_product/currently_vending = null // What we're requesting payment for right now - var/status_message = "" // Status screen messages like "insufficient funds", displayed in NanoUI - var/status_error = 0 // Set to 1 if status_message is an error + /// No sales pitches if off + var/active = 1 + /// If off, vendor is busy and unusable until current action finishes + var/vend_ready = TRUE + /// How long vendor takes to vend one item. + var/vend_delay = 10 + /// Item currently being bought + var/datum/data/vending_product/currently_vending = null // To be filled out at compile time var/list/products = list() // For each, use the following pattern: @@ -51,16 +52,19 @@ var/list/product_records = list() var/list/hidden_records = list() var/list/coin_records = list() + var/list/imagelist = list() - - var/list/ads_list = list() //Small ad messages in the vending screen - random chance, TODO: implementation + /// Unimplemented list of ads that are meant to show up somewhere, but don't. + var/list/ads_list = list() // Stuff relating vocalizations - var/list/slogan_list = list() //List of slogans the vendor will say, optional + /// List of slogans the vendor will say, optional + var/list/slogan_list = list() var/vend_reply //Thank you for shopping! - var/shut_up = 0 //Stop spouting those godawful pitches! + /// If true, prevent saying sales pitches + var/shut_up = FALSE ///can we access the hidden inventory? - var/extended_inventory = 0 + var/extended_inventory = FALSE var/last_reply = 0 var/last_slogan = 0 //When did we last pitch? var/slogan_delay = 6000 //How long until we can pitch again? @@ -69,17 +73,26 @@ var/obj/item/vending_refill/refill_canister = null // Things that can go wrong - emagged = 0 //Ignores if somebody doesn't have card access to that machine. - var/seconds_electrified = 0 //Shock customers like an airlock. - var/shoot_inventory = 0 //Fire items at customers! We're broken! - var/shoot_speed = 3 //How hard are we firing the items? - var/shoot_chance = 2 //How often are we firing the items? + /// Allows people to access a vendor that's normally access restricted. + emagged = 0 + /// Shocks people like an airlock + var/seconds_electrified = 0 + /// Fire items at customers! We're broken! + var/shoot_inventory = FALSE + /// How hard are we firing the items? + var/shoot_speed = 3 + /// How often are we firing the items? (prob(...)) + var/shoot_chance = 2 - var/scan_id = 1 + /// If true, enforce access checks on customers. Disabled by messing with wires. + var/scan_id = TRUE + /// Holder for a coin inserted into the vendor var/obj/item/coin/coin var/datum/wires/vending/wires = null + /// boolean, whether this vending machine can accept people inserting items into it, used for coffee vendors var/item_slot = FALSE + /// the actual item inserted var/obj/item/inserted_item = null /obj/machinery/vending/Initialize(mapload) @@ -92,6 +105,10 @@ build_inventory(products, product_records) build_inventory(contraband, hidden_records) build_inventory(premium, coin_records) + for (var/datum/data/vending_product/R in (product_records + coin_records + hidden_records)) + var/obj/item/I = R.product_path + var/pp = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-") + imagelist[pp] = "[icon2base64(icon(initial(I.icon), initial(I.icon_state)))]" if(LAZYLEN(slogan_list)) // So not all machines speak at the exact same time. // The first time this machine says something will be at slogantime + this random value, @@ -101,6 +118,7 @@ power_change() /obj/machinery/vending/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) QDEL_NULL(coin) QDEL_NULL(inserted_item) @@ -213,37 +231,16 @@ ..() /obj/machinery/vending/attackby(obj/item/I, mob/user, params) - if(currently_vending && GLOB.vendor_account && !GLOB.vendor_account.suspended) - var/paid = 0 - var/handled = 0 - if(istype(I, /obj/item/card/id)) - var/obj/item/card/id/C = I - paid = pay_with_card(C) - handled = 1 - if(istype(I, /obj/item/pda)) - var/obj/item/pda/PDA = I - if(PDA.id) - paid = pay_with_card(PDA.id) - handled = 1 - else if(istype(I, /obj/item/stack/spacecash)) - var/obj/item/stack/spacecash/C = I - paid = pay_with_cash(C, user) - handled = 1 - - if(paid) - vend(currently_vending, usr) + if(istype(I, /obj/item/coin)) + if(!premium.len) + to_chat(user, "[src] does not accept coins.") return - else if(handled) - SSnanoui.update_uis(src) - return // don't smack that machine with your 2 thalers - - if(istype(I, /obj/item/coin) && premium.len) if(!user.drop_item()) return I.forceMove(src) coin = I to_chat(user, "You insert the [I] into the [src]") - SSnanoui.update_uis(src) + SStgui.update_uis(src) return if(refill_canister && istype(I, refill_canister)) if(!panel_open) @@ -294,7 +291,7 @@ else SCREWDRIVER_CLOSE_PANEL_MESSAGE overlays.Cut() - SSnanoui.update_uis(src) // Speaker switch is on the main UI, not wires UI + SStgui.update_uis(src) /obj/machinery/vending/wirecutter_act(mob/user, obj/item/I) . = TRUE @@ -358,12 +355,10 @@ if(!user.drop_item()) to_chat(user, "[I] is stuck to your hand, you can't seem to put it down!") return - inserted_item = I I.forceMove(src) - to_chat(user, "You insert [I] into [src].") - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/vending/proc/eject_item(mob/user) if(!item_slot || !inserted_item) @@ -376,23 +371,19 @@ var/turf/T = get_turf(src) inserted_item.forceMove(T) inserted_item = null - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/vending/emag_act(user as mob) emagged = TRUE to_chat(user, "You short out the product lock on [src]") -/** - * Receive payment with cashmoney. - * - * usr is the mob who gets the change. - */ + /obj/machinery/vending/proc/pay_with_cash(obj/item/stack/spacecash/cashmoney, mob/user) if(currently_vending.price > cashmoney.amount) // This is not a status display message, since it's something the character // themselves is meant to see BEFORE putting the money in to_chat(usr, "[bicon(cashmoney)] That is not enough money.") - return 0 + return FALSE // Bills (banknotes) cannot really have worth different than face value, // so we have to eat the bill and spit out change in a bundle @@ -403,66 +394,38 @@ cashmoney.use(currently_vending.price) // Vending machines have no idea who paid with cash - credit_purchase("(cash)") - return 1 + GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", name, "(cash)") + return TRUE -/** - * Scan a card and attempt to transfer payment from associated account. - * - * Takes payment for whatever is the currently_vending item. Returns 1 if - * successful, 0 if failed - */ -/obj/machinery/vending/proc/pay_with_card(var/obj/item/card/id/I) - visible_message("[usr] swipes a card through [src].") - return pay_with_account(get_card_account(I)) -/obj/machinery/vending/proc/pay_with_account(var/datum/money_account/customer_account) +/obj/machinery/vending/proc/pay_with_card(obj/item/card/id/I, mob/M) + visible_message("[M] swipes a card through [src].") + return pay_with_account(get_card_account(I), M) + +/obj/machinery/vending/proc/pay_with_account(datum/money_account/customer_account, mob/M) if(!customer_account) - src.status_message = "Error: Unable to access account. Please contact technical support if problem persists." - src.status_error = 1 - return 0 - + to_chat(M, "Error: Unable to access account. Please contact technical support if problem persists.") + return FALSE if(customer_account.suspended) - src.status_message = "Unable to access account: account suspended." - src.status_error = 1 - return 0 - - // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is - // empty at high security levels - if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + to_chat(M, "Unable to access account: account suspended.") + return FALSE + // Have the customer punch in the PIN before checking if there's enough money. + // Prevents people from figuring out acct is empty at high security levels + if(customer_account.security_level != 0) + // If card requires pin authentication (ie seclevel 1 or 2) var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - if(!attempt_account_access(customer_account.account_number, attempt_pin, 2)) - src.status_message = "Unable to access account: incorrect credentials." - src.status_error = 1 - return 0 - + to_chat(M, "Unable to access account: incorrect credentials.") + return FALSE if(currently_vending.price > customer_account.money) - src.status_message = "Insufficient funds in account." - src.status_error = 1 - return 0 - else - // Okay to move the money at this point - var/paid = customer_account.charge(currently_vending.price, GLOB.vendor_account, - "Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name, - "Sale of [currently_vending.name]", customer_account.owner_name) + to_chat(M, "Your bank account has insufficient money to purchase this.") + return FALSE + // Okay to move the money at this point + customer_account.charge(currently_vending.price, GLOB.vendor_account, + "Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name, + "Sale of [currently_vending.name]", customer_account.owner_name) + return TRUE - if(paid) - // Give the vendor the money. We use the account owner name, which means - // that purchases made with stolen/borrowed card will look like the card - // owner made them - credit_purchase(customer_account.owner_name) - return paid - -/** - * Add money for current purchase to the vendor account. - * - * Called after the money has already been taken from the customer. - */ -/obj/machinery/vending/proc/credit_purchase(var/target as text) - GLOB.vendor_account.money += currently_vending.price - GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", - name, target) /obj/machinery/vending/attack_ai(mob/user) return attack_hand(user) @@ -478,174 +441,238 @@ if(src.shock(user, 100)) return - ui_interact(user) + tgui_interact(user) wires.Interact(user) -/** - * Display the NanoUI window for the vending machine. - * - * See NanoUI documentation for details. - */ -/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/vending/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, "vending_machine.tmpl", src.name, 440, 600) + var/estimated_height = 100 + (length(product_records) * 34) + if(length(prices) > 0) + estimated_height += 100 // to account for the "current user" interface + ui = new(user, src, ui_key, "Vending", name, 470, estimated_height, master_ui, state) ui.open() -/obj/machinery/vending/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/vending/tgui_data(mob/user) var/list/data = list() - if(currently_vending) - data["mode"] = 1 - data["product"] = sanitize(currently_vending.name) - data["price"] = currently_vending.price - data["message_err"] = 0 - data["message"] = src.status_message - data["message_err"] = src.status_error - else - data["mode"] = 0 - var/list/listed_products = list() - - var/list/display_records = product_records + coin_records - if(extended_inventory) - display_records = product_records + coin_records + hidden_records - - for(var/key = 1 to display_records.len) - var/datum/data/vending_product/I = display_records[key] - - if(coin_records.Find(I) && !coin) - continue - - if(hidden_records.Find(I) && !extended_inventory) - continue - - listed_products.Add(list(list( - "key" = key, - "name" = sanitize(I.name), - "price" = I.price, - "amount" = I.amount))) - - data["products"] = listed_products - - if(coin) - data["coin"] = coin.name - - if(item_slot) - data["item_slot"] = 1 - if(inserted_item) - data["inserted_item"] = inserted_item - else - data["inserted_item"] = null - else - data["item_slot"] = 0 - - if(panel_open) - data["panel"] = 1 - data["speaker"] = shut_up ? 0 : 1 - else - data["panel"] = 0 + var/mob/living/carbon/human/H + var/obj/item/card/id/C + data["guestNotice"] = "No valid ID card detected. Wear your ID, or present cash."; + data["userMoney"] = 0 + data["user"] = null + if(ishuman(user)) + H = user + C = H.get_idcard(TRUE) + var/obj/item/stack/spacecash/S = H.get_active_hand() + if(istype(S)) + data["userMoney"] = S.amount + data["guestNotice"] = "Accepting Cash. You have: [S.amount] credits." + else if(istype(C)) + var/datum/money_account/A = get_card_account(C) + if(istype(A)) + data["user"] = list() + data["user"]["name"] = A.owner_name + data["userMoney"] = A.money + data["user"]["job"] = (istype(C) && C.rank) ? C.rank : "No Job" + else + data["guestNotice"] = "Unlinked ID detected. Present cash to pay."; + data["stock"] = list() + for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records) + data["stock"][R.name] = R.amount + data["extended_inventory"] = extended_inventory + data["vend_ready"] = vend_ready + data["coin_name"] = coin ? coin.name : FALSE + data["panel_open"] = panel_open ? TRUE : FALSE + data["speaker"] = shut_up ? FALSE : TRUE + data["item_slot"] = item_slot // boolean + data["inserted_item_name"] = inserted_item ? inserted_item.name : FALSE return data -/obj/machinery/vending/Topic(href, href_list) - if(..()) - return 1 - if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon)) - if(!coin) - to_chat(usr, "There is no coin in this machine.") - return +/obj/machinery/vending/tgui_static_data(mob/user) + var/list/data = list() + data["chargesMoney"] = length(prices) > 0 ? TRUE : FALSE + data["product_records"] = list() + var/i = 1 + for (var/datum/data/vending_product/R in product_records) + var/list/data_pr = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + price = (R.product_path in prices) ? prices[R.product_path] : 0, + max_amount = R.max_amount, + req_coin = FALSE, + is_hidden = FALSE, + inum = i + ) + data["product_records"] += list(data_pr) + i++ + data["coin_records"] = list() + for (var/datum/data/vending_product/R in coin_records) + var/list/data_cr = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + price = (R.product_path in prices) ? prices[R.product_path] : 0, + max_amount = R.max_amount, + req_coin = TRUE, + is_hidden = FALSE, + inum = i, + premium = TRUE + ) + data["coin_records"] += list(data_cr) + i++ + data["hidden_records"] = list() + for (var/datum/data/vending_product/R in hidden_records) + var/list/data_hr = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + price = (R.product_path in prices) ? prices[R.product_path] : 0, + max_amount = R.max_amount, + req_coin = FALSE, + is_hidden = TRUE, + inum = i, + premium = TRUE + ) + data["hidden_records"] += list(data_hr) + i++ + data["imagelist"] = imagelist + return data - usr.put_in_hands(coin) - coin = null - to_chat(usr, "You remove [coin] from [src].") +/obj/machinery/vending/tgui_act(action, params) + . = ..() + if(.) + return + if(issilicon(usr) && !isrobot(usr)) + to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") + return + switch(action) + if("toggle_voice") + if(panel_open) + shut_up = !shut_up + . = TRUE + if("eject_item") + eject_item(usr) + . = TRUE + if("remove_coin") + if(!coin) + to_chat(usr, "There is no coin in this machine.") + return + if(istype(usr, /mob/living/silicon)) + to_chat(usr, "You lack hands.") + return + to_chat(usr, "You remove [coin] from [src].") + usr.put_in_hands(coin) + coin = null + . = TRUE + if("vend") + if(!vend_ready) + to_chat(usr, "The vending machine is busy!") + return + if(panel_open) + to_chat(usr, "The vending machine cannot dispense products while its service panel is open!") + return + var/key = text2num(params["inum"]) + var/list/display_records = product_records + coin_records + if(extended_inventory) + display_records = product_records + coin_records + hidden_records + if(key < 1 || key > length(display_records)) + to_chat(usr, "ERROR: invalid inum passed to vendor. Report this bug.") + return + var/datum/data/vending_product/R = display_records[key] + if(!istype(R)) + to_chat(usr, "ERROR: unknown vending_product record. Report this bug.") + return + var/list/record_to_check = product_records + coin_records + if(extended_inventory) + record_to_check = product_records + coin_records + hidden_records + if(!R || !istype(R) || !R.product_path) + to_chat(usr, "ERROR: unknown product record. Report this bug.") + return + if(R in hidden_records) + if(!extended_inventory) + // Exploit prevention, stop the user purchasing hidden stuff if they haven't hacked the machine. + to_chat(usr, "ERROR: machine does not allow extended_inventory in current state. Report this bug.") + return + else if (!(R in record_to_check)) + // Exploit prevention, stop the user + message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!") + return + if (R.amount <= 0) + to_chat(usr, "Sold out of [R.name].") + flick(icon_deny, src) + return - if(href_list["remove_item"]) - eject_item(usr) + vend_ready = FALSE // From this point onwards, vendor is locked to performing this transaction only, until it is resolved. - if(href_list["pay"]) - if(currently_vending && GLOB.vendor_account && !GLOB.vendor_account.suspended) - var/paid = 0 - var/handled = 0 - var/datum/money_account/A = usr.get_worn_id_account() - if(A) - paid = pay_with_account(A) - handled = 1 - else if(istype(usr.get_active_hand(), /obj/item/card)) - paid = pay_with_card(usr.get_active_hand()) - handled = 1 - else if(usr.can_admin_interact()) - paid = 1 - handled = 1 + if(!ishuman(usr) || R.price <= 0) + // Either the purchaser is not human, or the item is free. + // Skip all payment logic. + vend(R, usr) + add_fingerprint(usr) + vend_ready = TRUE + . = TRUE + return + + // --- THE REST OF THIS PROC IS JUST PAYMENT LOGIC --- + + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/C = H.get_idcard(TRUE) + + if(!GLOB.vendor_account || GLOB.vendor_account.suspended) + to_chat(usr, "Vendor account offline. Unable to process transaction.") + flick(icon_deny, src) + vend_ready = TRUE + return + + currently_vending = R + var/paid = FALSE + + if(istype(usr.get_active_hand(), /obj/item/stack/spacecash)) + var/obj/item/stack/spacecash/S = usr.get_active_hand() + paid = pay_with_cash(S) + else if(istype(C, /obj/item/card)) + // Because this uses H.get_idcard(TRUE), it will attempt to use: + // active hand, inactive hand, pda.id, and then wear_id ID in that order + // this is important because it lets people buy stuff with someone else's ID by holding it while using the vendor + paid = pay_with_card(C, usr) + else if(usr.can_advanced_admin_interact()) + to_chat(usr, "Vending object due to admin interaction.") + paid = TRUE + else + to_chat(usr, "Payment failure: you have no ID or other method of payment.") + vend_ready = TRUE + flick(icon_deny, src) + . = TRUE // we set this because they shouldn't even be able to get this far, and we want the UI to update. + return if(paid) vend(currently_vending, usr) - return - else if(handled) - SSnanoui.update_uis(src) - return // don't smack that machine with your 2 credits - - if((href_list["vend"]) && vend_ready && !currently_vending) - - if(issilicon(usr) && !isrobot(usr)) - to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") - return - - if(!allowed(usr) && !usr.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(usr, "Access denied.") //Unless emagged of course - flick(icon_deny,src) - return - - var/key = text2num(href_list["vend"]) - var/list/display_records = product_records + coin_records - if(extended_inventory) - display_records = product_records + coin_records + hidden_records - var/datum/data/vending_product/R = display_records[key] - - // This should not happen unless the request from NanoUI was bad - if(coin_records.Find(R) && !coin) - return - - if(hidden_records.Find(R) && !extended_inventory) - return - - if(R.price <= 0) - vend(R, usr) - else - currently_vending = R - if(!GLOB.vendor_account || GLOB.vendor_account.suspended) - status_message = "This machine is currently unable to process payments due to problems with the associated account." - status_error = 1 + . = TRUE else - status_message = "Please swipe a card or insert cash to pay for the item." - status_error = 0 + to_chat(usr, "Payment failure: unable to process payment.") + vend_ready = TRUE + if(.) + add_fingerprint(usr) - else if(href_list["cancelpurchase"]) - currently_vending = null - else if(href_list["togglevoice"] && panel_open) - shut_up = !src.shut_up - add_fingerprint(usr) - SSnanoui.update_uis(src) /obj/machinery/vending/proc/vend(datum/data/vending_product/R, mob/user) - if(!allowed(usr) && !usr.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(usr, "Access denied.")//Unless emagged of course - flick(icon_deny,src) + if(!allowed(user) && !user.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH + to_chat(user, "Access denied.")//Unless emagged of course + flick(icon_deny, src) + vend_ready = TRUE return if(!R.amount) to_chat(user, "The vending machine has ran out of that product.") + vend_ready = TRUE return - vend_ready = 0 //One thing at a time!! - status_message = "Vending..." - status_error = 0 - SSnanoui.update_uis(src) + vend_ready = FALSE //One thing at a time!! if(coin_records.Find(R)) if(!coin) to_chat(user, "You need to insert a coin to get this item.") + vend_ready = TRUE return if(coin.string_attached) if(prob(50)) @@ -665,15 +692,13 @@ use_power(vend_power_usage) //actuators and stuff if(icon_vend) //Show the vending animation if needed flick(icon_vend, src) + playsound(get_turf(src), 'sound/machines/machine_vend.ogg', 50, TRUE) addtimer(CALLBACK(src, .proc/delayed_vend, R, user), vend_delay) /obj/machinery/vending/proc/delayed_vend(datum/data/vending_product/R, mob/user) do_vend(R, user) - status_message = "" - status_error = 0 - vend_ready = 1 + vend_ready = TRUE currently_vending = null - SSnanoui.update_uis(src) //override this proc to add handling for what to do with the vended product when you have a inserted item and remember to include a parent call for this generic handling /obj/machinery/vending/proc/do_vend(datum/data/vending_product/R, mob/user) @@ -809,17 +834,6 @@ */ -/* -/obj/machinery/vending/atmospherics //Commenting this out until someone ponies up some actual working, broken, and unpowered sprites - Quarxink - name = "\improper Tank Vendor" - desc = "A vendor with a wide variety of masks and gas tanks." - icon = 'icons/obj/objects.dmi' - icon_state = "dispenser" - product_paths = "/obj/item/tank/oxygen;/obj/item/tank/plasma;/obj/item/tank/emergency_oxygen;/obj/item/tank/emergency_oxygen/engi;/obj/item/clothing/mask/breath" - product_amounts = "10;10;10;5;25" - vend_delay = 0 -*/ - /obj/machinery/vending/assist products = list( /obj/item/assembly/prox_sensor = 5,/obj/item/assembly/igniter = 3,/obj/item/assembly/signaler = 4, /obj/item/wirecutters = 1, /obj/item/cartridge/signal = 4) @@ -1385,7 +1399,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, @@ -1480,6 +1493,7 @@ /obj/item/clothing/under/victsuit/redblk = 1, /obj/item/clothing/under/victsuit/red = 1, /obj/item/clothing/suit/tailcoat = 1, + /obj/item/clothing/under/tourist_suit = 1, /obj/item/clothing/suit/draculacoat = 1, /obj/item/clothing/head/zepelli = 1, /obj/item/clothing/under/redhawaiianshirt = 1, @@ -1566,7 +1580,6 @@ desc = "Tools for tools." icon_state = "tool" icon_deny = "tool-deny" - //req_access_txt = "12" //Maintenance access products = list(/obj/item/stack/cable_coil/random = 10,/obj/item/crowbar = 5,/obj/item/weldingtool = 3,/obj/item/wirecutters = 5, /obj/item/wrench = 5,/obj/item/analyzer = 5,/obj/item/t_scanner = 5,/obj/item/screwdriver = 5) contraband = list(/obj/item/weldingtool/hugetank = 2,/obj/item/clothing/gloves/color/fyellow = 2) diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index 896ce886887..34e8648c59e 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -158,36 +158,36 @@ /obj/machinery/computer/mech_bay_power_console/attack_hand(mob/user as mob) if(..()) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/mech_bay_power_console/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/mech_bay_power_console/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) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "mech_bay_console.tmpl", "Mech Bay Control Console", 500, 325) - // open the new ui window + ui = new(user, src, ui_key, "MechBayConsole", name, 400, 150, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/computer/mech_bay_power_console/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/computer/mech_bay_power_console/tgui_act(action, params) + if(..()) + return + switch(action) + if("reconnect") + reconnect() + . = TRUE + update_icon() + +/obj/machinery/computer/mech_bay_power_console/tgui_data(mob/user) + var/data = list() if(!recharge_port) reconnect() if(recharge_port && !QDELETED(recharge_port)) data["recharge_port"] = list("mech" = null) if(recharge_port.recharging_mecha && !QDELETED(recharge_port.recharging_mecha)) - data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mecha.obj_integrity, "maxhealth" = initial(recharge_port.recharging_mecha.max_integrity), "cell" = null) + data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mecha.obj_integrity, "maxhealth" = recharge_port.recharging_mecha.max_integrity, "cell" = null, "name" = recharge_port.recharging_mecha.name) if(recharge_port.recharging_mecha.cell && !QDELETED(recharge_port.recharging_mecha.cell)) - data["has_mech"] = 1 - data["mecha_name"] = recharge_port.recharging_mecha || "None" - data["mecha_charge"] = isnull(recharge_port.recharging_mecha) ? 0 : recharge_port.recharging_mecha.cell.charge - data["mecha_maxcharge"] = isnull(recharge_port.recharging_mecha) ? 0 : recharge_port.recharging_mecha.cell.maxcharge - data["mecha_charge_percentage"] = isnull(recharge_port.recharging_mecha) ? 0 : round(recharge_port.recharging_mecha.cell.percent()) - else - data["has_mech"] = 0 - + data["recharge_port"]["mech"]["cell"] = list( + "charge" = recharge_port.recharging_mecha.cell.charge, + "maxcharge" = recharge_port.recharging_mecha.cell.maxcharge + ) return data /obj/machinery/computer/mech_bay_power_console/Initialize() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 69f70207452..2cc543b9dc9 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -159,7 +159,6 @@ radio.name = "[src] radio" radio.icon = icon radio.icon_state = icon_state - radio.subspace_transmission = 1 /obj/mecha/examine(mob/user) . = ..() @@ -331,6 +330,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 +503,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 +541,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/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 9dd1c771b17..639687a45e9 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -58,7 +58,7 @@ icon_state = "eggs" var/amount_grown = 0 var/player_spiders = 0 - var/list/faction = list() + var/list/faction = list("spiders") /obj/structure/spider/eggcluster/New() ..() @@ -90,7 +90,7 @@ var/obj/machinery/atmospherics/unary/vent_pump/entry_vent var/travelling_in_vent = 0 var/player_spiders = 0 - var/list/faction = list() + var/list/faction = list("spiders") var/selecting_player = 0 /obj/structure/spider/spiderling/New() @@ -180,7 +180,7 @@ if(player_spiders && !selecting_player) selecting_player = 1 spawn() - var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S) if(candidates.len) var/mob/C = pick(candidates) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index baaf0cab16b..edcc6620531 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -2,6 +2,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect /obj/item name = "item" icon = 'icons/obj/items.dmi' + + move_resist = null // Set in the Initialise depending on the item size. Unless it's overriden by a specific item var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite var/blood_overlay_color = null @@ -113,6 +115,23 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect hitsound = 'sound/items/welder.ogg' if(damtype == "brute") hitsound = "swing_hit" + if(!move_resist) + determine_move_resist() + +/obj/item/proc/determine_move_resist() + switch(w_class) + if(WEIGHT_CLASS_TINY) + move_resist = MOVE_FORCE_EXTREMELY_WEAK + if(WEIGHT_CLASS_SMALL) + move_resist = MOVE_FORCE_VERY_WEAK + if(WEIGHT_CLASS_NORMAL) + move_resist = MOVE_FORCE_WEAK + if(WEIGHT_CLASS_BULKY) + move_resist = MOVE_FORCE_NORMAL + if(WEIGHT_CLASS_HUGE) + move_resist = MOVE_FORCE_NORMAL + if(WEIGHT_CLASS_GIGANTIC) + move_resist = MOVE_FORCE_NORMAL /obj/item/Destroy() flags &= ~DROPDEL //prevent reqdels 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/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 7a6b41966a8..e96fceb8b6c 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -191,7 +191,7 @@ if(CanUse(U)) if(!Use(U)) return - to_chat(U, "You replace [target.fitting] with [src].") + to_chat(U, "You replace the light [target.fitting] with [src].") if(target.status != LIGHT_EMPTY) AddShards(1, U) diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 284ce8d5598..c9d95dd8a99 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -10,8 +10,6 @@ materials = list(MAT_METAL=10000, MAT_GLASS=2500) var/code = 2 - is_special = 1 - /obj/item/radio/electropack/attack_hand(mob/user as mob) if(src == user.back) to_chat(user, "You need help taking this off!") @@ -54,23 +52,6 @@ if(src.flags & NODROP) A.flags |= NODROP -/obj/item/radio/electropack/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["freq"]) - var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) - set_frequency(new_frequency) - - else if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = clamp(code, 1, 100) - - else if(href_list["power"]) - on = !on - - add_fingerprint(usr) /obj/item/radio/electropack/receive_signal(datum/signal/signal) if(!signal || signal.encryption != code) @@ -96,18 +77,48 @@ return -/obj/item/radio/electropack/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/item/radio/electropack/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_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "radio_electro.tmpl", "[name]", 400, 500) + ui = new(user, src, ui_key, "Electropack", name, 360, 150, master_ui, state) ui.open() - ui.set_auto_update(1) - -/obj/item/radio/electropack/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/item/radio/electropack/tgui_data(mob/user) + var/list/data = list() data["power"] = on - data["freq"] = format_frequency(frequency) + data["frequency"] = frequency data["code"] = code - + data["minFrequency"] = PUBLIC_LOW_FREQ + data["maxFrequency"] = PUBLIC_HIGH_FREQ return data + +/obj/item/radio/electropack/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("power") + on = !on + if("freq") + var/value = params["freq"] + if(value) + frequency = sanitize_frequency(value) + set_frequency(frequency) + else + . = FALSE + if("code") + var/value = text2num(params["code"]) + if(value) + value = round(value) + code = clamp(value, 1, 100) + else + . = FALSE + if("reset") + if(params["reset"] == "freq") + frequency = initial(frequency) + else if(params["reset"] == "code") + code = initial(code) + else + . = FALSE + if(.) + add_fingerprint(usr) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 691c12e1347..d9af70df391 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -9,7 +9,6 @@ "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' ) //We read you loud and skree-er. materials = list(MAT_METAL=75) - subspace_transmission = TRUE canhear_range = 0 // can't hear headsets from very far away slot_flags = SLOT_EARS @@ -91,6 +90,7 @@ ks1type = /obj/item/encryptionkey/syndicate/nukeops requires_tcomms = FALSE instant = TRUE // Work instantly if there are no comms + freqlock = TRUE /obj/item/radio/headset/syndicate/alt //undisguised bowman with flash protection name = "syndicate headset" @@ -289,6 +289,7 @@ icon_state = "com_headset" item_state = "headset" ks2type = /obj/item/encryptionkey/ert + freqlock = TRUE /obj/item/radio/headset/ert/alt name = "\proper emergency response team's bowman headset" @@ -367,7 +368,7 @@ else to_chat(user, "This headset doesn't have any encryption keys! How useless...") -/obj/item/radio/headset/proc/recalculateChannels(var/setDescription = FALSE) +/obj/item/radio/headset/recalculateChannels(setDescription = FALSE) channels = list() translate_binary = FALSE translate_hive = FALSE diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 59d2c2a99bd..d9d3aa3c3c1 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -75,7 +75,6 @@ name = "illicit intercom" desc = "Talk through this. Evilly" frequency = SYND_FREQ - subspace_transmission = TRUE syndiekey = new /obj/item/encryptionkey/syndicate/nukeops /obj/item/radio/intercom/syndicate/New() @@ -85,7 +84,6 @@ /obj/item/radio/intercom/pirate name = "pirate radio intercom" desc = "You wouldn't steal a space shuttle. Piracy. It's a crime!" - subspace_transmission = 1 /obj/item/radio/intercom/pirate/New() ..() @@ -109,13 +107,13 @@ GLOB.global_intercoms.Remove(src) return ..() -/obj/item/radio/intercom/attack_ai(mob/user as mob) +/obj/item/radio/intercom/attack_ai(mob/user) add_hiddenprint(user) add_fingerprint(user) spawn(0) attack_self(user) -/obj/item/radio/intercom/attack_hand(mob/user as mob) +/obj/item/radio/intercom/attack_hand(mob/user) add_fingerprint(user) spawn(0) attack_self(user) @@ -186,10 +184,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 +268,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..cdcc2361937 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -26,21 +26,37 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( suffix = "\[3\]" icon_state = "walkietalkie" item_state = "walkietalkie" - var/on = 1 // 0 for off + /// boolean for radio enabled or not + var/on = TRUE var/last_transmission - var/frequency = PUB_FREQ //common chat - var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies - var/canhear_range = 3 // the range which mobs can hear this radio from + var/frequency = PUB_FREQ + /// tune to frequency to unlock traitor supplies + var/traitor_frequency = 0 + /// the range which mobs can hear this radio from + var/canhear_range = 3 var/datum/wires/radio/wires = null var/b_stat = 0 - var/broadcasting = 0 - var/listening = 1 - var/list/channels = list() //see communications.dm for full list. First channes is a "default" for :h - var/subspace_transmission = 0 - var/obj/item/encryptionkey/syndicate/syndiekey = null //Holder for the syndicate encryption key if present - var/disable_timer = 0 //How many times this is disabled by EMPs - var/is_special = 0 //For electropacks mostly, skips Topic() checks + /// Whether the radio will broadcast stuff it hears, out over the radio + var/broadcasting = FALSE + /// Whether the radio is currently receiving + var/listening = TRUE + /// Whether the radio can be re-tuned to restricted channels it has no key for + var/freerange = FALSE + /// Whether the radio is able to have its primary frequency changed. Used for radios with weird primary frequencies, like DS, syndi, etc + var/freqlock = FALSE + + /// Whether the radio broadcasts to everyone within a few tiles, or not + var/loudspeaker = FALSE + /// Whether loudspeaker can be toggled by the user + var/has_loudspeaker = FALSE + + /// see communications.dm for full list. First channes is a "default" for :h + var/list/channels = list() + /// Holder for the syndicate encryption key if present + var/obj/item/encryptionkey/syndicate/syndiekey = null + /// How many times this is disabled by EMPs + var/disable_timer = 0 flags = CONDUCT slot_flags = SLOT_BELT @@ -76,6 +92,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) @@ -99,47 +116,87 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( /obj/item/radio/attack_ghost(mob/user) return interact(user) -/obj/item/radio/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) +/obj/item/radio/attack_self(mob/user) + tgui_interact(user) /obj/item/radio/interact(mob/user) if(!user) return 0 - if(b_stat) wires.Interact(user) + tgui_interact(user) - return ui_interact(user) - -/obj/item/radio/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/item/radio/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, "radio_basic.tmpl", "[name]", 400, 550) + ui = new(user, src, ui_key, "Radio", name, 360, 150 + (length(channels) * 20), master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/item/radio/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/item/radio/tgui_data(mob/user) + var/list/data = list() - data["mic_status"] = broadcasting - data["speaker"] = listening - 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)) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - data["chan_list_len"] = chanlist.len - - if(syndiekey) - data["useSyndMode"] = 1 + data["broadcasting"] = broadcasting + data["listening"] = listening + data["frequency"] = frequency + data["minFrequency"] = freerange ? RADIO_LOW_FREQ : PUBLIC_LOW_FREQ + data["maxFrequency"] = freerange ? RADIO_HIGH_FREQ : PUBLIC_HIGH_FREQ + data["canReset"] = frequency == initial(frequency) ? FALSE : TRUE + data["freqlock"] = freqlock + data["channels"] = list() + for(var/channel in channels) + data["channels"][channel] = channels[channel] & FREQ_LISTENING + data["has_loudspeaker"] = has_loudspeaker + data["loudspeaker"] = loudspeaker return data +/obj/item/radio/tgui_act(action, params, datum/tgui/ui) + if(..()) + return + . = TRUE + switch(action) + if("frequency") + if(freqlock) + return + var/tune = params["tune"] + var/adjust = text2num(params["adjust"]) + if(tune == "reset") + tune = initial(frequency) + else if(adjust) + tune = frequency + adjust * 10 + else if(text2num(tune) != null) + tune = tune * 10 + else + . = FALSE + if(hidden_uplink) + if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) + usr << browse(null, "window=radio") + if(.) + set_frequency(sanitize_frequency(tune, freerange)) + if("listen") + listening = !listening + if("broadcast") + broadcasting = !broadcasting + if("channel") + var/channel = params["channel"] + if(!(channel in channels)) + return + if(channels[channel] & FREQ_LISTENING) + channels[channel] &= ~FREQ_LISTENING + else + channels[channel] |= FREQ_LISTENING + if("loudspeaker") + // Toggle loudspeaker mode, AKA everyone around you hearing your radio. + if(has_loudspeaker) + loudspeaker = !loudspeaker + if(loudspeaker) + canhear_range = 3 + else + canhear_range = 0 + else + . = FALSE + if(.) + add_fingerprint(usr) /obj/item/radio/proc/list_channels(var/mob/user) return list_internal_channels(user) @@ -184,57 +241,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)) - -/obj/item/radio/Topic(href, href_list) - if(..()) - return 1 - - if(is_special) - return 0 - - if(href_list["track"]) - var/mob/target = locate(href_list["track"]) - var/mob/living/silicon/ai/A = locate(href_list["track2"]) - if(A && target) - A.ai_actual_track(target) - . = 1 - - else if(href_list["freq"]) - var/new_frequency = (frequency + text2num(href_list["freq"])) - if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - set_frequency(new_frequency) - if(hidden_uplink) - if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) - usr << browse(null, "window=radio") - . = 1 - else if(href_list["talk"]) - ToggleBroadcast() - . = 1 - else if(href_list["listen"]) - var/chan_name = href_list["ch_name"] - if(!chan_name) - ToggleReception() - else - if(channels[chan_name] & FREQ_LISTENING) - channels[chan_name] &= ~FREQ_LISTENING - else - channels[chan_name] |= FREQ_LISTENING - . = 1 - else if(href_list["spec_freq"]) - var/freq = href_list["spec_freq"] - if(has_channel_access(usr, freq)) - set_frequency(text2num(freq)) - . = 1 - - if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk - return 1 - - add_fingerprint(usr) + listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) /obj/item/radio/proc/autosay(message, from, channel, role = "Unknown") //BS12 EDIT var/datum/radio_frequency/connection = null @@ -325,7 +335,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 +344,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 +438,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 +525,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 @@ -580,19 +591,24 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( if(!disable_timer) on = 1 +/obj/item/radio/proc/recalculateChannels() + /// Exists so that borg radios and headsets can override it. + stack_trace("recalculateChannels() called on a radio which does not implement the proc.") + /////////////////////////////// //////////Borg Radios////////// /////////////////////////////// //Giving borgs their own radio to have some more room to work with -Sieve /obj/item/radio/borg + name = "Cyborg Radio" var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks var/obj/item/encryptionkey/keyslot = null//Borg radios can handle a single encryption key - var/shut_up = 1 icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. icon_state = "radio" + has_loudspeaker = TRUE + loudspeaker = FALSE canhear_range = 0 - subspace_transmission = 1 dog_fashion = null /obj/item/radio/borg/syndicate @@ -614,12 +630,14 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( ..() syndiekey = keyslot set_frequency(SYND_FREQ) + freqlock = TRUE /obj/item/radio/borg/deathsquad /obj/item/radio/borg/deathsquad/New() ..() set_frequency(DTH_FREQ) + freqlock = TRUE /obj/item/radio/borg/ert keyslot = new /obj/item/encryptionkey/ert @@ -627,6 +645,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( /obj/item/radio/borg/ert/New() ..() set_frequency(ERT_FREQ) + freqlock = TRUE /obj/item/radio/borg/ert/specops keyslot = new /obj/item/encryptionkey/centcom @@ -671,7 +690,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( else to_chat(user, "This radio doesn't have any encryption keys!") -/obj/item/radio/borg/proc/recalculateChannels() +/obj/item/radio/borg/recalculateChannels() channels = list() syndiekey = null @@ -704,72 +723,12 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( return -/obj/item/radio/borg/Topic(href, href_list) - if(..()) - return 1 - if(href_list["mode"]) - var/enable_subspace_transmission = text2num(href_list["mode"]) - if(enable_subspace_transmission != subspace_transmission) - subspace_transmission = !subspace_transmission - if(subspace_transmission) - to_chat(usr, "Subspace Transmission is enabled.") - else - to_chat(usr, "Subspace Transmission is disabled.") - if(subspace_transmission == 0)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled - channels = list() - else - recalculateChannels() - . = 1 - if(href_list["shutup"]) // Toggle loudspeaker mode, AKA everyone around you hearing your radio. - var/do_shut_up = text2num(href_list["shutup"]) - if(do_shut_up != shut_up) - shut_up = !shut_up - if(shut_up) - canhear_range = 0 - to_chat(usr, "Loudspeaker disabled.") - else - canhear_range = 3 - to_chat(usr, "Loudspeaker enabled.") - . = 1 - - -/obj/item/radio/borg/interact(mob/user as mob) +/obj/item/radio/borg/interact(mob/user) if(!on) return - . = ..() -/obj/item/radio/borg/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, "radio_basic.tmpl", "[name]", 430, 500) - ui.open() - ui.set_auto_update(1) - -/obj/item/radio/borg/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["mic_status"] = broadcasting - data["speaker"] = listening - data["freq"] = format_frequency(frequency) - data["rawfreq"] = num2text(frequency) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - data["chan_list_len"] = chanlist.len - - if(syndiekey) - data["useSyndMode"] = 1 - - data["has_loudspeaker"] = 1 - data["loudspeaker"] = !shut_up - data["has_subspace"] = 1 - data["subspace"] = subspace_transmission - - return data - /obj/item/radio/proc/config(op) if(SSradio) for(var/ch_name in channels) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index f20a2cc5875..43697ac4776 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -72,7 +72,7 @@ REAGENT SCANNER var/turf/U = O.loc if(U && U.intact) O.invisibility = 101 - 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/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index acba0087caf..fdef93dc7a3 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -86,64 +86,64 @@ O.hear_message(M, msg) /obj/item/transfer_valve/attack_self(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - // 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/item/transfer_valve/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_inventory_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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "transfer_valve.tmpl", "Tank Transfer Valve", 460, 280) - // open the new ui window + ui = new(user, src, ui_key, "TransferValve", name, 460, 320, master_ui, state) ui.open() - // auto update every Master Controller tick - //ui.set_auto_update(1) - -/obj/item/transfer_valve/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["attachmentOne"] = tank_one ? tank_one.name : null - data["attachmentTwo"] = tank_two ? tank_two.name : null - data["valveAttachment"] = attached_device ? attached_device.name : null - data["valveOpen"] = valve_open ? 1 : 0 +/obj/item/transfer_valve/tgui_data(mob/user) + var/list/data = list() + data["tank_one"] = tank_one ? tank_one.name : null + data["tank_two"] = tank_two ? tank_two.name : null + data["attached_device"] = attached_device ? attached_device.name : null + data["valve"] = valve_open return data -/obj/item/transfer_valve/Topic(href, href_list) - ..() - if(usr.incapacitated()) - return 0 - if(loc != usr) - return 0 - if(tank_one && href_list["tankone"]) - split_gases() - valve_open = 0 - tank_one.forceMove(get_turf(src)) - tank_one = null + + +/obj/item/transfer_valve/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("tankone") + if(tank_one) + split_gases() + valve_open = FALSE + tank_one.forceMove(get_turf(src)) + tank_one = null + update_icon() + if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) + w_class = WEIGHT_CLASS_NORMAL + if("tanktwo") + if(tank_two) + split_gases() + valve_open = FALSE + tank_two.forceMove(get_turf(src)) + tank_two = null + update_icon() + if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) + w_class = WEIGHT_CLASS_NORMAL + if("toggle") + toggle_valve() + if("device") + if(attached_device) + attached_device.attack_self(usr) + if("remove_device") + if(attached_device) + attached_device.forceMove(get_turf(src)) + attached_device.holder = null + attached_device = null + update_icon() + else + . = FALSE + if(.) update_icon() - if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) - w_class = WEIGHT_CLASS_NORMAL - else if(tank_two && href_list["tanktwo"]) - split_gases() - valve_open = 0 - tank_two.forceMove(get_turf(src)) - tank_two = null - update_icon() - if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) - w_class = WEIGHT_CLASS_NORMAL - else if(href_list["open"]) - toggle_valve() - else if(attached_device) - if(href_list["rem_device"]) - attached_device.forceMove(get_turf(src)) - attached_device.holder = null - attached_device = null - update_icon() - if(href_list["device"]) - attached_device.attack_self(usr) - add_fingerprint(usr) - return 1 // Returning 1 sends an update to attached UIs + add_fingerprint(usr) + /obj/item/transfer_valve/proc/process_activation(obj/item/D) if(toggle) 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 66f35a169f9..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" @@ -344,56 +344,56 @@ /obj/item/toy/prize/ripley name = "toy ripley" - desc = "Mini-Mecha action figure! Collect them all! 1/11." + desc = "Mini-Mecha action figure! Collect them all! 1/11. This one is a ripley, a mining and engineering mecha." /obj/item/toy/prize/fireripley name = "toy firefighting ripley" - desc = "Mini-Mecha action figure! Collect them all! 2/11." + desc = "Mini-Mecha action figure! Collect them all! 2/11. This one is a firefighter ripley, a fireproof mining and engineering mecha." icon_state = "fireripleytoy" /obj/item/toy/prize/deathripley name = "toy deathsquad ripley" - desc = "Mini-Mecha action figure! Collect them all! 3/11." + desc = "Mini-Mecha action figure! Collect them all! 3/11. This one is the black ripley used by the hero of DeathSquad, that TV drama about loose-cannon ERT officers!" icon_state = "deathripleytoy" /obj/item/toy/prize/gygax name = "toy gygax" - desc = "Mini-Mecha action figure! Collect them all! 4/11." + desc = "Mini-Mecha action figure! Collect them all! 4/11. This one is the speedy gygax combat mecha. Zoom zoom, pew pew!" icon_state = "gygaxtoy" /obj/item/toy/prize/durand name = "toy durand" - desc = "Mini-Mecha action figure! Collect them all! 5/11." + desc = "Mini-Mecha action figure! Collect them all! 5/11. This one is the heavy durand combat mecha. Stomp stomp!" icon_state = "durandprize" /obj/item/toy/prize/honk name = "toy H.O.N.K." - desc = "Mini-Mecha action figure! Collect them all! 6/11." + desc = "Mini-Mecha action figure! Collect them all! 6/11. This one is the infamous H.O.N.K mech!" icon_state = "honkprize" /obj/item/toy/prize/marauder name = "toy marauder" - desc = "Mini-Mecha action figure! Collect them all! 7/11." + desc = "Mini-Mecha action figure! Collect them all! 7/11. This one is the powerful marauder combat mecha! Run for cover!" icon_state = "marauderprize" /obj/item/toy/prize/seraph name = "toy seraph" - desc = "Mini-Mecha action figure! Collect them all! 8/11." + desc = "Mini-Mecha action figure! Collect them all! 8/11. This one is the powerful seraph combat mecha! Someone's in trouble!" icon_state = "seraphprize" /obj/item/toy/prize/mauler name = "toy mauler" - desc = "Mini-Mecha action figure! Collect them all! 9/11." + desc = "Mini-Mecha action figure! Collect them all! 9/11. This one is the deadly mauler combat mecha! Look out!" icon_state = "maulerprize" /obj/item/toy/prize/odysseus name = "toy odysseus" - desc = "Mini-Mecha action figure! Collect them all! 10/11." + desc = "Mini-Mecha action figure! Collect them all! 10/11. This one is the spindly, syringe-firing odysseus medical mecha." icon_state = "odysseusprize" /obj/item/toy/prize/phazon name = "toy phazon" - desc = "Mini-Mecha action figure! Collect them all! 11/11." + desc = "Mini-Mecha action figure! Collect them all! 11/11. This one is the mysterious Phazon combat mecha! Nobody's safe!" icon_state = "phazonprize" @@ -1591,182 +1591,217 @@ obj/item/toy/cards/deck/syndicate/black /obj/item/toy/figure/cmo name = "Chief Medical Officer action figure" + desc = "The ever-suffering CMO, from Space Life's SS12 figurine collection." icon_state = "cmo" toysay = "Suit sensors!" /obj/item/toy/figure/assistant name = "Assistant action figure" + desc = "The faceless, hairless scourge of the station, from Space Life's SS12 figurine collection." icon_state = "assistant" toysay = "Grey tide station wide!" /obj/item/toy/figure/atmos name = "Atmospheric Technician action figure" + desc = "The faithful atmospheric technician, from Space Life's SS12 figurine collection." icon_state = "atmos" toysay = "Glory to Atmosia!" /obj/item/toy/figure/bartender name = "Bartender action figure" + desc = "The suave bartender, from Space Life's SS12 figurine collection." icon_state = "bartender" toysay = "Wheres my monkey?" /obj/item/toy/figure/borg name = "Cyborg action figure" + desc = "The iron-willed cyborg, from Space Life's SS12 figurine collection." icon_state = "borg" toysay = "I. LIVE. AGAIN." /obj/item/toy/figure/botanist name = "Botanist action figure" + desc = "The drug-addicted botanist, from Space Life's SS12 figurine collection." icon_state = "botanist" toysay = "Dude, I see colors..." /obj/item/toy/figure/captain name = "Captain action figure" + desc = "The inept captain, from Space Life's SS12 figurine collection." icon_state = "captain" toysay = "Crew, the Nuke Disk is safely up my ass." /obj/item/toy/figure/cargotech name = "Cargo Technician action figure" + desc = "The hard-working cargo tech, from Space Life's SS12 figurine collection." icon_state = "cargotech" toysay = "For Cargonia!" /obj/item/toy/figure/ce name = "Chief Engineer action figure" + desc = "The expert Chief Engineer, from Space Life's SS12 figurine collection." icon_state = "ce" toysay = "Wire the solars!" /obj/item/toy/figure/chaplain name = "Chaplain action figure" + desc = "The obsessed Chaplain, from Space Life's SS12 figurine collection." icon_state = "chaplain" toysay = "Gods make me a killing machine please!" /obj/item/toy/figure/chef name = "Chef action figure" + desc = "The cannibalistic chef, from Space Life's SS12 figurine collection." icon_state = "chef" toysay = "I swear it's not human meat." /obj/item/toy/figure/chemist name = "Chemist action figure" + desc = "The legally dubious Chemist, from Space Life's SS12 figurine collection." icon_state = "chemist" toysay = "Get your pills!" /obj/item/toy/figure/clown name = "Clown action figure" + desc = "The mischevious Clown, from Space Life's SS12 figurine collection." icon_state = "clown" toysay = "Honk!" /obj/item/toy/figure/ian name = "Ian action figure" + desc = "The adorable corgi, from Space Life's SS12 figurine collection." icon_state = "ian" toysay = "Arf!" /obj/item/toy/figure/detective name = "Detective action figure" + desc = "The clever detective, from Space Life's SS12 figurine collection." icon_state = "detective" toysay = "This airlock has grey jumpsuit and insulated glove fibers on it." /obj/item/toy/figure/dsquad name = "Death Squad Officer action figure" + desc = "It's a member of the DeathSquad, a TV drama where loose-cannon ERT officers face up against the threats of the galaxy! It's from Space Life's special edition SS12 figurine collection." icon_state = "dsquad" toysay = "Eliminate all threats!" /obj/item/toy/figure/engineer name = "Engineer action figure" + desc = "The frantic engineer, from Space Life's SS12 figurine collection." icon_state = "engineer" toysay = "Oh god, the singularity is loose!" /obj/item/toy/figure/geneticist name = "Geneticist action figure" + desc = "The balding geneticist, from Space Life's SS12 figurine collection." icon_state = "geneticist" toysay = "I'm not qualified for this job." /obj/item/toy/figure/hop name = "Head of Personnel action figure" + desc = "The officious Head of Personnel, from Space Life's SS12 figurine collection." icon_state = "hop" - toysay = "Giving out all access!" + toysay = "Papers, please!" /obj/item/toy/figure/hos name = "Head of Security action figure" + desc = "The bloodlust-filled Head of Security, from Space Life's SS12 figurine collection." icon_state = "hos" - toysay = "I'm here to win, anything else is secondary." + toysay = "Space law? What?" /obj/item/toy/figure/qm name = "Quartermaster action figure" + desc = "The nationalistic Quartermaster, from Space Life's SS12 figurine collection." icon_state = "qm" toysay = "Hail Cargonia!" /obj/item/toy/figure/janitor name = "Janitor action figure" + desc = "The water-using Janitor, from Space Life's SS12 figurine collection." icon_state = "janitor" toysay = "Look at the signs, you idiot." /obj/item/toy/figure/lawyer name = "Internal Affairs Agent action figure" + desc = "The unappreciated Internal Affairs Agent, from Space Life's SS12 figurine collection." icon_state = "lawyer" toysay = "Standard Operating Procedure says they're guilty! Hacking is proof they're an Enemy of the Corporation!" /obj/item/toy/figure/librarian name = "Librarian action figure" + desc = "The quiet Librarian, from Space Life's SS12 figurine collection." icon_state = "librarian" toysay = "One day while..." /obj/item/toy/figure/md name = "Medical Doctor action figure" + desc = "The stressed-out doctor, from Space Life's SS12 figurine collection." icon_state = "md" toysay = "The patient is already dead!" /obj/item/toy/figure/mime name = "Mime action figure" - desc = "A \"Space Life\" brand Mime action figure." + desc = "... from Space Life's SS12 figurine collection." icon_state = "mime" toysay = "..." /obj/item/toy/figure/miner name = "Shaft Miner action figure" + desc = "The gun-toting Shaft Miner, from Space Life's SS12 figurine collection." icon_state = "miner" toysay = "Oh god it's eating my intestines!" /obj/item/toy/figure/ninja name = "Ninja action figure" + desc = "It's the mysterious ninja! It's from Space Life's special edition SS12 figurine collection." icon_state = "ninja" toysay = "Oh god! Stop shooting, I'm friendly!" /obj/item/toy/figure/wizard name = "Wizard action figure" + desc = "It's the deadly, spell-slinging wizard! It's from Space Life's special edition SS12 figurine collection." icon_state = "wizard" toysay = "Ei Nath!" /obj/item/toy/figure/rd name = "Research Director action figure" + desc = "The ambitious RD, from Space Life's SS12 figurine collection." icon_state = "rd" toysay = "Blowing all of the borgs!" /obj/item/toy/figure/roboticist name = "Roboticist action figure" + desc = "The skillful Roboticist, from Space Life's SS12 figurine collection." icon_state = "roboticist" toysay = "He asked to be borged!" /obj/item/toy/figure/scientist name = "Scientist action figure" + desc = "The mad Scientist, from Space Life's SS12 figurine collection." icon_state = "scientist" toysay = "Someone else must have made those bombs!" /obj/item/toy/figure/syndie name = "Nuclear Operative action figure" + desc = "It's the red-suited Nuclear Operative! It's from Space Life's special edition SS12 figurine collection." icon_state = "syndie" toysay = "Get that fucking disk!" /obj/item/toy/figure/secofficer name = "Security Officer action figure" + desc = "The power-tripping Security Officer, from Space Life's SS12 figurine collection." icon_state = "secofficer" toysay = "I am the law!" /obj/item/toy/figure/virologist name = "Virologist action figure" + desc = "The pandemic-starting Virologist, from Space Life's SS12 figurine collection." icon_state = "virologist" - toysay = "The cure is potassium!" + toysay = "It's not my virus!" /obj/item/toy/figure/warden name = "Warden action figure" + desc = "The amnesiac Warden, from Space Life's SS12 figurine collection." icon_state = "warden" toysay = "Execute him for breaking in!" diff --git a/code/game/objects/items/weapons/batons.dm b/code/game/objects/items/weapons/batons.dm new file mode 100644 index 00000000000..34d2bdd7ac8 --- /dev/null +++ b/code/game/objects/items/weapons/batons.dm @@ -0,0 +1,133 @@ +/// Delay in deci-seconds between two non-lethal attacks +#define BATON_STUN_COOLDOWN 4 SECONDS +/// Force of the telescopic baton when deployed +#define BATON_TELESCOPIC_FORCE_DEPLOYED 10 + +/** + * # Police Baton + * + * Knocks down the hit mob when not on harm intent and when [/obj/item/melee/classic_baton/on] is TRUE + * + * A non-lethal attack has a cooldown to avoid spamming + */ +/obj/item/melee/classic_baton + name = "police baton" + desc = "A wooden truncheon for beating criminal scum." + icon_state = "baton" + item_state = "classic_baton" + slot_flags = SLOT_BELT + force = 12 //9 hit crit + w_class = WEIGHT_CLASS_NORMAL + /// Whether the baton is on cooldown + var/on_cooldown = FALSE + /// Whether the baton is toggled on (to allow attacking) + var/on = TRUE + +/obj/item/melee/classic_baton/attack(mob/living/target, mob/living/user) + if(!on) + return ..() + + add_fingerprint(user) + if((CLUMSY in user.mutations) && prob(50)) + user.visible_message("[user] accidentally clubs [user.p_them()]self with [src]!", \ + "You accidentally club yourself with [src]!") + user.Weaken(force * 3) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(force * 2, BRUTE, "head") + else + user.take_organ_damage(force * 2) + return + + if(user.a_intent == INTENT_HARM || isrobot(target)) // Lethal attack or it's a borg (can't knock them down!) + return ..() + else if(!on_cooldown) // Non-lethal attack - knock them down + // Check for shield/countering + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + return + if(check_martial_counter(H, user)) + return + // Visuals and sound + user.do_attack_animation(target) + playsound(target, 'sound/effects/woodhit.ogg', 75, TRUE, -1) + add_attack_logs(user, target, "Stunned with [src]") + target.visible_message("[user] has knocked down [target] with \the [src]!", \ + "[user] has knocked down [target] with \the [src]!") + // Hit 'em + target.LAssailant = iscarbon(user) ? user : null + target.Weaken(3) + on_cooldown = TRUE + addtimer(CALLBACK(src, .proc/cooldown_finished), BATON_STUN_COOLDOWN) + +/** + * Called some time after a non-lethal attack + */ +/obj/item/melee/classic_baton/proc/cooldown_finished() + on_cooldown = FALSE + +/** + * # Fancy Cane + */ +/obj/item/melee/classic_baton/ntcane + name = "fancy cane" + desc = "A cane with special engraving on it. It seems well suited for fending off assailants..." + icon_state = "cane_nt" + item_state = "cane_nt" + needs_permit = FALSE + +/obj/item/melee/classic_baton/ntcane/is_crutch() + return TRUE + +/** + * # Telescopic Baton + */ +/obj/item/melee/classic_baton/telescopic + name = "telescopic baton" + desc = "A compact yet robust personal defense weapon. Can be concealed when folded." + icon_state = "telebaton_0" + item_state = null + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + needs_permit = FALSE + force = 0 + on = FALSE + /// Attack verbs when concealed (created on Initialize) + var/static/list/attack_verb_off + /// Attack verbs when extended (created on Initialize) + var/static/list/attack_verb_on + +/obj/item/melee/classic_baton/telescopic/Initialize(mapload) + . = ..() + if(!attack_verb_off) + attack_verb_off = list("hit", "poked") + attack_verb_on = list("smacked", "struck", "cracked", "beaten") + attack_verb = on ? attack_verb_on : attack_verb_off + +/obj/item/melee/classic_baton/telescopic/attack_self(mob/user) + on = !on + icon_state = "telebaton_[on]" + if(on) + to_chat(user, "You extend the baton.") + item_state = "nullrod" + w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance + force = BATON_TELESCOPIC_FORCE_DEPLOYED //stunbaton damage + attack_verb = attack_verb_on + else + to_chat(user, "You collapse the baton.") + item_state = null //no sprite for concealment even when in hand + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + force = 0 //not so robust now + attack_verb = attack_verb_off + // Update mob hand visuals + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + playsound(loc, 'sound/weapons/batonextend.ogg', 50, TRUE) + add_fingerprint(user) + +#undef BATON_STUN_COOLDOWN +#undef BATON_TELESCOPIC_FORCE_DEPLOYED diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index dc26b14e24b..b780a837559 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -212,6 +212,11 @@ return M owner_ckey = null +/obj/item/card/id/proc/getPlayerCkey() + var/mob/living/carbon/human/H = getPlayer() + if(istype(H)) + return H.ckey + /obj/item/card/id/proc/is_untrackable() return untrackable diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index 42f65141094..fcfe183dddf 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -55,16 +55,15 @@ /obj/item/lipstick/random/Initialize(mapload) . = ..() - var/lscolor = pick(lipstick_colors) // A random color is picked from the var defined initially in a new var. - colour = lipstick_colors[lscolor] // The color of the lipstick is pulled from the new variable (right hand side, HTML & Hex RGB) - name = "[lscolor] lipstick" // The new variable is also used to match the name to the color of the lipstick. Kudos to Desolate & Lemon + colour = pick(lipstick_colors) + name = "[colour] lipstick" /obj/item/lipstick/attack_self(mob/user) cut_overlays() to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") open = !open if(open) - var/image/colored = mutable_appearance('icons/obj/items.dmi', "lipstick_uncap_color") + var/mutable_appearance/colored = mutable_appearance('icons/obj/items.dmi', "lipstick_uncap_color") colored.color = lipstick_colors[colour] icon_state = "lipstick_uncap" add_overlay(colored) 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/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index b3028f13c90..13f9dd8f49d 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -5,39 +5,51 @@ origin_tech = "materials=2;combat=3" light_power = 10 light_color = LIGHT_COLOR_WHITE - var/light_time = 2 - var/range = 7 + + var/light_time = 0.2 SECONDS // The duration the area is illuminated + var/range = 7 // The range in tiles of the flashbang /obj/item/grenade/flashbang/prime() update_mob() - var/flashbang_turf = get_turf(src) - if(!flashbang_turf) - return + var/turf/T = get_turf(src) + if(T) + // VFX and SFX + do_sparks(rand(5, 9), FALSE, src) + playsound(T, 'sound/effects/bang.ogg', 100, TRUE) + new /obj/effect/dummy/lighting_obj(T, light_color, range + 2, light_power, light_time) - set_light(7) - - do_sparks(rand(5, 9), FALSE, src) - playsound(flashbang_turf, 'sound/effects/bang.ogg', 25, 1) - bang(flashbang_turf, src, range) - - for(var/obj/structure/blob/B in hear(8, flashbang_turf)) //Blob damage here - var/damage = round(30 / (get_dist(B, get_turf(src)) + 1)) - B.take_damage(damage, BURN, "melee", 0) - - spawn(light_time) - qdel(src) + // Stunning & damaging mechanic + bang(T, src, range) + qdel(src) +/** + * Creates a flashing effect that blinds and deafens mobs within range + * + * Also damages blobs + * Arguments: + * * T - The turf to flash + * * A - The flashing atom + * * range - The range in tiles of the flash + * * flash - Whether to flash (blind) + * * bang - Whether to bang (deafen) + */ /proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE) + // Blob damage + for(var/obj/structure/blob/B in hear(range + 1, T)) + var/damage = round(30 / (get_dist(B, T) + 1)) + B.take_damage(damage, BURN, "melee", FALSE) + + // Flashing mechanic + var/source_turf = get_turf(A) for(var/mob/living/M in hearers(range, T)) if(M.stat == DEAD) continue M.show_message("BANG", 2) - //Checking for protections - var/ear_safety = M.check_ear_prot() - var/distance = max(1, get_dist(get_turf(A), get_turf(M))) + var/distance = max(1, get_dist(source_turf, get_turf(M))) + var/stun_amount = max(10 / distance, 3) - //Flash + // Flash if(flash) if(M.weakeyes) M.visible_message("[M] screams and collapses!") @@ -49,21 +61,20 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) if(E) - E.receive_damage(8, 1) - + E.receive_damage(8, TRUE) if(M.flash_eyes(affect_silicon = TRUE)) - M.Stun(max(10 / distance, 3)) - M.Weaken(max(10 / distance, 3)) + M.Stun(stun_amount) + M.Weaken(stun_amount) - - //Bang + // Bang + var/ear_safety = M.check_ear_prot() if(bang) - if(!distance || A.loc == M || A.loc == M.loc) //Holding on person or being exactly where lies is significantly more dangerous and voids protection + if(!distance || A.loc == M || A.loc == M.loc) // Holding on person or being exactly where lies is significantly more dangerous and voids protection M.Stun(10) M.Weaken(10) if(!ear_safety) - M.Stun(max(10 / distance, 3)) - M.Weaken(max(10 / distance, 3)) + M.Stun(stun_amount) + M.Weaken(stun_amount) M.AdjustEarDamage(rand(0, 5), 15) if(iscarbon(M)) var/mob/living/carbon/C = M @@ -74,6 +85,5 @@ if(prob(ears.ear_damage - 5)) to_chat(M, "You can't hear anything!") M.BecomeDeaf() - else - if(ears.ear_damage >= 5) - to_chat(M, "Your ears start to ring!") + else if(ears.ear_damage >= 5) + to_chat(M, "Your ears start to ring!") 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/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index 6423bbeb6ff..974bd3b677f 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -255,7 +255,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, FALSE, 10 SECONDS, source = src) var/mob/dead/observer/theghost = null if(candidates.len) diff --git a/code/game/objects/items/weapons/shards.dm b/code/game/objects/items/weapons/shards.dm index 6daabffe000..f987f047f11 100644 --- a/code/game/objects/items/weapons/shards.dm +++ b/code/game/objects/items/weapons/shards.dm @@ -83,7 +83,7 @@ /obj/item/shard/Crossed(mob/living/L, oldloc) if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || L.flying) + if(L.incorporeal_move || L.flying || L.floating) return playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) return ..() 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/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 403088e2b71..e31e98789a3 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -204,8 +204,10 @@ /obj/item/flashlight/pen, /obj/item/seeds, /obj/item/wirecutters, - /obj/item/wrench, - ) + /obj/item/wrench, + /obj/item/reagent_containers/spray/weedspray, + /obj/item/reagent_containers/spray/pestspray + ) /obj/item/storage/belt/security name = "security belt" 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/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm deleted file mode 100644 index e75935a3cc2..00000000000 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ /dev/null @@ -1,119 +0,0 @@ -/* Weapons - * Contains: - * Banhammer - * Classic Baton - */ - -/* - * Banhammer - */ -/obj/item/banhammer/attack(mob/M, mob/user) - to_chat(M, " You have been banned FOR NO REISIN by [user]") - to_chat(user, " You have BANNED [M]") - playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much - -/* - * Classic Baton - */ - -/obj/item/melee/classic_baton - name = "police baton" - desc = "A wooden truncheon for beating criminal scum." - icon_state = "baton" - item_state = "classic_baton" - slot_flags = SLOT_BELT - force = 12 //9 hit crit - w_class = WEIGHT_CLASS_NORMAL - var/cooldown = 0 - var/on = 1 - -/obj/item/melee/classic_baton/attack(mob/target as mob, mob/living/user as mob) - if(on) - add_fingerprint(user) - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, "head") - else - user.take_organ_damage(2*force) - return - if(isrobot(target)) - ..() - return - if(!isliving(target)) - return - if(user.a_intent == INTENT_HARM) - if(!..()) return - if(!isrobot(target)) return - else - if(cooldown <= 0) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) - return - if(check_martial_counter(H, user)) - return - playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1) - target.Weaken(3) - add_attack_logs(user, target, "Stunned with [src]") - add_fingerprint(user) - target.visible_message("[user] has knocked down [target] with \the [src]!", \ - "[user] has knocked down [target] with \the [src]!") - if(!iscarbon(user)) - target.LAssailant = null - else - target.LAssailant = user - cooldown = 1 - spawn(40) - cooldown = 0 - return - else - return ..() - -/obj/item/melee/classic_baton/ntcane - name = "fancy cane" - desc = "A cane with special engraving on it. It seems well suited for fending off assailants..." - icon_state = "cane_nt" - item_state = "cane_nt" - needs_permit = 0 - -/obj/item/melee/classic_baton/ntcane/is_crutch() - return 1 - -//Telescopic baton -/obj/item/melee/classic_baton/telescopic - name = "telescopic baton" - desc = "A compact yet robust personal defense weapon. Can be concealed when folded." - icon_state = "telebaton_0" - item_state = null - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - needs_permit = 0 - force = 0 - on = 0 - -/obj/item/melee/classic_baton/telescopic/attack_self(mob/user as mob) - on = !on - if(on) - to_chat(user, "You extend the baton.") - icon_state = "telebaton_1" - item_state = "nullrod" - w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance - force = 10 //stunbaton damage - attack_verb = list("smacked", "struck", "cracked", "beaten") - else - to_chat(user, "You collapse the baton.") - icon_state = "telebaton_0" - item_state = null //no sprite for concealment even when in hand - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - force = 0 //not so robust now - attack_verb = list("hit", "poked") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) - add_fingerprint(user) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 9ded7947202..8e05185baae 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() @@ -142,70 +138,55 @@ if(!(air_contents)) return - ui_interact(user) + tgui_interact(user) -/obj/item/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // 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/item/tank/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_inventory_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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "tanks.tmpl", "Tank", 500, 300) - // open the new ui window + ui = new(user, src, ui_key, "Tank", name, 300, 150, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/item/tank/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/using_internal - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.internal == src) - using_internal = 1 - - var/data[0] +/obj/item/tank/tgui_data(mob/user) + var/list/data = list() data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["valveOpen"] = using_internal ? 1 : 0 - - data["maskConnected"] = 0 - - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.internal == src) - data["maskConnected"] = 1 - else - if(C.wear_mask && (C.wear_mask.flags & AIRTIGHT)) - data["maskConnected"] = 1 - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && (H.head.flags & AIRTIGHT)) - data["maskConnected"] = 1 - + var/mob/living/carbon/C = user + if(!istype(C)) + C = loc.loc + if(!istype(C)) + return data + data["has_mask"] = C.wear_mask ? TRUE : FALSE + data["connected"] = (C.internal && C.internal == src) ? TRUE : FALSE return data -/obj/item/tank/Topic(href, href_list) +/obj/item/tank/tgui_act(action, params) if(..()) - return 1 - - if(href_list["dist_p"]) - if(href_list["dist_p"] == "reset") - distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - else if(href_list["dist_p"] == "max") - distribute_pressure = TANK_MAX_RELEASE_PRESSURE + return + . = TRUE + switch(action) + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = initial(distribute_pressure) + else if(pressure == "min") + pressure = TANK_DEFAULT_RELEASE_PRESSURE + else if(pressure == "max") + pressure = TANK_MAX_RELEASE_PRESSURE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + else + . = FALSE + if(.) + distribute_pressure = clamp(round(pressure), TANK_DEFAULT_RELEASE_PRESSURE, TANK_MAX_RELEASE_PRESSURE) + if("internals") + toggle_internals(usr) else - var/cp = text2num(href_list["dist_p"]) - distribute_pressure += cp - distribute_pressure = min(max(round(distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE) - - if(href_list["stat"]) - toggle_internals(usr) - - add_fingerprint(usr) - return 1 - + . = FALSE + if(.) + add_fingerprint(usr) /obj/item/tank/remove_air(amount) return air_contents.remove(amount) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 32158a9a3ab..e5f903a1853 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -1,3 +1,6 @@ +/** + * # Banhammer + */ /obj/item/banhammer desc = "A banhammer" name = "banhammer" @@ -13,11 +16,15 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) resistance_flags = FIRE_PROOF - /obj/item/banhammer/suicide_act(mob/user) to_chat(viewers(user), "[user] is hitting [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.") return BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS +/obj/item/banhammer/attack(mob/M, mob/user) + to_chat(M, " You have been banned FOR NO REISIN by [user]") + to_chat(user, " You have BANNED [M]") + playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much + /obj/item/sord name = "\improper SORD" desc = "This thing is so unspeakably shitty you are having a hard time even holding it." diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 6db1846e872..138c77f92eb 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -25,9 +25,13 @@ ..() spawn(1) if(!opened) // if closed, any item at the crate's loc is put in the contents + var/itemcount = 0 for(var/obj/item/I in loc) if(I.density || I.anchored || I == src) continue I.forceMove(src) + // Ensure the storage cap is respected + if(++itemcount >= storage_capacity) + break // Fix for #383 - C4 deleting fridges with corpses /obj/structure/closet/Destroy() 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/crates_lockers/crittercrate.dm b/code/game/objects/structures/crates_lockers/crittercrate.dm index fd726aab6a7..7629fc62918 100644 --- a/code/game/objects/structures/crates_lockers/crittercrate.dm +++ b/code/game/objects/structures/crates_lockers/crittercrate.dm @@ -82,9 +82,8 @@ content_mob = /mob/living/simple_animal/pet/dog/fox /obj/structure/closet/critter/butterfly - name = "butterflies crate" + name = "butterfly crate" content_mob = /mob/living/simple_animal/butterfly - amount = 50 /obj/structure/closet/critter/deer name = "deer crate" 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/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index cd7b3525478..33298669157 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -40,7 +40,7 @@ return if(!in_range(src, user)) return - if(!iscarbon(usr)) + if(!iscarbon(usr) && !isrobot(usr)) return playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3) opened = !opened 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/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm index d8ed888bc27..26921ba9ffd 100644 --- a/code/game/turfs/simulated/floor/chasm.dm +++ b/code/game/turfs/simulated/floor/chasm.dm @@ -97,8 +97,8 @@ return FALSE //Flies right over the chasm if(isliving(AM)) - var/mob/M = AM - if(M.flying) + var/mob/living/M = AM + if(M.flying || M.floating) return FALSE if(ishuman(AM)) var/mob/living/carbon/human/H = AM diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 7fc000aa06f..2f90e4d7cc4 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -165,7 +165,6 @@ var/mob/O = M if(!O.lastarea) O.lastarea = get_area(O.loc) -// O.update_gravity(O.mob_has_gravity(src)) var/loopsanity = 100 for(var/atom/A in range(1)) @@ -264,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/verbs/ooc.dm b/code/game/verbs/ooc.dm index 824159487eb..04ee5901054 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -1,6 +1,6 @@ -GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") +GLOBAL_VAR_INIT(normal_ooc_colour, "#275FC5") GLOBAL_VAR_INIT(member_ooc_colour, "#035417") -GLOBAL_VAR_INIT(mentor_ooc_colour, "#0099cc") +GLOBAL_VAR_INIT(mentor_ooc_colour, "#00B0EB") GLOBAL_VAR_INIT(moderator_ooc_colour, "#184880") GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") @@ -53,6 +53,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") return log_ooc(msg, src) + mob.create_log(OOC_LOG, msg) var/display_colour = GLOB.normal_ooc_colour if(holder && !holder.fakekey) @@ -205,7 +206,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") return log_looc(msg, src) - + mob.create_log(LOOC_LOG, msg) var/mob/source = mob.get_looc_source() var/list/heard = get_mobs_in_view(7, source) diff --git a/code/game/world.dm b/code/game/world.dm index fdb0b8f63be..c279fa24b54 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -266,6 +266,13 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) update_status() return "Set listed status to invisible." + + else if("hostannounce" in input) + if(!key_valid) + return keySpamProtect(addr) + + to_chat(world, "
Server Announcement: [input["message"]]
") + /proc/keySpamProtect(var/addr) if(GLOB.world_topic_spam_protect_ip == addr && abs(GLOB.world_topic_spam_protect_time - world.time) < 50) spawn(50) @@ -333,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/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 09df94dc06e..1f55fa11f70 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -299,7 +299,8 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) datum/admins/proc/DB_ban_unban_by_id(var/id) - if(!check_rights(R_BAN)) return + if(!check_rights(R_BAN)) + return var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]" @@ -343,9 +344,9 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) /client/proc/DB_ban_panel() set category = "Admin" set name = "Banning Panel" - set desc = "Edit admin permissions" + set desc = "DB Ban Panel" - if(!holder) + if(!check_rights(R_BAN)) return holder.DB_ban_panel() @@ -356,7 +357,8 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) if(!usr.client) return - if(!check_rights(R_BAN)) return + if(!check_rights(R_BAN)) + return establish_db_connection() if(!GLOB.dbcon.IsConnected()) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 958dedae362..07dd4975ddb 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -69,7 +69,10 @@ GLOBAL_VAR_INIT(nologevent, 0) body += "Options panel for [M]" if(M.client) body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " + if(check_rights(R_PERMISSIONS, 0)) + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " + else + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]" if(isnewplayer(M)) @@ -110,17 +113,18 @@ GLOBAL_VAR_INIT(nologevent, 0) else body += "Add to Watchlist " - if(M.client) body += "| Prison | " body += "\ Send back to Lobby | " + body += "\ Erase Flavor Text | " + body += "\ Use Random Name | " var/muted = M.client.prefs.muted body += {"
Mute: - \[IC | - OOC | - PRAY | - ADMINHELP | - DEADCHAT\] - (toggle all) + \[IC | + OOC | + PRAY | + ADMINHELP | + DEADCHAT\] + (toggle all) "} var/jumptoeye = "" @@ -281,7 +285,7 @@ GLOBAL_VAR_INIT(nologevent, 0) /datum/admins/proc/vpn_whitelist() set category = "Admin" set name = "VPN Ckey Whitelist" - if(!check_rights(R_ADMIN)) + if(!check_rights(R_BAN)) return var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32) if(key) @@ -700,7 +704,7 @@ GLOBAL_VAR_INIT(nologevent, 0) var/msg = "" if(SSticker.current_state == GAME_STATE_STARTUP) msg = " (The server is still setting up, but the round will be started as soon as possible.)" - message_admins("[usr.key] has started the game.[msg]") + message_admins("[usr.key] has started the game.[msg]") feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return 1 else @@ -761,19 +765,6 @@ GLOBAL_VAR_INIT(nologevent, 0) world.update_status() feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/datum/admins/proc/toggle_aliens() - set category = "Event" - set desc="Toggle alien mobs" - set name="Toggle Aliens" - - if(!check_rights(R_EVENT)) - return - - GLOB.aliens_allowed = !GLOB.aliens_allowed - log_admin("[key_name(usr)] toggled aliens to [GLOB.aliens_allowed].") - message_admins("[key_name_admin(usr)] toggled aliens [GLOB.aliens_allowed ? "on" : "off"].") - feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /datum/admins/proc/delay() set category = "Server" set desc="Delay the game start/end" diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index a627c96148f..527540e9490 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -33,7 +33,8 @@ /client/proc/investigate_show( subject in GLOB.investigate_log_subjects ) set name = "Investigate" set category = "Admin" - if(!holder) return + if(!check_rights(R_ADMIN)) + return switch(subject) if("notes") show_note() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index b7f6a9d6bc9..b3957a86b44 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -10,11 +10,8 @@ GLOBAL_LIST_INIT(admin_verbs_default, list( GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/check_antagonists, /*shows all antags*/ /datum/admins/proc/show_player_panel, - /client/proc/player_panel, /*shows an interface for all players, with links to various panels (old style)*/ /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ /client/proc/invisimin, /*allows our mob to go invisible/visible*/ - /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ - /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ /datum/admins/proc/announce, /*priority announce something to all clients.*/ /client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/ /client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/ @@ -23,7 +20,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ - /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ /client/proc/cmd_admin_open_logging_view, @@ -55,36 +51,30 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /datum/admins/proc/PlayerNotes, /client/proc/cmd_mentor_say, /datum/admins/proc/show_player_notes, - /datum/admins/proc/vpn_whitelist, /client/proc/free_slot, /*frees slot for chosen job*/ /client/proc/toggleattacklogs, /client/proc/toggleadminlogs, /client/proc/toggledebuglogs, /client/proc/update_mob_sprite, - /client/proc/toggledrones, /client/proc/man_up, /client/proc/global_man_up, /client/proc/delbook, /client/proc/view_flagged_books, + /client/proc/view_asays, /client/proc/empty_ai_core_toggle_latejoin, /client/proc/aooc, /client/proc/freeze, - /client/proc/alt_check, /client/proc/secrets, - /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ - /client/proc/change_human_appearance_self, /* Allows the human-based mob itself to change its basic appearance */ /client/proc/debug_variables, /client/proc/reset_all_tcs, /*resets all telecomms scripts*/ /client/proc/toggle_mentor_chat, /client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/ - /client/proc/list_ssds_afks, - /client/proc/cmd_admin_headset_message, - /client/proc/spawn_floor_cluwne + /client/proc/list_ssds_afks )) GLOBAL_LIST_INIT(admin_verbs_ban, list( - /client/proc/unban_panel, - /client/proc/jobbans, - /client/proc/stickybanpanel + /client/proc/ban_panel, + /client/proc/stickybanpanel, + /datum/admins/proc/vpn_whitelist )) GLOBAL_LIST_INIT(admin_verbs_sounds, list( /client/proc/play_local_sound, @@ -100,7 +90,6 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/drop_bomb, /client/proc/cinematic, /client/proc/one_click_antag, - /datum/admins/proc/toggle_aliens, /client/proc/cmd_admin_add_freeform_ai_law, /client/proc/cmd_admin_add_random_ai_law, /client/proc/make_sound, @@ -110,6 +99,7 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/show_tip, /client/proc/cmd_admin_change_custom_event, /datum/admins/proc/access_news_network, /*allows access of newscasters*/ + /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ /client/proc/response_team, // Response Teams admin verb @@ -117,7 +107,10 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/fax_panel, /client/proc/event_manager_panel, /client/proc/modify_goals, - /client/proc/outfit_manager + /client/proc/outfit_manager, + /client/proc/cmd_admin_headset_message, + /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ + /client/proc/change_human_appearance_self /* Allows the human-based mob itself to change its basic appearance */ )) GLOBAL_LIST_INIT(admin_verbs_spawn, list( @@ -126,24 +119,27 @@ GLOBAL_LIST_INIT(admin_verbs_spawn, list( /client/proc/admin_deserialize )) GLOBAL_LIST_INIT(admin_verbs_server, list( + /client/proc/reload_admins, /client/proc/Set_Holiday, /datum/admins/proc/startnow, /datum/admins/proc/restart, /datum/admins/proc/delay, /datum/admins/proc/toggleaban, + /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ + /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ /client/proc/toggle_log_hrefs, /client/proc/everyone_random, /datum/admins/proc/toggleAI, /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ - /client/proc/cmd_debug_del_all, /client/proc/cmd_debug_del_sing, - /datum/admins/proc/toggle_aliens, /client/proc/delbook, /client/proc/view_flagged_books, + /client/proc/view_asays, /client/proc/toggle_antagHUD_use, /client/proc/toggle_antagHUD_restrictions, /client/proc/set_ooc, - /client/proc/reset_ooc + /client/proc/reset_ooc, + /client/proc/toggledrones )) GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/cmd_admin_list_open_jobs, @@ -152,9 +148,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/debug_controller, /client/proc/cmd_debug_mob_lists, /client/proc/cmd_admin_delete, - /client/proc/cmd_debug_del_all, /client/proc/cmd_debug_del_sing, - /client/proc/reload_admins, /client/proc/restart_controller, /client/proc/enable_debug_verbs, /client/proc/toggledebuglogs, @@ -197,7 +191,7 @@ GLOBAL_LIST_INIT(admin_verbs_mod, list( /client/proc/player_panel_new, /client/proc/dsay, /datum/admins/proc/show_player_panel, - /client/proc/jobbans, + /client/proc/ban_panel, /client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ )) GLOBAL_LIST_INIT(admin_verbs_mentor, list( @@ -367,19 +361,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") mob.remove_from_all_data_huds() -/client/proc/player_panel() - set name = "Player Panel" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.player_panel_old() - feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - /client/proc/player_panel_new() - set name = "Player Panel New" + set name = "Player Panel" set category = "Admin" if(!check_rights(R_ADMIN|R_MOD)) @@ -401,22 +384,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return -/client/proc/jobbans() - set name = "Display Job bans" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(config.ban_legacy_system) - holder.Jobbans() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/unban_panel() - set name = "Unban Panel" +/client/proc/ban_panel() + set name = "Ban Panel" set category = "Admin" if(!check_rights(R_BAN)) @@ -809,7 +778,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( set desc = "Allows you to change the mob appearance" set category = null - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return if(!istype(H)) @@ -835,7 +804,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( set desc = "Allows the mob to change its appearance" set category = null - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return if(!istype(H)) @@ -980,8 +949,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( else to_chat(usr, "You now won't get debug log messages") -/client/proc/man_up(mob/T as mob in GLOB.mob_list) - set category = "Admin" +/client/proc/man_up(mob/T as mob in GLOB.player_list) + set category = null set name = "Man Up" set desc = "Tells mob to man up and deal with it." diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 2122f88f4ed..699eca075bb 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -42,20 +42,6 @@ GLOBAL_DATUM_INIT(jobban_regex, /regex, regex("(\[\\S]+) - (\[^#]+\[^# ])(?: ## else return 0 -/* -DEBUG -/mob/verb/list_all_jobbans() - set name = "list all jobbans" - - for(var/s in jobban_keylist) - to_chat(world, s) - -/mob/verb/reload_jobbans() - set name = "reload jobbans" - - jobban_loadbanfile() -*/ - /proc/jobban_loadbanfile() if(config.ban_legacy_system) var/savefile/S=new("data/job_full.ban") diff --git a/code/modules/admin/machine_upgrade.dm b/code/modules/admin/machine_upgrade.dm index 1e569b51d2d..4f38f70613d 100644 --- a/code/modules/admin/machine_upgrade.dm +++ b/code/modules/admin/machine_upgrade.dm @@ -1,20 +1,20 @@ /proc/machine_upgrade(obj/machinery/M in world) set name = "Tweak Component Ratings" - set category = "Debug" + set category = null - if(!check_rights(R_DEBUG)) + if(!check_rights(R_DEBUG)) return - + if(!istype(M)) to_chat(usr, "This can only be used on subtypes of /obj/machinery.") return - + var/new_rating = input("Enter new rating:","Num") as num if(!isnull(new_rating) && M.component_parts) for(var/obj/item/stock_parts/P in M.component_parts) P.rating = new_rating M.RefreshParts() - + message_admins("[key_name_admin(usr)] has set the component rating of [M] to [new_rating]") log_admin("[key_name(usr)] has set the component rating of [M] to [new_rating]") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 1c6bab4d3cd..c2248261baa 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -332,65 +332,6 @@ usr << browse(dat, "window=players;size=600x480") -//The old one -/datum/admins/proc/player_panel_old() - if(!usr.client.holder) - return - var/dat = "Player Menu" - dat += "" - //add to this if wanting to add back in IP checking - //add if you want to know their ip to the lists below - var/list/mobs = sortmobs() - - for(var/mob/M in mobs) - if(!M.ckey) continue - - dat += "" - if(isAI(M)) - dat += "" - else if(isrobot(M)) - dat += "" - else if(issmall(M)) - dat += "" - else if(ishuman(M)) - dat += "" - else if(istype(M, /mob/living/silicon/pai)) - dat += "" - else if(isnewplayer(M)) - dat += "" - else if(isobserver(M)) - dat += "" - else if(isalien(M)) - dat += "" - else - dat += "" - - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.mind && H.mind.assigned_role) - dat += "" - else - dat += "" - - - dat += {" - - - "} - switch(is_special_character(M)) - if(0) - dat += {""} - if(1) - dat += {""} - if(2) - dat += {""} - - dat += "
NameReal NameAssigned JobKeyOptionsPMTraitor?
IP:(IP: [M.lastKnownIP])
[M.name]AICyborgMonkey[M.real_name]pAINew PlayerGhostAlienUnknown[H.mind.assigned_role]NA[(M.client ? "[M.client]" : "No client")]XPMTraitor?Traitor?Traitor?
" - - usr << browse(dat, "window=players;size=640x480") - - /datum/admins/proc/check_antagonists_line(mob/M, caption = "", close = 1) var/logout_status diff --git a/code/modules/admin/tickets/adminticketsverbs.dm b/code/modules/admin/tickets/adminticketsverbs.dm index 406019745f8..4e8e79f2a26 100644 --- a/code/modules/admin/tickets/adminticketsverbs.dm +++ b/code/modules/admin/tickets/adminticketsverbs.dm @@ -5,7 +5,7 @@ set name = "Open Admin Ticket Interface" set category = "Admin" - if(!holder || !check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN)) return SStickets.showUI(usr) @@ -14,7 +14,7 @@ set name = "Resolve All Open Admin Tickets" set category = null - if(!holder || !check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN)) return if(alert("Are you sure you want to resolve ALL open admin tickets?","Resolve all open admin tickets?","Yes","No") != "Yes") diff --git a/code/modules/admin/tickets/mentorticketsverbs.dm b/code/modules/admin/tickets/mentorticketsverbs.dm index d65f4a30220..c7bec3e043d 100644 --- a/code/modules/admin/tickets/mentorticketsverbs.dm +++ b/code/modules/admin/tickets/mentorticketsverbs.dm @@ -5,7 +5,7 @@ set name = "Open Mentor Ticket Interface" set category = "Admin" - if(!holder || !check_rights(R_MENTOR|R_ADMIN)) + if(!check_rights(R_MENTOR|R_ADMIN)) return SSmentor_tickets.showUI(usr) @@ -14,7 +14,7 @@ set name = "Resolve All Open Mentor Tickets" set category = null - if(!holder || !check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN)) return if(alert("Are you sure you want to resolve ALL open mentor tickets?","Resolve all open mentor tickets?","Yes","No") != "Yes") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 1d2c75aff0a..2390394b280 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -14,7 +14,7 @@ if(!check_rights(R_ADMIN|R_MOD)) return var/client/C = locateUID(href_list["rejectadminhelp"]) - if(!C) + if(!isclient(C)) return C << 'sound/effects/adminhelp.ogg' @@ -114,39 +114,39 @@ switch(bantype) if(BANTYPE_PERMA) if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_TEMP) if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") return banjob = null if(BANTYPE_JOB_PERMA) if(!banckey || !banreason || !banjob) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") return banduration = null if(BANTYPE_JOB_TEMP) if(!banckey || !banreason || !banjob || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") return if(BANTYPE_APPEARANCE) if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_ADMIN_PERMA) if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_ADMIN_TEMP) if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") return banjob = null @@ -324,8 +324,8 @@ if(!check_rights(R_SPAWN)) return var/mob/M = locateUID(href_list["mob"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return var/delmob = 0 @@ -436,18 +436,18 @@ if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["appearanceban"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") + to_chat(usr, "This mob has no ckey") return var/ban_ckey_param = href_list["dbbanaddckey"] var/banreason = appearance_isbanned(M) if(banreason) /* if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") DB_ban_panel(M.ckey) return */ switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No")) @@ -458,7 +458,7 @@ DB_ban_unban(M.ckey, BANTYPE_APPEARANCE) appearance_unban(M) message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1) - to_chat(M, "[usr.client.ckey] has removed your appearance ban.") + to_chat(M, "[usr.client.ckey] has removed your appearance ban.") else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel")) if("Yes") @@ -473,7 +473,7 @@ appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0) message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1) - to_chat(M, "You have been appearance banned by [usr.client.ckey].") + to_chat(M, "You have been appearance banned by [usr.client.ckey].") to_chat(M, "The reason is: [reason]") to_chat(M, "Appearance ban can be lifted only upon request.") if(config.banappeals) @@ -487,15 +487,15 @@ // if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["jobban2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") + to_chat(usr, "This mob has no ckey") return if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") + to_chat(usr, "SSjobs has not been setup!") return var/dat = "" @@ -735,8 +735,8 @@ if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["jobban4"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(M != usr) //we can jobban ourselves @@ -747,7 +747,7 @@ var/ban_ckey_param = href_list["dbbanaddckey"] if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") + to_chat(usr, "SSjobs has not been setup!") return //get jobs for department if specified, otherwise just returnt he one job in a list. @@ -840,7 +840,7 @@ msg += ", [job]" add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") to_chat(M, "The reason is: [reason]") to_chat(M, "This jobban will be lifted in [mins] minutes.") href_list["jobban2"] = 1 // lets it fall through and refresh @@ -861,7 +861,7 @@ else msg += ", [job]" add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") to_chat(M, "The reason is: [reason]") to_chat(M, "Jobban can be lifted only upon request.") href_list["jobban2"] = 1 // lets it fall through and refresh @@ -873,7 +873,7 @@ //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") DB_ban_panel(M.ckey) return var/msg @@ -894,22 +894,28 @@ continue if(msg) message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") + to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") href_list["jobban2"] = 1 // lets it fall through and refresh return 1 return 0 //we didn't do anything! else if(href_list["boot2"]) var/mob/M = locateUID(href_list["boot2"]) - if(ismob(M)) - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) - to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.") + if(!ismob(M)) + return + var/client/C = M.client + if(C == null) + to_chat(usr, "Mob has no client to kick.") + return + if(alert("Kick [C.ckey]?",,"Yes","No") == "Yes") + if(C && C.holder && (C.holder.rights & R_BAN)) + to_chat(usr, "[key_name_admin(C)] cannot be kicked from the server.") return - to_chat(M, "You have been kicked from the server") - log_admin("[key_name(usr)] booted [key_name(M)].") - message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) - //M.client = null - qdel(M.client) + to_chat(C, "You have been kicked from the server") + log_admin("[key_name(usr)] booted [key_name(C)].") + message_admins("[key_name_admin(usr)] booted [key_name_admin(C)].", 1) + //C = null + qdel(C) else if(href_list["open_logging_view"]) var/mob/M = locateUID(href_list["open_logging_view"]) @@ -986,7 +992,7 @@ if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["newban"]) - if(!ismob(M)) + if(!istype(M, /mob)) return var/ban_ckey_param = href_list["dbbanaddckey"] @@ -1002,7 +1008,7 @@ M = admin_ban_mobsearch(M, ban_ckey_param, usr) AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") feedback_inc("ban_tmp",1) DB_ban_record(BANTYPE_TEMP, M, mins, reason) @@ -1022,7 +1028,7 @@ if(!reason) return AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") to_chat(M, "This ban does not expire automatically and must be appealed.") if(M.client) M.client.link_forum_account(TRUE) @@ -1090,7 +1096,7 @@ return var/mob/M = locateUID(href_list["mute"]) - if(!ismob(M)) return + if(!istype(M, /mob)) return if(!M.client) return var/mute_type = href_list["mute_type"] @@ -1104,7 +1110,7 @@ if(SSticker && SSticker.mode) return alert(usr, "The game has already started.", null, null, null, null) - var/dat = {"What mode do you wish to play?
"} + var/dat = {"What mode do you wish to play?
"} for(var/mode in config.modes) dat += {"[config.mode_names[mode]]
"} dat += {"Secret
"} @@ -1119,7 +1125,7 @@ return alert(usr, "The game has already started.", null, null, null, null) if(GLOB.master_mode != "secret") return alert(usr, "The game mode has to be secret!", null, null, null, null) - var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
"} + var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
"} for(var/mode in config.modes) dat += {"[config.mode_names[mode]]
"} dat += {"Random (default)
"} @@ -1157,7 +1163,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes") return @@ -1172,7 +1178,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["corgione"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes") @@ -1187,7 +1193,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makePAI"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes") return @@ -1210,8 +1216,9 @@ if(!check_rights(R_SERVER|R_EVENT)) return var/mob/M = locateUID(href_list["forcespeech"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. if(!speech) return @@ -1227,11 +1234,11 @@ return var/mob/M = locateUID(href_list["sendtoprison"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return var/turf/prison_cell = pick(GLOB.prisonwarp) @@ -1290,6 +1297,71 @@ NP.ckey = M.ckey qdel(M) + else if(href_list["eraseflavortext"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["eraseflavortext"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(M.flavor_text == "" && M.client.prefs.flavor_text == "") + to_chat(usr, "[M] has no flavor text set.") + return + + if(alert(usr, "Erase [key_name(M)]'s flavor text?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has erased [key_name(M)]'s flavor text.") + message_admins("[key_name_admin(usr)] has erased [key_name_admin(M)]'s flavor text.") + + // Clears the mob's flavor text + M.flavor_text = "" + + // Clear and save the DB character's flavor text + M.client.prefs.flavor_text = "" + M.client.prefs.save_character(M.client) + + else if(href_list["userandomname"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["userandomname"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(alert(usr, "Force [key_name(M)] to use a random name?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has forced [key_name(M)] to use a random name.") + message_admins("[key_name_admin(usr)] has forced [key_name_admin(M)] to use a random name.") + + // Update the mob's name with a random one straight away + var/random_name = random_name(M.client.prefs.gender, M.client.prefs.species) + M.rename_character(M.real_name, random_name) + + // Save that random name for next rounds + M.client.prefs.real_name = random_name + M.client.prefs.save_character(M.client) + + else if(href_list["asays"]) + if(!check_rights(R_ADMIN)) + return + + usr.client.view_asays() + else if(href_list["tdome1"]) if(!check_rights(R_SERVER|R_EVENT)) return @@ -1297,11 +1369,11 @@ return var/mob/M = locateUID(href_list["tdome1"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1327,11 +1399,11 @@ return var/mob/M = locateUID(href_list["tdome2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1357,11 +1429,11 @@ return var/mob/M = locateUID(href_list["tdomeadmin"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return M.Paralyse(5) @@ -1379,11 +1451,11 @@ return var/mob/M = locateUID(href_list["tdomeobserve"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1413,11 +1485,11 @@ return var/mob/M = locateUID(href_list["aroomwarp"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return M.Paralyse(5) @@ -1434,7 +1506,7 @@ var/mob/living/L = locateUID(href_list["revive"]) if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return L.revive() @@ -1446,7 +1518,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes") @@ -1462,7 +1534,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes") return @@ -1474,7 +1546,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes") return @@ -1486,7 +1558,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes") @@ -1499,7 +1571,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes") return @@ -1511,7 +1583,7 @@ var/mob/M = locateUID(href_list["makeanimal"]) if(isnewplayer(M)) - to_chat(usr, "This cannot be used on instances of type /mob/new_player") + to_chat(usr, "This cannot be used on instances of type /mob/new_player") return if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") return @@ -1524,10 +1596,14 @@ var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"]) if(!istype(G)) - to_chat(usr, "This will only work on /mob/dead/observer") + to_chat(usr, "This will only work on /mob/dead/observer") + return var/posttransformoutfit = usr.client.robust_dress_shop() + if(!posttransformoutfit) + return + var/mob/living/carbon/human/H = G.incarnate_ghost() if(posttransformoutfit && istype(H)) @@ -1541,7 +1617,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["togmutate"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/block=text2num(href_list["block"]) //testing("togmutate([href_list["block"]] -> [block])") @@ -1551,6 +1627,11 @@ else if(href_list["adminplayeropts"]) var/mob/M = locateUID(href_list["adminplayeropts"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + show_player_panel(M) else if(href_list["adminplayerobservefollow"]) @@ -1560,6 +1641,11 @@ return C.admin_ghost() var/mob/M = locateUID(href_list["adminplayerobservefollow"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + var/mob/dead/observer/A = C.mob sleep(2) A.ManualFollow(M) @@ -1588,6 +1674,12 @@ return SStickets.autoRespond(index) + if(href_list["convert_ticket"]) + var/indexNum = text2num(href_list["convert_ticket"]) + if(href_list["is_mhelp"]) + SSmentor_tickets.convert_to_other_ticket(indexNum) + else + SStickets.convert_to_other_ticket(indexNum) else if(href_list["cult_nextobj"]) if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes") return @@ -1608,10 +1700,10 @@ for(var/datum/mind/H in SSticker.mode.cult) if (H.current) - to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]
") + to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]") for(var/mob/dead/observer/O in GLOB.player_list) - to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]
") + to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]") message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].") log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]") @@ -1633,6 +1725,11 @@ else if(href_list["adminmoreinfo"]) var/mob/M = locateUID(href_list["adminmoreinfo"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + admin_mob_info(M) else if(href_list["adminspawncookie"]) @@ -1640,7 +1737,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"]) if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand ) @@ -1664,7 +1761,7 @@ var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"]) if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") @@ -1702,6 +1799,11 @@ return var/mob/M = locateUID(href_list["CentcommReply"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + usr.client.admin_headset_message(M, "Centcomm") else if(href_list["SyndicateReply"]) @@ -1709,6 +1811,11 @@ return var/mob/M = locateUID(href_list["SyndicateReply"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + usr.client.admin_headset_message(M, "Syndicate") else if(href_list["HeadsetMessage"]) @@ -1716,6 +1823,11 @@ return var/mob/M = locateUID(href_list["HeadsetMessage"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + usr.client.admin_headset_message(M) else if(href_list["EvilFax"]) @@ -1723,7 +1835,7 @@ return var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine") var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes @@ -1757,12 +1869,12 @@ P.ico = new P.ico += "paper_stamp-[stampvalue]" P.overlays += stampoverlay - P.stamps += "
" + P.stamps += "
" P.update_icon() P.faxmachineid = fax.UID() P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required. if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") to_chat(src.owner, "You sent a [eviltype] fax to [H]") log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax") message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax") @@ -1771,7 +1883,7 @@ return var/mob/living/M = locateUID(href_list["Bless"]) if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return var/btypes = list("To Arrivals", "Moderate Heal") var/mob/living/carbon/human/H @@ -1838,7 +1950,7 @@ var/petchoice = input("Select pet type", "Pets") as null|anything in pets if(isnull(petchoice)) return - var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Play as the special event pet [H]?", poll_time = 20 SECONDS, min_hours = 10, source = petchoice) var/mob/dead/observer/theghost = null if(candidates.len) var/mob/living/simple_animal/pet/P = new petchoice(H.loc) @@ -1891,7 +2003,7 @@ var/mob/living/M = locateUID(href_list["Smite"]) var/mob/living/carbon/human/H if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return var/ptypes = list("Lightning bolt", "Fire Death", "Gib") if(ishuman(M)) @@ -2002,7 +2114,7 @@ var/datum/antagonist/traitor/T = new() T.give_objectives = FALSE to_chat(newtraitormind.current, "ATTENTION: It is time to pay your debt to the Syndicate...") - to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") + to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") newtraitormind.add_antag_datum(T) else to_chat(usr, "ERROR: Unable to find any valid candidate to send after [H].") @@ -2031,10 +2143,10 @@ return var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(!href_list["cryoafk"] && !isLivingSSD(H)) - to_chat(usr, "This can only be used on living, SSD players.") + to_chat(usr, "This can only be used on living, SSD players.") return if(istype(H.loc, /obj/machinery/cryopod)) var/obj/machinery/cryopod/P = H.loc @@ -2054,28 +2166,28 @@ return var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/obj/item/paper/P = new /obj/item/paper(null) var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) P.name = "Central Command - paper" var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions") var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes - var/tmsg = "



Nanotrasen Science Station [GLOB.using_map.station_short]


NAS Trurl Communications Department Report


" + var/tmsg = "



Nanotrasen Science Station [GLOB.using_map.station_short]


NAS Trurl Communications Department Report


" if(stype == "Handle it yourselves!") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

This is an automatic message." else if(stype == "Illegible fax") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

Please consult your nearest dictionary and/or thesaurus and try again.

This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

Please consult your nearest dictionary and/or thesaurus and try again.

This is an automatic message." else if(stype == "Fax not signed") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Your fax has not been correctly signed and, as such, we cannot verify your identity.

Please sign your faxes before sending them so that we may verify your identity.

This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Your fax has not been correctly signed and, as such, we cannot verify your identity.

Please sign your faxes before sending them so that we may verify your identity.

This is an automatic message." else if(stype == "Not Right Now") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

Please try again later.

This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

Please try again later.

This is an automatic message." else if(stype == "You are wasting our time") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

This is an automatic message." else if(stype == "Keep up the good work") tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.

We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.

This is absolutely not an automated message." else if(stype == "ERT Instructions") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Please utilize the Card Swipers if you wish to call for an ERT.

This is an automated message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

Please utilize the Card Swipers if you wish to call for an ERT.

This is an automated message." else return tmsg += "
" @@ -2096,11 +2208,11 @@ P.ico = new P.ico += "paper_stamp-[stampvalue]" P.overlays += stampoverlay - P.stamps += "
" + P.stamps += "
" P.update_icon() fax.receivefax(P) if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") to_chat(src.owner, "You sent a standard '[stype]' fax to [H]") log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax") message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax") @@ -2108,10 +2220,10 @@ else if(href_list["HONKReply"]) var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") + to_chat(usr, "The person you are trying to contact is not wearing a headset") return var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "") @@ -2128,13 +2240,13 @@ if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny") var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(H.stat != 0) - to_chat(usr, "The person you are trying to contact is not conscious.") + to_chat(usr, "The person you are trying to contact is not conscious.") return if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") + to_chat(usr, "The person you are trying to contact is not wearing a headset") return var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "") @@ -2142,7 +2254,7 @@ GLOB.ert_request_answered = TRUE to_chat(src.owner, "You sent [input] to [H] via a secure channel.") log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].") - to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].") + to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].") else src.owner.response_team() @@ -2297,14 +2409,14 @@ P.stamped = new P.stamped += /obj/item/stamp/centcom P.overlays += stampoverlay - P.stamps += "
" + P.stamps += "
" else if(stamptype == "text") if(!P.stamped) P.stamped = new P.stamped += /obj/item/stamp P.overlays += stampoverlay - P.stamps += "
[stampvalue]" + P.stamps += "
[stampvalue]" if(destination != "All Departments") if(!fax.receivefax(P)) @@ -2334,7 +2446,7 @@ if(notify == "Yes") var/mob/living/carbon/human/H = sender if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") if(sender) log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]") message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1) @@ -2353,8 +2465,8 @@ if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["getplaytimewindow"]) - if(!M) - to_chat(usr, "ERROR: Mob not found.") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return cmd_mentor_show_exp_panel(M.client) @@ -2362,6 +2474,9 @@ if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["jumpto"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.jumptomob(M) else if(href_list["getmob"]) @@ -2369,24 +2484,37 @@ if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return var/mob/M = locateUID(href_list["getmob"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.Getmob(M) else if(href_list["sendmob"]) if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["sendmob"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.sendmob(M) else if(href_list["narrateto"]) if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["narrateto"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.cmd_admin_direct_narrate(M) else if(href_list["subtlemessage"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_EVENT)) + return var/mob/M = locateUID(href_list["subtlemessage"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.cmd_admin_subtle_message(M) else if(href_list["traitor"]) @@ -2397,8 +2525,8 @@ return var/mob/M = locateUID(href_list["traitor"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return show_traitor_panel(M) @@ -2467,7 +2595,7 @@ switch(where) if("inhand") if(!iscarbon(usr) && !isrobot(usr)) - to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") + to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") where = "onfloor" target = usr @@ -2479,10 +2607,10 @@ target = locate(loc.x + X,loc.y + Y,loc.z + Z) if("inmarked") if(!marked_datum) - to_chat(usr, "You don't have any object marked. Abandoning spawn.") + to_chat(usr, "You don't have any object marked. Abandoning spawn.") return else if(!istype(marked_datum,/atom)) - to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") return else target = marked_datum @@ -2547,7 +2675,7 @@ message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") else - to_chat(usr, "You may only use this when the game is running.") + to_chat(usr, "You may only use this when the game is running.") else if(href_list["memoeditlist"]) if(!check_rights(R_SERVER)) return @@ -2627,7 +2755,7 @@ feedback_add_details("admin_secrets_fun_used","TriAI") if("gravity") if(!(SSticker && SSticker.mode)) - to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") + to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") return GLOB.gravity_is_on = !GLOB.gravity_is_on for(var/area/A in world) @@ -2964,7 +3092,7 @@ if(usr) log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) + to_chat(world, text("A secret has been activated by []!", usr.key)) else if(href_list["secretsadmin"]) if(!check_rights(R_ADMIN)) return @@ -2972,17 +3100,17 @@ var/ok = 0 switch(href_list["secretsadmin"]) if("list_signalers") - var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
" + var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
" for(var/sig in GLOB.lastsignalers) dat += "[sig]
" usr << browse(dat, "window=lastsignalers;size=800x500") if("list_lawchanges") - var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
" + var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
" for(var/sig in GLOB.lawchanges) dat += "[sig]
" usr << browse(dat, "window=lawchanges;size=800x500") if("list_job_debug") - var/dat = "Job Debug info.
" + var/dat = "Job Debug info.
" if(SSjobs) for(var/line in SSjobs.job_debug) dat += "[line]
" @@ -3000,7 +3128,7 @@ alert("The game mode is [SSticker.mode.name]") else alert("For some reason there's a ticker, but not a game mode") if("manifest") - var/dat = "Showing Crew Manifest.
" + var/dat = "Showing Crew Manifest.
" dat += "" for(var/thing in GLOB.human_list) var/mob/living/carbon/human/H = thing @@ -3011,7 +3139,7 @@ if("check_antagonist") check_antagonists() if("DNA") - var/dat = "Showing DNA from blood.
" + var/dat = "Showing DNA from blood.
" dat += "
NamePosition
" for(var/thing in GLOB.human_list) var/mob/living/carbon/human/H = thing @@ -3020,7 +3148,7 @@ dat += "
NameDNABlood Type
" usr << browse(dat, "window=DNA;size=440x410") if("fingerprints") - var/dat = "Showing Fingerprints.
" + var/dat = "Showing Fingerprints.
" dat += "" for(var/thing in GLOB.human_list) var/mob/living/carbon/human/H = thing @@ -3055,14 +3183,14 @@ if(usr) log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) + to_chat(world, text("A secret has been activated by []!", usr.key)) else if(href_list["secretscoder"]) if(!check_rights(R_DEBUG)) return switch(href_list["secretscoder"]) if("spawn_objects") - var/dat = "Admin Log
" + var/dat = "Admin Log
" for(var/l in GLOB.admin_log) dat += "
  • [l]
  • " if(!GLOB.admin_log.len) @@ -3232,27 +3360,27 @@ else if(href_list["ac_censor_channel_author"]) var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) - if(FC.author != "\[REDACTED\]") + if(FC.author != "\[REDACTED\]") FC.backup_author = FC.author - FC.author = "\[REDACTED\]" + FC.author = "\[REDACTED\]" else FC.author = FC.backup_author src.access_news_network() else if(href_list["ac_censor_channel_story_author"]) var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) - if(MSG.author != "\[REDACTED\]") + if(MSG.author != "\[REDACTED\]") MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" + MSG.author = "\[REDACTED\]" else MSG.author = MSG.backup_author src.access_news_network() else if(href_list["ac_censor_channel_story_body"]) var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) - if(MSG.body != "\[REDACTED\]") + if(MSG.body != "\[REDACTED\]") MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" + MSG.body = "\[REDACTED\]" else MSG.body = MSG.backup_body src.access_news_network() @@ -3448,14 +3576,15 @@ return var/datum/outfit/O = hunter_outfits[dresscode] message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") - var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_traitor") + var/list/candidates = SSghost_spawns.poll_candidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, TRUE, source = source) if(!candidates.len) - to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") + to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") return var/mob/C = pick(candidates) var/key_of_hunter = C.key if(!key_of_hunter) - to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") + to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") return var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) hunter_mind.active = 1 @@ -3486,9 +3615,9 @@ hunter_mind.objectives += protect_objective SSticker.mode.traitors |= hunter_mob.mind to_chat(hunter_mob, "ATTENTION: You are now on a mission!") - to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. ") + to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]."); if(killthem) - to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived.") + to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR var/datum/atom_hud/antag/tatorhud = GLOB.huds[ANTAG_HUD_TRAITOR] tatorhud.join_hud(hunter_mob) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index f74b8c9d030..0b88d6030bb 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -80,7 +80,7 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo mobs_found += found if(!ai_found && isAI(found)) ai_found = 1 - msg += "[original_word] " + msg += "[original_word] " continue msg += "[original_word] " @@ -125,22 +125,28 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo ticketNum = T.ticketNum // ticketNum is the number of their ticket. T.addResponse(src, msg) - msg = "[span][selected_type]: [key_name(src, TRUE, selected_type)] ([ADMIN_QUE(mob,"?")]) ([ADMIN_PP(mob,"PP")]) ([ADMIN_VV(mob,"VV")]) ([ADMIN_TP(mob,"TP")]) ([ADMIN_SM(mob,"SM")]) ([admin_jump_link(mob)]) (TICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" + var/finalised_msg = "[span][selected_type]: [key_name(src, TRUE, selected_type)] " + finalised_msg += "([ADMIN_QUE(mob,"?")]) ([ADMIN_PP(mob,"PP")]) ([ADMIN_VV(mob,"VV")]) ([ADMIN_TP(mob,"TP")]) ([ADMIN_SM(mob,"SM")]) " + finalised_msg += "([admin_jump_link(mob)]) (TICKET) " + finalised_msg += "[ai_found ? "(CL)" : ""] (TAKE) " + finalised_msg += "(RESOLVE) [isMhelp ? "" : "(AUTO)"] " + finalised_msg += "(CONVERT) : [span][msg]" + if(isMhelp) //Open a new adminticket and inform the user. - SSmentor_tickets.newTicket(src, prunedmsg, msg) + SSmentor_tickets.newTicket(src, prunedmsg, finalised_msg) for(var/client/X in mentorholders + modholders + adminholders) if(X.prefs.sound & SOUND_MENTORHELP) - X << 'sound/effects/adminhelp.ogg' - to_chat(X, msg) + SEND_SOUND(X, 'sound/effects/adminhelp.ogg') + to_chat(X, finalised_msg) else //Ahelp //Open a new adminticket and inform the user. - SStickets.newTicket(src, prunedmsg, msg) + SStickets.newTicket(src, prunedmsg, finalised_msg) for(var/client/X in modholders + adminholders) if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' + SEND_SOUND(X, 'sound/effects/adminhelp.ogg') window_flash(X) - to_chat(X, msg) + to_chat(X, finalised_msg) diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index ca83b5bad08..e5e5dae03b5 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -117,7 +117,7 @@ feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/Getmob(var/mob/M in GLOB.mob_list) - set category = "Admin" + set category = null set name = "Get Mob" set desc = "Mob to teleport" @@ -134,7 +134,7 @@ feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/Getkey() - set category = "Admin" + set category = null set name = "Get Key" set desc = "Key to teleport" diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index dba9a5c26aa..ce5f06053eb 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -2,19 +2,19 @@ /client/proc/cmd_admin_pm_context(mob/M as mob in GLOB.mob_list) set category = null set name = "Admin PM Mob" - if(!holder) - to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.") + if(!check_rights(R_ADMIN|R_MENTOR)) + return + if(!ismob(M) || !M.client) return - if( !ismob(M) || !M.client ) return cmd_admin_pm(M.client,null) feedback_add_details("admin_verb","APMM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + //shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm /client/proc/cmd_admin_pm_panel() set category = "Admin" set name = "Admin PM Name" - if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + if(!check_rights(R_ADMIN|R_MENTOR)) return var/list/client/targets[0] for(var/client/T) @@ -36,8 +36,7 @@ /client/proc/cmd_admin_pm_by_key_panel() set category = "Admin" set name = "Admin PM Key" - if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + if(!check_rights(R_ADMIN|R_MENTOR)) return var/list/client/targets[0] for(var/client/T) diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 5a0425af30a..431633309a9 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -7,6 +7,8 @@ msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) if(!msg) return + var/datum/asays/asay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday) + GLOB.asays += asay log_adminsay(msg, src) if(check_rights(R_ADMIN,0)) diff --git a/code/modules/admin/verbs/alt_check.dm b/code/modules/admin/verbs/alt_check.dm deleted file mode 100644 index c8060f22036..00000000000 --- a/code/modules/admin/verbs/alt_check.dm +++ /dev/null @@ -1,20 +0,0 @@ -/client/proc/alt_check() - set category = "Admin" - set name = "Alt Account Checker" - - var/dat = {"Just to be sure you should try to also look up computer IDs/IPs on the server logs for a second opinion. -
    Additionally make an attempt to introduce new players to the server -
    "} - - if(GLOB.dbcon.IsConnected()) - for(var/client/C in GLOB.clients) - dat += "

    [C.ckey] (Player Age: [C.player_age]) - [C.computer_id] / [C.address]
    " - if(C.related_accounts_cid.len) - dat += "--Accounts associated with CID: " - dat += "[jointext(C.related_accounts_cid, " - ")]
    " - if(C.related_accounts_ip.len) - dat += "--Accounts associated with IP: " - dat += "[jointext(C.related_accounts_ip, " - ")] " - usr << browse(dat, "window=alt_panel;size=640x480") - return - diff --git a/code/modules/admin/verbs/asays.dm b/code/modules/admin/verbs/asays.dm new file mode 100644 index 00000000000..e713d2b1bcc --- /dev/null +++ b/code/modules/admin/verbs/asays.dm @@ -0,0 +1,70 @@ +GLOBAL_LIST_EMPTY(asays) + +/datum/asays + var/ckey + var/rank + var/message + var/time + +/datum/asays/New(ckey = "", rank = "", message = "", time = 0) + src.ckey = ckey + src.rank = rank + src.message = message + src.time = time + +/client/proc/view_asays() + set name = "Asays" + set desc = "View Asays from the current round." + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + var/list/output = list({" + + Refresh +

    NameFingerprints
    + "}) + + // Header & body start + output += {" + + + + + + + + + "} + + for(var/datum/asays/A in GLOB.asays) + var/timestr = time2text(A.time, "hh:mm:ss") + output += {" + + + + + + "} + + output += {" + +
    TimeCkeyMessage
    [timestr][A.ckey] ([A.rank])[A.message]
    "} + + var/datum/browser/popup = new(src, "asays", "
    Current Round Asays
    ", 1200, 825) + popup.set_content(output.Join()) + popup.open(0) diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index b3d1543aedc..1284f9e0faf 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -3,8 +3,7 @@ set category = "Event" set name = "Change Custom Event" - if(!holder) - to_chat(src, "Only administrators may use this command.") + if(!check_rights(R_EVENT)) return var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", GLOB.custom_event_msg) as message|null diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 03b9dfb60e7..d2c05ff4f6c 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -168,7 +168,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) #endif /client/proc/callproc_datum(var/A as null|area|mob|obj|turf) - set category = "Debug" + set category = null set name = "Atom ProcCall" if(!check_rights(R_PROCCALL)) @@ -418,25 +418,6 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) else alert("Invalid mob") -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all() - set category = "Debug" - set name = "Del-All" - - if(!check_rights(R_DEBUG)) - return - - // to prevent REALLY stupid deletions - var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) - var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked - if(hsbitem) - for(var/atom/O in world) - if(istype(O, hsbitem)) - qdel(O) - log_admin("[key_name(src)] has deleted all instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/proc/cmd_debug_del_sing() set category = "Debug" set name = "Del Singulo / Tesla" diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 78f2647e429..2ffb4659486 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -108,7 +108,7 @@ /client/proc/reload_admins() set name = "Reload Admins" - set category = "Debug" + set category = "Server" if(!check_rights(R_SERVER)) return diff --git a/code/modules/admin/verbs/gimmick_team.dm b/code/modules/admin/verbs/gimmick_team.dm index 37afe35ef6a..3403a4417cf 100644 --- a/code/modules/admin/verbs/gimmick_team.dm +++ b/code/modules/admin/verbs/gimmick_team.dm @@ -39,6 +39,7 @@ if(alert("Do you want these characters automatically classified as antagonists?",,"Yes","No")=="Yes") is_syndicate = 1 + var/datum/outfit/O = outfit_list[dresscode] var/list/players_to_spawn = list() if(pick_manually) var/list/possible_ghosts = list() @@ -52,14 +53,12 @@ players_to_spawn += candidate else to_chat(src, "Polling candidates...") - players_to_spawn = pollCandidates("Do you want to play as an event character?") + players_to_spawn = SSghost_spawns.poll_candidates("Do you want to play as \a [O.name]?") if(!players_to_spawn.len) to_chat(src, "Nobody volunteered.") return 0 - var/datum/outfit/O = outfit_list[dresscode] - var/players_spawned = 0 for(var/mob/thisplayer in players_to_spawn) var/mob/living/carbon/human/H = new /mob/living/carbon/human(T) diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm index 54ddf85045e..d4ea1a0dd2e 100644 --- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm +++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm @@ -55,7 +55,8 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0) infiltrators += candidate else to_chat(src, "Polling candidates...") - infiltrators = pollCandidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, 1) + var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate") + infiltrators = SSghost_spawns.poll_candidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, TRUE, source = ma) if(!infiltrators.len) to_chat(src, "Nobody volunteered.") diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index edbca5a842f..0dc3dfc1791 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -2,8 +2,9 @@ set category = "Debug" set name = "Map template - Place" - if(!holder) + if(!check_rights(R_DEBUG)) return + var/datum/map_template/template var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in GLOB.map_templates @@ -36,6 +37,9 @@ set category = "Debug" set name = "Map Template - Upload" + if(!check_rights(R_DEBUG)) + return + var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file if(!map) return diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index 3e89a6ac18d..5d9d218c90e 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -137,7 +137,8 @@ client/proc/one_click_antag() var/confirm = alert("Are you sure?", "Confirm creation", "Yes", "No") if(confirm != "Yes") return 0 - var/list/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard") + var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "wizard") + var/list/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", source = ma) log_admin("[key_name(owner)] tried making a Wizard with One-Click-Antag") message_admins("[key_name_admin(owner)] tried making a Wizard with One-Click-Antag") diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm index 5cf4c34ffe2..3c1d0820dd7 100644 --- a/code/modules/admin/verbs/possess.dm +++ b/code/modules/admin/verbs/possess.dm @@ -2,6 +2,9 @@ set name = "Possess Obj" set category = null + if(!check_rights(R_POSSESS)) + return + if(istype(O,/obj/singularity)) if(config.forbid_singulo_possession) to_chat(usr, "It is forbidden to possess singularities.") @@ -35,6 +38,9 @@ set category = null //usr.loc = get_turf(usr) + if(!check_rights(R_POSSESS)) + return + if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object usr.real_name = usr.name_archive usr.name = usr.real_name diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 7e9f397a855..3864f573597 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -51,7 +51,7 @@ if(!ismob(M)) return - if(!check_rights(R_SERVER|R_EVENT)) + if(!check_rights(R_EVENT)) return var/msg = clean_input("Message:", text("Subtle PM to [M.key]")) @@ -123,7 +123,7 @@ feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE - set category = "Event" + set category = null set name = "Direct Narrate" if(!check_rights(R_SERVER|R_EVENT)) @@ -158,7 +158,7 @@ /client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) var/mob/living/carbon/human/H = M - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return if(!istype(H)) @@ -572,7 +572,7 @@ Traitors and the like can also be revived with the previous role mostly intact. feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list) - set category = "Event" + set category = null set name = "Rejuvenate" if(!check_rights(R_REJUVINATE)) @@ -638,7 +638,7 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) - set category = "Admin" + set category = null set name = "Delete" if(!check_rights(R_ADMIN)) @@ -1078,12 +1078,12 @@ Traitors and the like can also be revived with the previous role mostly intact. message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1) /client/proc/show_tip() - set category = "Admin" + set category = "Event" set name = "Show Custom Tip" set desc = "Sends a tip (that you specify) to all players. After all \ you're the experienced player here." - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null diff --git a/code/modules/admin/verbs/spawnfloorcluwne.dm b/code/modules/admin/verbs/spawnfloorcluwne.dm deleted file mode 100644 index 37fd3057af4..00000000000 --- a/code/modules/admin/verbs/spawnfloorcluwne.dm +++ /dev/null @@ -1,30 +0,0 @@ -/client/proc/spawn_floor_cluwne() - set category = "Event" - set name = "Unleash Floor Cluwne" - set desc = "Pick a specific target or just let it select randomly and spawn the floor cluwne mob on the station. Be warned: spawning more than one may cause issues!" - var/mob/living/carbon/human/target - - if(!check_rights(R_EVENT)) - return - - var/confirm = alert("Are you sure you want to release a floor cluwne and kill a lot of people?", "Confirm Massacre", "Yes", "No") - if(confirm == "Yes") - - var/turf/T = get_turf(usr) - var/list/potential_targets = list() - for(var/mob/M in GLOB.player_list) - var/mob/living/carbon/human/H = M - if(!istype(H)) - continue - if(H.mind.assigned_role == "Cluwne") - continue - potential_targets += H - if(!potential_targets.len) //You're probably the only player on this damn station, spawn it yourself - to_chat(src, "No valid targets!") - return - target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in potential_targets - var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) - if(target) - FC.Acquire_Victim(target) - log_admin("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].") - message_admins("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].") diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 0b84c96e71a..87649fe3d4e 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -37,7 +37,8 @@ GLOBAL_VAR_INIT(sent_strike_team, 0) break // Find ghosts willing to be DS - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_deathsquad") + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE, source = source) if(!commando_ghosts.len) to_chat(usr, "Nobody volunteered to join the DeathSquad.") return @@ -163,6 +164,7 @@ GLOBAL_VAR_INIT(sent_strike_team, 0) R.set_frequency(DTH_FREQ) R.requires_tcomms = FALSE R.instant = TRUE + R.freqlock = TRUE equip_to_slot_or_del(R, slot_l_ear) if(is_leader) equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index 12df7467cbc..405c1d3c3d2 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -45,7 +45,7 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0) break // Find ghosts willing to be SST - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) if(!commando_ghosts.len) to_chat(usr, "Nobody volunteered to join the SST.") return 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/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 4a6fe7d6640..41333760f2d 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(antagonists) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [name]?", job_rank, TRUE, 50) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 8e29326632f..82f492d214b 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -46,7 +46,8 @@ checking = TRUE to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollCandidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 150) + var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate_space_sword") + var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = ma) if(LAZYLEN(nuke_candidates)) checking = FALSE if(QDELETED(src) || !check_usability(user)) @@ -180,7 +181,7 @@ var/type = "slaughter" if(demon_type == /mob/living/simple_animal/slaughter/laughter) type = "laughter" - var/list/candidates = pollCandidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, 1, 100) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, TRUE, 10 SECONDS, source = demon_type) if(candidates.len > 0) var/mob/C = pick(candidates) @@ -252,7 +253,7 @@ used = TRUE to_chat(user, "You break the seal on the bottle, calling upon the dire sludge to awaken...") - var/list/candidates = pollCandidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 100) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 10 SECONDS, source = morph_type) if(candidates.len > 0) var/mob/C = pick(candidates) 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/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm index 7cddddd8b08..65c64edd83f 100644 --- a/code/modules/awaymissions/mission_code/academy.dm +++ b/code/modules/awaymissions/mission_code/academy.dm @@ -193,7 +193,7 @@ servant_mind.objectives += O servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 300) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm index c5b56805213..75fbbf698e6 100644 --- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm +++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm @@ -4,10 +4,18 @@ name = "Mission Briefing" info = "To the Magnificent Z.A.P.
    A small mining base has been created within our territory by wandless scum. Send them a message from the wizard federation they will not forget. I know your kind is rather fragile, but a group of lightly armed miners should not pose any threat to you at all. Just be warned they have a security cyborg for self defence, you might want to tune your spells to that threat. I look forward to hearing of your success.
    Grand Magus Abra the Wonderous" +/obj/item/spellbook/oneuse/emp + spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + spellname = "Disable Technology" + icon_state = "bookcharge" //it's a lightning bolt, seems appropriate enough + desc = "For the tech-hating wizard on the go." + +/obj/item/spellbook/oneuse/emp/used + used = TRUE //spawns used /obj/effect/spawner/lootdrop/wizardcrash loot = list( - /obj/item/guardiancreator = 1, //jackpot. + /obj/item/guardiancreator = 1, //jackpot. /obj/item/spellbook/oneuse/knock = 1, //tresspassing charges incoming /obj/item/gun/magic/wand/resurrection = 1, //medbay's best friend /obj/item/spellbook/oneuse/charge = 20, //and now for less useful stuff to dilute the good loot chances diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 8ca8bf0fbb1..8d8d83b4ae2 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -309,6 +309,20 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/chem_master/send(client) send_asset_list(client, assets, verify) +//Cloning pod sprites for UIs +/datum/asset/cloning + var/assets = list() + var/verify = FALSE + +/datum/asset/cloning/register() + assets["pod_idle.gif"] = icon('icons/obj/cloning.dmi', "pod_idle") + assets["pod_cloning.gif"] = icon('icons/obj/cloning.dmi', "pod_cloning") + assets["pod_mess.gif"] = icon('icons/obj/cloning.dmi', "pod_mess") + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) + +/datum/asset/cloning/send(client) + send_asset_list(client, assets, verify) //Pipe sprites for UIs /datum/asset/rpd 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/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index 5534e18e253..0f4317e3ce2 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -30,7 +30,8 @@ set name = "Show/Hide RadioChatter" set category = "Preferences" set desc = "Toggle seeing radiochatter from radios and speakers" - if(!holder) return + if(!check_rights(R_ADMIN)) + return prefs.toggles ^= CHAT_RADIO prefs.save_preferences(src) to_chat(usr, "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers") @@ -49,7 +50,8 @@ set name = "Hear/Silence Admin Bwoinks" set category = "Preferences" set desc = "Toggle hearing a notification when admin PMs are recieved" - if(!holder) return + if(!check_rights(R_ADMIN)) + return prefs.sound ^= SOUND_ADMINHELP prefs.save_preferences(src) to_chat(usr, "You will [(prefs.sound & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive.") @@ -59,7 +61,7 @@ set name = "Hear/Silence Mentorhelp Bwoinks" set category = "Preferences" set desc = "Toggle hearing a notification when mentorhelps are recieved" - if(!holder) + if(!check_rights(R_ADMIN|R_MENTOR)) return prefs.sound ^= SOUND_MENTORHELP prefs.save_preferences(src) @@ -296,7 +298,7 @@ to_chat(src, "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].") prefs.save_preferences(src) feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - + /client/verb/silence_current_midi() set name = "Silence Current Midi" set category = "Preferences" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index bfa43b92fa3..b1efd1f0b78 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -32,6 +32,7 @@ var/cooldown = 0 var/species_disguise = null var/magical = FALSE + w_class = WEIGHT_CLASS_SMALL /obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. if(!can_use(user)) @@ -590,6 +591,7 @@ BLIND // can't see anything name = "Space helmet" icon_state = "space" desc = "A special helmet designed for work in a hazardous, low-pressure environment." + w_class = WEIGHT_CLASS_NORMAL flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH item_state = "s_helmet" 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/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 2a9abf461c9..05bd3d6f48d 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -2,6 +2,7 @@ name = "helmet" desc = "Standard Security gear. Protects the head from impacts." icon_state = "helmetmaterials" + w_class = WEIGHT_CLASS_NORMAL flags = HEADBANGPROTECT flags_cover = HEADCOVERSEYES item_state = "helmetmaterials" diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 962cfed80c5..1f8cb7aaed1 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -6,6 +6,7 @@ /obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses name = "combat boots" desc = "High speed, low drag combat boots." + w_class = WEIGHT_CLASS_NORMAL can_cut_open = 1 icon_state = "jackboots" item_state = "jackboots" diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 6c4ace8c993..bdcd27c24e3 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -11,8 +11,10 @@ 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 /obj/item/clothing/suit/armor/vest name = "armor" 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/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 7e9e698d185..63f5cf8dca3 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -12,6 +12,7 @@ /obj/item/clothing/suit/bluetag name = "blue laser tag armour" desc = "Blue Pride, Station Wide." + w_class = WEIGHT_CLASS_NORMAL icon_state = "bluetag" item_state = "bluetag" blood_overlay_type = "armor" @@ -26,6 +27,7 @@ /obj/item/clothing/suit/redtag name = "red laser tag armour" desc = "Pew pew pew." + w_class = WEIGHT_CLASS_NORMAL icon_state = "redtag" item_state = "redtag" blood_overlay_type = "armor" @@ -71,6 +73,7 @@ /obj/item/clothing/suit/cyborg_suit name = "cyborg suit" desc = "Suit for a cyborg costume." + w_class = WEIGHT_CLASS_NORMAL icon_state = "death" item_state = "death" flags = CONDUCT @@ -371,6 +374,7 @@ /obj/item/clothing/suit/hooded/wintercoat/captain name = "captain's winter coat" icon_state = "wintercoat_captain" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatcaptain" armor = list("melee" = 25, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic) @@ -382,6 +386,7 @@ /obj/item/clothing/suit/hooded/wintercoat/security name = "security winter coat" icon_state = "wintercoat_sec" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatsecurity" armor = list("melee" = 15, "bullet" = 10, "laser" = 15, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic) @@ -393,6 +398,7 @@ /obj/item/clothing/suit/hooded/wintercoat/medical name = "medical winter coat" icon_state = "wintercoat_med" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatmedical" allowed = list(/obj/item/analyzer, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45) @@ -404,6 +410,7 @@ /obj/item/clothing/suit/hooded/wintercoat/science name = "science winter coat" icon_state = "wintercoat_sci" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatscience" allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 0, acid = 0) @@ -415,6 +422,7 @@ /obj/item/clothing/suit/hooded/wintercoat/engineering name = "engineering winter coat" icon_state = "wintercoat_engi" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatengineer" armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 45) allowed = list(/obj/item/flashlight, /obj/item/tank/emergency_oxygen, /obj/item/t_scanner, /obj/item/rcd) @@ -454,6 +462,7 @@ /obj/item/clothing/suit/hooded/wintercoat/miner name = "mining winter coat" icon_state = "wintercoat_miner" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatminer" allowed = list(/obj/item/pickaxe, /obj/item/flashlight, /obj/item/tank/emergency_oxygen, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter) armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) @@ -767,6 +776,7 @@ /obj/item/clothing/suit/jacket/pilot name = "security bomber jacket" desc = "A stylish and worn-in armoured black bomber jacket emblazoned with the NT Security crest on the left breast. Looks rugged." + w_class = WEIGHT_CLASS_NORMAL icon_state = "bombersec" item_state = "bombersec" ignore_suitadjust = 0 @@ -825,6 +835,7 @@ /obj/item/clothing/suit/toggle/owlwings name = "owl cloak" desc = "A soft brown cloak made of synthetic feathers. Soft to the touch, stylish, and a 2 meter wing span that will drive the ladies mad." + w_class = WEIGHT_CLASS_NORMAL icon_state = "owl_wings" item_state = "owl_wings" body_parts_covered = ARMS @@ -871,6 +882,7 @@ /obj/item/clothing/suit/advanced_protective_suit name = "Advanced Protective Suit" desc = "An incredibly advanced and complex suit; it has so many buttons and dials as to be incomprehensible." + w_class = WEIGHT_CLASS_BULKY icon_state = "bomb" item_state = "bomb" actions_types = list(/datum/action/item_action/toggle) @@ -924,6 +936,7 @@ //Syndicate Chaplain Robe (WOLOLO!) /obj/item/clothing/suit/hooded/chaplain_hoodie/missionary_robe description_antag = "This robe is made of reinforced fibers, granting it superior protection. The robes also wirelessly generate power for the neurotransmitter in the linked missionary staff while being worn." + w_class = WEIGHT_CLASS_NORMAL armor = list(melee = 10, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 15, fire = 30, acid = 30) var/obj/item/nullrod/missionary_staff/linked_staff = null diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm index 9a675742b5e..1da4138fe77 100644 --- a/code/modules/clothing/suits/storage.dm +++ b/code/modules/clothing/suits/storage.dm @@ -1,5 +1,6 @@ /obj/item/clothing/suit/storage var/obj/item/storage/internal/pockets + w_class = WEIGHT_CLASS_NORMAL //we don't want these to be able to fit in their own pockets. /obj/item/clothing/suit/storage/New() ..() diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 308380e4c55..e77bcb7478a 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -64,6 +64,7 @@ /obj/item/clothing/head/bomb_hood name = "bomb hood" desc = "Use in case of bomb." + w_class = WEIGHT_CLASS_NORMAL icon_state = "bombsuit" flags = BLOCKHAIR | THICKMATERIAL armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index a9b4eab329e..9a2afbc8a60 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -838,3 +838,10 @@ icon_state = "cuban_suit" item_state = "cuban_suit" item_color = "cuban_suit" + +/obj/item/clothing/under/tourist_suit + name = "tourist outfit" + desc = "A light blue shirt with brown shorts. Feels oddly spooky." + icon_state = "tourist" + icon_state = "tourist" + item_color = "tourist" 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/abductor.dm b/code/modules/events/abductor.dm index 7fe60e8b77f..4ad8fb4eb90 100644 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -9,7 +9,7 @@ makeAbductorTeam() /datum/event/abductor/proc/makeAbductorTeam() - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, 1) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, TRUE) if(candidates.len >= 2) //Oh god why we can't have static functions diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index 8c01d5c7cc2..23e4e266819 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -23,7 +23,7 @@ vents += temp_vent spawn() - var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, TRUE, source = /mob/living/carbon/alien/larva) while(spawncount > 0 && vents.len && candidates.len) var/obj/vent = pick_n_take(vents) 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/blob.dm b/code/modules/events/blob.dm index caec11de9f2..de2da9fbf64 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -12,14 +12,14 @@ if(!T) return kill() - var/list/candidates = pollCandidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, 1) - if(!candidates.len) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/simple_animal/mouse/blobinfected) + if(!length(candidates)) return kill() var/list/vents = list() for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in GLOB.all_vent_pumps) if(is_station_level(temp_vent.loc.z) && !temp_vent.welded) - if(temp_vent.parent.other_atmosmch.len > 50) + if(length(temp_vent.parent.other_atmosmch) > 50) vents += temp_vent var/obj/vent = pick(vents) 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/events/event_procs.dm b/code/modules/events/event_procs.dm index c5b7c893c26..cc00f05723f 100644 --- a/code/modules/events/event_procs.dm +++ b/code/modules/events/event_procs.dm @@ -1,9 +1,9 @@ /client/proc/forceEvent(var/type in SSevents.allEvents) - set name = "Trigger Event (Debug Only)" + set name = "Trigger Event" set category = "Debug" - if(!holder) + if(!check_rights(R_EVENT)) return if(ispath(type)) diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index ab8a405785f..7007a99f0ec 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -3,8 +3,7 @@ /datum/event/sentience/start() processing = FALSE //so it won't fire again in next tick - var/ghostmsg = "Do you want to awaken as a sentient being?" - var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to awaken as a sentient being?", ROLE_SENTIENT, TRUE) var/list/potential = list() var/sentience_type = SENTIENCE_ORGANIC diff --git a/code/modules/events/slaughterevent.dm b/code/modules/events/slaughterevent.dm index 91918894cfd..85f0f8aa555 100644 --- a/code/modules/events/slaughterevent.dm +++ b/code/modules/events/slaughterevent.dm @@ -3,7 +3,7 @@ /datum/event/spawn_slaughter/proc/get_slaughter(var/end_if_fail = 0) spawn() - var/list/candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, source = /mob/living/simple_animal/slaughter) if(!candidates.len) key_of_slaughter = null return kill() diff --git a/code/modules/events/traders.dm b/code/modules/events/traders.dm index 0beeb3691e2..d4ae1de3b9c 100644 --- a/code/modules/events/traders.dm +++ b/code/modules/events/traders.dm @@ -34,7 +34,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol")) trader_objectives = forge_trader_objectives() spawn() - var/list/candidates = pollCandidates("Do you want to play as a trader?", ROLE_TRADER, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a Sol Trader?", ROLE_TRADER, TRUE) var/index = 1 while(spawn_count > 0 && candidates.len > 0) if(index > spawnlocs.len) diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm index 5f13f5c7a06..8313b967722 100644 --- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm +++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm @@ -13,6 +13,10 @@ max_integrity = 20 resistance_flags = ACID_PROOF +/obj/item/reagent_containers/food/drinks/set_APTFT() + set hidden = FALSE + ..() + /obj/item/reagent_containers/food/drinks/drinkingglass/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/reagent_containers/food/snacks/egg)) //breaking eggs var/obj/item/reagent_containers/food/snacks/egg/E = I 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.dm b/code/modules/food_and_drinks/food.dm index 2180f9a0f63..15e28cc6c62 100644 --- a/code/modules/food_and_drinks/food.dm +++ b/code/modules/food_and_drinks/food.dm @@ -35,6 +35,7 @@ /obj/item/reagent_containers/food/set_APTFT() set hidden = TRUE + ..() /obj/item/reagent_containers/food/proc/check_for_ants() if(!antable) diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index bdeadf2a72c..733ac4692a3 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -29,6 +29,10 @@ /obj/item/reagent_containers/food/condiment/attack_self(mob/user) return +/obj/item/reagent_containers/food/condiment/set_APTFT() + set hidden = FALSE + ..() + /obj/item/reagent_containers/food/condiment/attack(mob/M, mob/user, def_zone) if(!reagents || !reagents.total_volume) 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 346b78e5b26..f6bb3c8e1c4 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -1,206 +1,81 @@ -/* SmartFridge. Much todo -*/ +#define SMART_FRIDGE_LOCK_SHORTED -1 + +/** + * # Smart Fridge + * + * Stores items of a specified type. + */ /obj/machinery/smartfridge name = "\improper SmartFridge" icon = 'icons/obj/vending.dmi' icon_state = "smartfridge" layer = 2.9 - density = 1 - anchored = 1 + density = TRUE + anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 100 + /// The maximum number of items the fridge can hold. Multiplicated by the matter bin component's rating. var/max_n_of_items = 1500 - var/item_quants = list() + /// Associative list (/text => /number) tracking the amounts of a specific item held by the fridge. + var/list/item_quants + /// How long in ticks the fridge is electrified for. Decrements every process. var/seconds_electrified = 0 + /// Whether the fridge should randomly shoot held items at a nearby living target or not. var/shoot_inventory = FALSE + /// Whether the fridge is locked. Used for the secure variant of the fridge. var/locked = FALSE + /// Whether the fridge requires ID scanning. Used for the secure variant of the fridge. var/scan_id = TRUE + /// Whether the fridge is considered secure. Used for wiring and display. var/is_secure = FALSE + /// Whether the fridge can dry its' contents. Used for display. var/can_dry = FALSE + /// Whether the fridge is currently drying. Used by [drying racks][/obj/machinery/smartfridge/drying_rack]. var/drying = FALSE + /// Whether the fridge's contents are visible on the world icon. var/visible_contents = TRUE - var/datum/wires/smartfridge/wires = null + /// The wires controlling the fridge. + var/datum/wires/smartfridge/wires + /// Typecache of accepted item types, init it in [/obj/machinery/smartfridge/Initialize]. + var/list/accepted_items_typecache -/obj/machinery/smartfridge/New() - ..() +/obj/machinery/smartfridge/Initialize(mapload) + . = ..() + item_quants = list() + // Reagents create_reagents() reagents.set_reacting(FALSE) + // Components component_parts = list() var/obj/item/circuitboard/smartfridge/board = new(null) board.set_type(type) component_parts += board component_parts += new /obj/item/stock_parts/matter_bin(null) RefreshParts() + // Wires + if(is_secure) + wires = new/datum/wires/smartfridge/secure(src) + else + wires = new/datum/wires/smartfridge(src) + // Accepted items + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/food/snacks/grown, + /obj/item/seeds, + /obj/item/grown, + )) /obj/machinery/smartfridge/RefreshParts() for(var/obj/item/stock_parts/matter_bin/B in component_parts) max_n_of_items = 1500 * B.rating -/obj/machinery/smartfridge/secure - is_secure = 1 - -/obj/machinery/smartfridge/New() - ..() - if(is_secure) - wires = new/datum/wires/smartfridge/secure(src) - else - wires = new/datum/wires/smartfridge(src) - /obj/machinery/smartfridge/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) for(var/atom/movable/A in contents) A.forceMove(loc) return ..() -/obj/machinery/smartfridge/proc/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/) || istype(O,/obj/item/grown/)) - return 1 - return 0 - -/obj/machinery/smartfridge/seeds - name = "\improper MegaSeed Servitor" - desc = "When you need seeds fast!" - icon = 'icons/obj/vending.dmi' - icon_state = "seeds" - -/obj/machinery/smartfridge/seeds/accept_check(obj/item/O) - if(istype(O,/obj/item/seeds/)) - return 1 - return 0 - -/obj/machinery/smartfridge/medbay - name = "\improper Refrigerated Medicine Storage" - desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_state = "smartfridge" //To fix the icon in the map editor. - -/obj/machinery/smartfridge/medbay/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/glass)) - return 1 - if(istype(O,/obj/item/reagent_containers/iv_bag)) - return 1 - if(istype(O,/obj/item/storage/pill_bottle)) - return 1 - if(ispill(O)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/extract - name = "\improper Slime Extract Storage" - desc = "A refrigerated storage unit for slime extracts" - req_access_txt = "47" - -/obj/machinery/smartfridge/secure/extract/accept_check(obj/item/O) - if(istype(O,/obj/item/slime_extract)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/medbay - name = "\improper Secure Refrigerated Medicine Storage" - desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_state = "smartfridge" //To fix the icon in the map editor. - req_one_access_txt = "5;33" - -/obj/machinery/smartfridge/secure/medbay/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/glass)) - return 1 - if(istype(O,/obj/item/reagent_containers/iv_bag)) - return 1 - if(istype(O,/obj/item/storage/pill_bottle)) - return 1 - if(ispill(O)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/chemistry - name = "\improper Smart Chemical Storage" - desc = "A refrigerated storage unit for medicine and chemical storage." - icon_state = "smartfridge" //To fix the icon in the map editor. - req_access_txt = "33" - var/list/spawn_meds = list() - -/obj/machinery/smartfridge/secure/chemistry/New() - ..() - for(var/typekey in spawn_meds) - var/amount = spawn_meds[typekey] - if(isnull(amount)) amount = 1 - while(amount) - var/obj/item/I = new typekey(src) - if(item_quants[I.name]) - item_quants[I.name]++ - else - item_quants[I.name] = 1 - SSnanoui.update_uis(src) - amount-- - update_icon() - -/obj/machinery/smartfridge/secure/chemistry/accept_check(obj/item/O) - if(istype(O,/obj/item/storage/pill_bottle) || istype(O,/obj/item/reagent_containers)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/chemistry/preloaded - spawn_meds = list( - /obj/item/reagent_containers/food/pill/epinephrine = 12, - /obj/item/reagent_containers/food/pill/charcoal = 5, - /obj/item/reagent_containers/glass/bottle/epinephrine = 1, - /obj/item/reagent_containers/glass/bottle/charcoal = 1) - -/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate - req_access_txt = null - req_access = list(ACCESS_SYNDICATE) - -/obj/machinery/smartfridge/disks - name = "disk compartmentalizer" - desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)." - icon_state = "disktoaster" - pass_flags = PASSTABLE - visible_contents = FALSE - -/obj/machinery/smartfridge/disks/accept_check(obj/item/O) - return istype(O, /obj/item/disk) - -// ---------------------------- -// Virology Medical Smartfridge -// ---------------------------- -/obj/machinery/smartfridge/secure/chemistry/virology - name = "Smart Virus Storage" - desc = "A refrigerated storage unit for volatile sample storage." - req_access_txt = "39" - spawn_meds = list(/obj/item/reagent_containers/syringe/antiviral = 4, - /obj/item/reagent_containers/glass/bottle/cold = 1, - /obj/item/reagent_containers/glass/bottle/flu_virion = 1, - /obj/item/reagent_containers/glass/bottle/mutagen = 1, - /obj/item/reagent_containers/glass/bottle/plasma = 1, - /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1) - -/obj/machinery/smartfridge/secure/chemistry/virology/accept_check(obj/item/O) - if(istype(O, /obj/item/reagent_containers/syringe) || istype(O, /obj/item/reagent_containers/glass/bottle) || istype(O, /obj/item/reagent_containers/glass/beaker)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/chemistry/virology/preloaded - spawn_meds = list( - /obj/item/reagent_containers/syringe/antiviral = 4, - /obj/item/reagent_containers/glass/bottle/cold = 1, - /obj/item/reagent_containers/glass/bottle/flu_virion = 1, - /obj/item/reagent_containers/glass/bottle/mutagen = 1, - /obj/item/reagent_containers/glass/bottle/plasma = 1, - /obj/item/reagent_containers/glass/bottle/reagent/synaptizine = 1, - /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde = 1) - -/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate - req_access_txt = null - req_access = list(ACCESS_SYNDICATE) - -/obj/machinery/smartfridge/drinks - name = "\improper Drink Showcase" - desc = "A refrigerated storage unit for tasty tasty alcohol." - -/obj/machinery/smartfridge/drinks/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/glass) || istype(O,/obj/item/reagent_containers/food/drinks) || istype(O,/obj/item/reagent_containers/food/condiment)) - return 1 - /obj/machinery/smartfridge/process() if(stat & (BROKEN|NOPOWER)) return @@ -216,52 +91,57 @@ update_icon() /obj/machinery/smartfridge/update_icon() + var/prefix = initial(icon_state) if(stat & (BROKEN|NOPOWER)) - icon_state = "[initial(icon_state)]-off" + icon_state = "[prefix]-off" else if(visible_contents) - switch(contents.len) + switch(length(contents)) if(0) - icon_state = "[initial(icon_state)]" + icon_state = "[prefix]" if(1 to 25) - icon_state = "[initial(icon_state)]1" + icon_state = "[prefix]1" if(26 to 75) - icon_state = "[initial(icon_state)]2" + icon_state = "[prefix]2" if(76 to INFINITY) - icon_state = "[initial(icon_state)]3" + icon_state = "[prefix]3" else - icon_state = "[initial(icon_state)]" + icon_state = "[prefix]" -/******************* -* Item Adding -********************/ - -/obj/machinery/smartfridge/default_deconstruction_screwdriver(mob/user, obj/item/screwdriver/S) - . = ..(user, icon_state, icon_state, S) +// Interactions +/obj/machinery/smartfridge/screwdriver_act(mob/living/user, obj/item/I) + . = default_deconstruction_screwdriver(user, icon_state, icon_state, I) + if(!.) + return overlays.Cut() if(panel_open) overlays += image(icon, "[initial(icon_state)]-panel") -/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user) - if(default_deconstruction_screwdriver(user, O)) - return - - if(exchange_parts(user, O)) - return - - if(default_unfasten_wrench(user, O)) +/obj/machinery/smartfridge/wrench_act(mob/living/user, obj/item/I) + . = default_unfasten_wrench(user, I) + if(.) power_change() - return - if(default_deconstruction_crowbar(user, O)) - return +/obj/machinery/smartfridge/crowbar_act(mob/living/user, obj/item/I) + . = default_deconstruction_crowbar(user, I) - if(istype(O, /obj/item/multitool)||istype(O, /obj/item/wirecutters)) - if(panel_open) - attack_hand(user) - return +/obj/machinery/smartfridge/wirecutter_act(mob/living/user, obj/item/I) + if(panel_open) + attack_hand(user) + return TRUE + return ..() - if(stat & NOPOWER) +/obj/machinery/smartfridge/multitool_act(mob/living/user, obj/item/I) + if(panel_open) + attack_hand(user) + return TRUE + return ..() + +/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user) + if(exchange_parts(user, O)) + SSnanoui.update_uis(src) + return + if(stat & (BROKEN|NOPOWER)) to_chat(user, "\The [src] is unpowered and useless.") return @@ -269,92 +149,63 @@ user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") SSnanoui.update_uis(src) update_icon() - else if(istype(O, /obj/item/storage/bag)) var/obj/item/storage/bag/P = O - var/plants_loaded = 0 + var/items_loaded = 0 for(var/obj/G in P.contents) if(load(G, user)) - plants_loaded++ - if(plants_loaded) + items_loaded++ + if(items_loaded) user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].") - if(P.contents.len > 0) - to_chat(user, "Some items are refused.") - - SSnanoui.update_uis(src) - update_icon() - + SSnanoui.update_uis(src) + update_icon() + var/failed = length(P.contents) + if(failed) + to_chat(user, "[failed] item\s [failed == 1 ? "is" : "are"] refused.") else if(!istype(O, /obj/item/card/emag)) to_chat(user, "\The [src] smartly refuses [O].") - return 1 - -/obj/machinery/smartfridge/proc/load(obj/I, mob/user) - if(accept_check(I)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 0 - else - if(istype(I.loc, /obj/item/storage)) - var/obj/item/storage/S = I.loc - S.remove_from_storage(I, src) - else if(istype(I.loc, /mob)) - var/mob/M = I.loc - if(M.get_active_hand() == I) - if(!M.drop_item()) - to_chat(user, "\The [I] is stuck to you!") - return 0 - else - M.unEquip(I) - I.forceMove(src) - else - I.forceMove(src) - - if(item_quants[I.name]) - item_quants[I.name]++ - else - item_quants[I.name] = 1 - return 1 - return 0 + return TRUE /obj/machinery/smartfridge/attack_ai(mob/user) - return 0 + return FALSE /obj/machinery/smartfridge/attack_ghost(mob/user) return attack_hand(user) /obj/machinery/smartfridge/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) + if(stat & (BROKEN|NOPOWER)) return wires.Interact(user) ui_interact(user) + return ..() //Drag pill bottle to fridge to empty it into the fridge /obj/machinery/smartfridge/MouseDrop_T(obj/over_object, mob/user) if(!istype(over_object, /obj/item/storage/pill_bottle)) //Only pill bottles, please return - - if(stat & NOPOWER) + if(stat & (BROKEN|NOPOWER)) to_chat(user, "\The [src] is unpowered and useless.") return var/obj/item/storage/box/pillbottles/P = over_object + if(!length(P.contents)) + to_chat(user, "\The [P] is empty.") + return + var/items_loaded = 0 for(var/obj/G in P.contents) if(load(G, user)) items_loaded++ if(items_loaded) - user.visible_message( \ - "[user] empties \the [P] into \the [src].", \ - "You empty \the [P] into \the [src].") - if(P.contents.len > 0) - to_chat(user, "Some items are refused.") - SSnanoui.update_uis(src) + user.visible_message("[user] empties \the [P] into \the [src].", "You empty \the [P] into \the [src].") + SSnanoui.update_uis(src) + update_icon() + var/failed = length(P.contents) + if(failed) + to_chat(user, "[failed] item\s [failed == 1 ? "is" : "are"] refused.") -/******************* -* SmartFridge Menu -********************/ - -/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) +// UI +/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE) user.set_machine(src) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) @@ -374,13 +225,13 @@ data["drying"] = drying var/list/items[0] - for(var/i=1 to length(item_quants)) + for(var/i in 1 to length(item_quants)) var/K = item_quants[i] var/count = item_quants[K] if(count > 0) items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count))) - if(items.len > 0) + if(length(items)) data["contents"] = items return data @@ -402,6 +253,8 @@ if(href_list["vend"]) var/index = text2num(href_list["vend"]) var/amount = text2num(href_list["amount"]) + if(isnull(index) || !ISINDEXSAFE(item_quants, index) || isnull(amount)) + return FALSE var/K = item_quants[index] var/count = item_quants[K] @@ -429,36 +282,343 @@ if(i <= 0) return TRUE return TRUE + return FALSE +/** + * Tries to load an item if it is accepted by [/obj/machinery/smartfridge/proc/accept_check]. + * + * Arguments: + * * I - The item to load. + * * user - The user trying to load the item. + */ +/obj/machinery/smartfridge/proc/load(obj/I, mob/user) + if(accept_check(I)) + if(length(contents) >= max_n_of_items) + to_chat(user, "\The [src] is full.") + return FALSE + else + if(istype(I.loc, /obj/item/storage)) + var/obj/item/storage/S = I.loc + S.remove_from_storage(I, src) + else if(ismob(I.loc)) + var/mob/M = I.loc + if(M.get_active_hand() == I) + if(!M.drop_item()) + to_chat(user, "\The [I] is stuck to you!") + return FALSE + else + M.unEquip(I) + I.forceMove(src) + else + I.forceMove(src) + + item_quants[I.name] += 1 + return TRUE + return FALSE + +/** + * Tries to shoot a random at a nearby living mob. + */ /obj/machinery/smartfridge/proc/throw_item() - var/obj/throw_item = null - var/mob/living/target = locate() in view(7,src) + var/obj/item/throw_item = null + var/mob/living/target = locate() in view(7, src) if(!target) - return 0 + return FALSE for(var/O in item_quants) if(item_quants[O] <= 0) //Try to use a record that actually has something to dump. continue - item_quants[O]-- - for(var/obj/T in contents) - if(T.name == O) - T.forceMove(loc) - throw_item = T + for(var/obj/I in contents) + if(I.name == O) + I.forceMove(loc) + throw_item = I update_icon() break - break if(!throw_item) - return 0 - spawn(0) - throw_item.throw_at(target,16,3,src) - visible_message("[src] launches [throw_item.name] at [target.name]!") - return 1 + return FALSE -// ---------------------------- -// Drying Rack 'smartfridge' -// ---------------------------- + INVOKE_ASYNC(throw_item, /atom/movable.proc/throw_at, target, 16, 3, src) + visible_message("[src] launches [throw_item.name] at [target.name]!") + return TRUE + +/** + * Returns whether the smart fridge can accept the given item. + * + * By default checks if the item is in [the typecache][/obj/machinery/smartfridge/var/accepted_items_typecache]. + * Arguments: + * * O - The item to check. + */ +/obj/machinery/smartfridge/proc/accept_check(obj/item/O) + return is_type_in_typecache(O, accepted_items_typecache) + +/** + * # Secure Fridge + * + * Secure variant of the [Smart Fridge][/obj/machinery/smartfridge]. + * Can be emagged and EMP'd to short the lock. + */ +/obj/machinery/smartfridge/secure + is_secure = TRUE + +/obj/machinery/smartfridge/secure/emag_act(mob/user) + emagged = TRUE + locked = SMART_FRIDGE_LOCK_SHORTED + to_chat(user, "You short out the product lock on \the [src].") + +/obj/machinery/smartfridge/secure/emp_act(severity) + if(!emagged && locked != SMART_FRIDGE_LOCK_SHORTED && prob(40 / severity)) + playsound(loc, 'sound/effects/sparks4.ogg', 60, TRUE) + emagged = TRUE + locked = SMART_FRIDGE_LOCK_SHORTED + +/obj/machinery/smartfridge/secure/Topic(href, href_list) + if(stat & (BROKEN|NOPOWER)) + return FALSE + + if(href_list["vend"] && (usr.contents.Find(src) || Adjacent(usr))) + if(!emagged && locked != SMART_FRIDGE_LOCK_SHORTED && scan_id && !allowed(usr)) + to_chat(usr, "Access denied.") + SSnanoui.update_uis(src) + return FALSE + + return ..() + +/** + * # Seed Storage + * + * Seeds variant of the [Smart Fridge][/obj/machinery/smartfridge]. + * Formerly known as MegaSeed Servitor, but renamed to avoid confusion with the [vending machine][/obj/machinery/vending/hydroseeds]. + */ +/obj/machinery/smartfridge/seeds + name = "\improper Seed Storage" + desc = "When you need seeds fast!" + icon = 'icons/obj/vending.dmi' + icon_state = "seeds" + +/obj/machinery/smartfridge/seeds/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/seeds + )) + +/** + * # Refrigerated Medicine Storage + * + * Medical variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/medbay + name = "\improper Refrigerated Medicine Storage" + desc = "A refrigerated storage unit for storing medicine and chemicals." + icon_state = "smartfridge" //To fix the icon in the map editor. + +/obj/machinery/smartfridge/medbay/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/glass, + /obj/item/reagent_containers/iv_bag, + /obj/item/reagent_containers/applicator, + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers/food/pill, + )) + +/** + * # Slime Extract Storage + * + * Secure, Xenobiology variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/secure/extract + name = "\improper Slime Extract Storage" + desc = "A refrigerated storage unit for slime extracts" + +/obj/machinery/smartfridge/secure/extract/Initialize(mapload) + . = ..() + req_access_txt = "[ACCESS_RESEARCH]" + accepted_items_typecache = typecacheof(list( + /obj/item/slime_extract + )) + +/** + * # Secure Refrigerated Medicine Storage + * + * Secure, Medical variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/secure/medbay + name = "\improper Secure Refrigerated Medicine Storage" + desc = "A refrigerated storage unit for storing medicine and chemicals." + icon_state = "smartfridge" //To fix the icon in the map editor. + req_one_access_txt = "5;33" + +/obj/machinery/smartfridge/secure/medbay/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/glass, + /obj/item/reagent_containers/iv_bag, + /obj/item/reagent_containers/applicator, + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers/food/pill, + )) + +/** + * # Smart Chemical Storage + * + * Secure, Chemistry variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/secure/chemistry + name = "\improper Smart Chemical Storage" + desc = "A refrigerated storage unit for medicine and chemical storage." + icon_state = "smartfridge" //To fix the icon in the map editor. + /// Associative list (/obj/item => /number) representing the items the fridge should initially contain. + var/list/spawn_meds + +/obj/machinery/smartfridge/secure/chemistry/Initialize(mapload) + . = ..() + req_access_txt = "[ACCESS_CHEMISTRY]" + // Spawn initial chemicals + if(mapload) + LAZYINITLIST(spawn_meds) + for(var/typekey in spawn_meds) + var/amount = spawn_meds[typekey] || 1 + while(amount--) + var/obj/item/I = new typekey(src) + item_quants[I.name] += 1 + update_icon() + // Accepted items + accepted_items_typecache = typecacheof(list( + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers, + )) + +/** + * # Smart Chemical Storage (Preloaded) + * + * A [Smart Chemical Storage][/obj/machinery/smartfridge/secure/chemistry] but with some items already in. + */ +/obj/machinery/smartfridge/secure/chemistry/preloaded + // I exist! + +/obj/machinery/smartfridge/secure/chemistry/preloaded/Initialize(mapload) + spawn_meds = list( + /obj/item/reagent_containers/food/pill/epinephrine = 12, + /obj/item/reagent_containers/food/pill/charcoal = 5, + /obj/item/reagent_containers/glass/bottle/epinephrine = 1, + /obj/item/reagent_containers/glass/bottle/charcoal = 1, + ) + . = ..() + +/** + * # Smart Chemical Storage (Preloaded, Syndicate) + * + * A [Smart Chemical Storage (Preloaded)][/obj/machinery/smartfridge/secure/chemistry/preloaded] but with exclusive access to Syndicate. + */ +/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate + req_access_txt = null + +/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate/Initialize(mapload) + . = ..() + req_access = list(ACCESS_SYNDICATE) + +/** + * # Disk Compartmentalizer + * + * Disk variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/disks + name = "disk compartmentalizer" + desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)." + icon_state = "disktoaster" + pass_flags = PASSTABLE + visible_contents = FALSE + +/obj/machinery/smartfridge/disks/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/disk, + )) + +/** + * # Smart Virus Storage + * + * Secure, Virology variant of the [Smart Chemical Storage][/obj/machinery/smartfridge/secure/chemistry]. + * Comes with some items. + */ +/obj/machinery/smartfridge/secure/chemistry/virology + name = "\improper Smart Virus Storage" + desc = "A refrigerated storage unit for volatile sample storage." + +/obj/machinery/smartfridge/secure/chemistry/virology/Initialize(mapload) + spawn_meds = list( + /obj/item/reagent_containers/syringe/antiviral = 4, + /obj/item/reagent_containers/glass/bottle/cold = 1, + /obj/item/reagent_containers/glass/bottle/flu_virion = 1, + /obj/item/reagent_containers/glass/bottle/mutagen = 1, + /obj/item/reagent_containers/glass/bottle/plasma = 1, + /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1 + ) + . = ..() + req_access_txt = "[ACCESS_VIROLOGY]" + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/syringe, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/glass/beaker, + )) + +/** + * # Smart Virus Storage (Preloaded) + * + * A [Smart Virus Storage][/obj/machinery/smartfridge/secure/chemistry/virology] but with some additional items. + */ +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded + // I exist! + +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/Initialize(mapload) + spawn_meds = list( + /obj/item/reagent_containers/syringe/antiviral = 4, + /obj/item/reagent_containers/glass/bottle/cold = 1, + /obj/item/reagent_containers/glass/bottle/flu_virion = 1, + /obj/item/reagent_containers/glass/bottle/mutagen = 1, + /obj/item/reagent_containers/glass/bottle/plasma = 1, + /obj/item/reagent_containers/glass/bottle/reagent/synaptizine = 1, + /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde = 1 + ) + . = ..() + +/** + * # Smart Virus Storage (Preloaded, Syndicate) + * + * A [Smart Virus Storage (Preloaded)][/obj/machinery/smartfridge/secure/chemistry/virology/preloaded] but with exclusive access to Syndicate. + */ +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate + req_access_txt = null + +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate/Initialize(mapload) + . = ..() + req_access = list(ACCESS_SYNDICATE) + +/** + * # Drink Showcase + * + * Drink variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/drinks + name = "\improper Drink Showcase" + desc = "A refrigerated storage unit for tasty tasty alcohol." + +/obj/machinery/smartfridge/drinks/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/glass, + /obj/item/reagent_containers/food/drinks, + /obj/item/reagent_containers/food/condiment, + )) + +/** + * # Drying Rack + * + * Variant of the [Smart Fridge][/obj/machinery/smartfridge] for drying stuff. + * Doesn't have components. + */ /obj/machinery/smartfridge/drying_rack name = "drying rack" desc = "A wooden contraption, used to dry plant products, food and leather." @@ -470,11 +630,16 @@ can_dry = TRUE visible_contents = FALSE -/obj/machinery/smartfridge/drying_rack/New() - ..() - if(component_parts && component_parts.len) - component_parts.Cut() +/obj/machinery/smartfridge/drying_rack/Initialize(mapload) + . = ..() + // Remove components, this is wood duh + QDEL_LIST(component_parts) component_parts = null + // Accepted items + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/food/snacks, + /obj/item/stack/sheet/wetleather, + )) /obj/machinery/smartfridge/drying_rack/on_deconstruction() new /obj/item/stack/sheet/wood(loc, 10) @@ -483,34 +648,6 @@ /obj/machinery/smartfridge/drying_rack/RefreshParts() return -/obj/machinery/smartfridge/drying_rack/default_deconstruction_screwdriver() - return - -/obj/machinery/smartfridge/drying_rack/exchange_parts() - return - -/obj/machinery/smartfridge/drying_rack/spawn_frame() - return - -/obj/machinery/smartfridge/drying_rack/default_deconstruction_crowbar(user, obj/item/crowbar/C, ignore_panel = 1) - ..() - -/obj/machinery/smartfridge/drying_rack/Topic(href, href_list) - if(..()) - return 1 - if(href_list["dryingOn"]) - drying = TRUE - use_power = ACTIVE_POWER_USE - update_icon() - return 1 - - if(href_list["dryingOff"]) - drying = FALSE - use_power = IDLE_POWER_USE - update_icon() - return 1 - return 0 - /obj/machinery/smartfridge/drying_rack/power_change() if(powered() && anchored) stat &= ~NOPOWER @@ -519,35 +656,67 @@ toggle_drying(TRUE) update_icon() -/obj/machinery/smartfridge/drying_rack/load(obj/I, mob/user) //For updating the filled overlay +/obj/machinery/smartfridge/drying_rack/screwdriver_act(mob/living/user, obj/item/I) + return + +/obj/machinery/smartfridge/drying_rack/exchange_parts() + return + +/obj/machinery/smartfridge/drying_rack/spawn_frame() + return + +/obj/machinery/smartfridge/drying_rack/crowbar_act(mob/living/user, obj/item/I) + . = default_deconstruction_crowbar(user, I, TRUE) + +/obj/machinery/smartfridge/drying_rack/emp_act(severity) + ..() + atmos_spawn_air(LINDA_SPAWN_HEAT) + +/obj/machinery/smartfridge/drying_rack/Topic(href, href_list) if(..()) + return TRUE + + if(href_list["dryingOn"]) + drying = TRUE + use_power = ACTIVE_POWER_USE update_icon() - return 1 + return TRUE + + if(href_list["dryingOff"]) + drying = FALSE + use_power = IDLE_POWER_USE + update_icon() + return TRUE + + return FALSE /obj/machinery/smartfridge/drying_rack/update_icon() ..() - overlays.Cut() if(drying) overlays += "drying_rack_drying" - if(contents.len) + if(length(contents)) overlays += "drying_rack_filled" /obj/machinery/smartfridge/drying_rack/process() ..() - if(drying) - if(rack_dry())//no need to update unless something got dried - update_icon() + if(drying && rack_dry())//no need to update unless something got dried + update_icon() /obj/machinery/smartfridge/drying_rack/accept_check(obj/item/O) + . = ..() + // If it's a food, reject non driable ones if(istype(O, /obj/item/reagent_containers/food/snacks)) var/obj/item/reagent_containers/food/snacks/S = O - if(S.dried_type) - return TRUE - if(istype(O, /obj/item/stack/sheet/wetleather)) - return TRUE - return FALSE + if(!S.dried_type) + return FALSE +/** + * Toggles the drying process. + * + * Arguments: + * * forceoff - Whether to force turn off the drying rack. + */ /obj/machinery/smartfridge/drying_rack/proc/toggle_drying(forceoff) if(drying || forceoff) drying = FALSE @@ -557,6 +726,9 @@ use_power = ACTIVE_POWER_USE update_icon() +/** + * Called in [/obj/machinery/smartfridge/drying_rack/process] to dry the contents. + */ /obj/machinery/smartfridge/drying_rack/proc/rack_dry() for(var/obj/item/reagent_containers/food/snacks/S in contents) if(S.dried_type == S.type)//if the dried type is the same as the object's type, don't bother creating a whole new item... @@ -580,30 +752,4 @@ return TRUE return FALSE -/obj/machinery/smartfridge/drying_rack/emp_act(severity) - ..() - atmos_spawn_air(LINDA_SPAWN_HEAT) - -/************************ -* Secure SmartFridges -*************************/ -/obj/machinery/smartfridge/secure/emag_act(mob/user) - emagged = 1 - locked = -1 - to_chat(user, "You short out the product lock on [src].") - -/obj/machinery/smartfridge/secure/emp_act(severity) - if(prob(40/severity) && (!emagged) && (locked != -1)) - playsound(loc, 'sound/effects/sparks4.ogg', 60, 1) - emagged = 1 - locked = -1 - -/obj/machinery/smartfridge/secure/Topic(href, href_list) - if(stat & (NOPOWER|BROKEN)) - return 0 - if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) - if(!allowed(usr) && !emagged && locked != -1 && scan_id && href_list["vend"]) - to_chat(usr, "Access denied.") - SSnanoui.update_uis(src) - return 0 - return ..() +#undef SMART_FRIDGE_LOCK_SHORTED 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/library/admin.dm b/code/modules/library/admin.dm index db907fc20ba..b56aa6994cf 100644 --- a/code/modules/library/admin.dm +++ b/code/modules/library/admin.dm @@ -3,8 +3,7 @@ set desc = "Permamently deletes a book from the database." set category = "Admin" - if(!holder) - to_chat(src, "Only administrators may use this command.") + if(!check_rights(R_ADMIN)) return var/isbn = input("ISBN number?", "Delete Book") as num | null @@ -25,8 +24,7 @@ set desc = "View books flagged for content." set category = "Admin" - if(!holder) - to_chat(src, "Only administrators may use this command.") + if(!check_rights(R_ADMIN)) return holder.view_flagged_books() diff --git a/code/modules/mining/laborcamp/laborshuttle.dm b/code/modules/mining/laborcamp/laborshuttle.dm index 8e585eb15b0..88b0a6601dc 100644 --- a/code/modules/mining/laborcamp/laborshuttle.dm +++ b/code/modules/mining/laborcamp/laborshuttle.dm @@ -13,15 +13,3 @@ possible_destinations = "laborcamp_away" circuit = /obj/item/circuitboard/labor_shuttle/one_way req_access = list( ) - -/obj/machinery/computer/shuttle/labor/one_way/Topic(href, href_list) - if(href_list["move"]) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp") - if(!M) - to_chat(usr, "Cannot locate shuttle!") - return 0 - var/obj/docking_port/stationary/S = M.get_docked() - if(S && S.name == "laborcamp_away") - to_chat(usr, "Shuttle is already at the outpost!") - return 0 - ..() diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index 0bde4d95464..e99e7132f03 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -349,7 +349,7 @@ harm_intent_damage = 1 friendly = "mends" density = 0 - flying = 1 + flying = TRUE obj_damage = 0 pass_flags = PASSTABLE | PASSGRILLE | PASSMOB ventcrawler = 2 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 += "" - dat += "
    [prize.equipment_name][prize.cost]Purchase
    " - 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/observer.dm b/code/modules/mob/dead/observer/observer.dm index 336ebdb8b67..31ff0098335 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -53,7 +53,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) if(ismob(body)) T = get_turf(body) //Where is the body located? attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost - logs = body.logs.Copy() var/mutable_appearance/MA = copy_appearance(body) if(body.mind && body.mind.name) 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 2b07e10291f..2514ce68b06 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -47,7 +47,7 @@ . = trim(. + trim(msg)) . += "\"" -/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(!client) return 0 @@ -74,7 +74,7 @@ return 0 var/speaker_name = speaker.name - if(ishuman(speaker)) + if(use_voice && ishuman(speaker)) var/mob/living/carbon/human/H = speaker speaker_name = H.GetVoice() @@ -99,9 +99,9 @@ if(speaker == src) to_chat(src, "You cannot hear yourself speak!") else - to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].") + to_chat(src, "[speaker.name] talks but you cannot hear [speaker.p_them()].") else - to_chat(src, "[speaker_name][speaker.GetAltName()] [track][message]") + to_chat(src, "[speaker_name][use_voice ? speaker.GetAltName() : ""] [track][message]") if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) var/turf/source = speaker? get_turf(speaker) : get_turf(src) playsound_local(source, speech_sound, sound_vol, 1, sound_frequency) @@ -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/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index aeee677b905..f281b8abec3 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -81,7 +81,7 @@ /mob/living/carbon/alien/larva/show_inv(mob/user as mob) return -/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) return FALSE /* Commented out because it's duplicated in life.dm diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 5d828e7c181..79ed90a61b8 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -70,7 +70,7 @@ return polling = 1 spawn() - var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, FALSE, source = /mob/living/carbon/alien/larva) var/mob/C = null // To stop clientless larva, we will check that our host has a client diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 98585262518..b6e93dc78ff 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -538,6 +538,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven take_organ_damage(10) if(iscarbon(hit_atom) && hit_atom != src) var/mob/living/carbon/victim = hit_atom + if(victim.flying) + return if(hurt) victim.take_organ_damage(10) take_organ_damage(10) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 801a123b19f..865fc395148 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.dm b/code/modules/mob/living/carbon/human/human.dm index e07930f1241..d495ca3d2f4 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1447,12 +1447,13 @@ var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes) if(istype(dna.species) && dna.species.eyes) - var/icon/eyes_icon = new /icon('icons/mob/human_face.dmi', dna.species.eyes) + var/icon/eyes_icon if(eye_implant) //Eye implants override native DNA eye colo(u)r eyes_icon = eye_implant.generate_icon() else if(eyes) eyes_icon = eyes.generate_icon() else //Error 404: Eyes not found! + eyes_icon = new('icons/mob/human_face.dmi', dna.species.eyes) eyes_icon.Blend("#800000", ICON_ADD) return eyes_icon 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/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index f5496652dad..e667833dfe3 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -273,11 +273,6 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) overlays_standing[UNDERWEAR_LAYER] = mutable_appearance(underwear_standing, layer = -UNDERWEAR_LAYER) apply_overlay(UNDERWEAR_LAYER) - if(lip_style && (LIPS in dna.species.species_traits)) - var/mutable_appearance/lips = mutable_appearance('icons/mob/human_face.dmi', "lips_[lip_style]_s") - lips.color = lip_color - standing += lips - overlays_standing[BODY_LAYER] = standing apply_overlay(BODY_LAYER) //tail @@ -1303,6 +1298,11 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) else . += "#000000" + if(lip_color && (LIPS in dna.species.species_traits)) + . += "[lip_color]" + else + . += "#000000" + for(var/organ_tag in dna.species.has_limbs) var/obj/item/organ/external/part = bodyparts_by_name[organ_tag] if(isnull(part)) diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index cb607bae294..c3d0ca679d4 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -29,7 +29,7 @@ if(changed) animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT) handle_transform_change() - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). + floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). /mob/living/carbon/proc/handle_transform_change() return diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 3062ef461f9..c0341f11b47 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -2,9 +2,8 @@ set waitfor = FALSE set invisibility = 0 - if(flying) //TODO: Better floating - animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING) - animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING) + if(flying && !floating) //TODO: Better floating + float(TRUE) if(client || registered_z) // This is a temporary error tracker to make sure we've caught everything var/turf/T = get_turf(src) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 93a3b6f14cd..9e9724d20ec 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -3,8 +3,29 @@ var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medhud.add_to_hud(src) faction += "\ref[src]" + determine_move_and_pull_forces() GLOB.mob_living_list += src +// Used to determine the forces dependend on the mob size +// Will only change the force if the force was not set in the mob type itself +/mob/living/proc/determine_move_and_pull_forces() + var/value + switch(mob_size) + if(MOB_SIZE_TINY) + value = MOVE_FORCE_EXTREMELY_WEAK + if(MOB_SIZE_SMALL) + value = MOVE_FORCE_WEAK + if(MOB_SIZE_HUMAN) + value = MOVE_FORCE_NORMAL + if(MOB_SIZE_LARGE) + value = MOVE_FORCE_NORMAL // For now + if(!move_force) + move_force = value + if(!pull_force) + pull_force = value + if(!move_resist) + move_resist = value + /mob/living/prepare_huds() ..() prepare_data_huds() @@ -203,7 +224,7 @@ set category = "Object" if(istype(AM) && Adjacent(AM)) - start_pulling(AM) + start_pulling(AM, show_message = TRUE) else stop_pulling() @@ -737,16 +758,17 @@ /mob/living/proc/float(on) if(throwing) return - var/fixed = 0 + var/fixed = FALSE if(anchored || (buckled && buckled.anchored)) - fixed = 1 + fixed = TRUE if(on && !floating && !fixed) animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1) - floating = 1 + sleep(10) + animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1) + floating = TRUE else if(((!on || fixed) && floating)) - var/final_pixel_y = get_standard_pixel_y_offset(lying) - animate(src, pixel_y = final_pixel_y, time = 10) - floating = 0 + animate(src, pixel_y = get_standard_pixel_y_offset(lying), time = 10) + floating = FALSE /mob/living/proc/can_use_vents() return "You can't fit into that vent." @@ -821,15 +843,17 @@ if(!used_item) used_item = get_active_hand() ..() - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. + floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. /mob/living/proc/do_jitter_animation(jitteriness, loop_amount = 6) - var/amplitude = min(4, (jitteriness/100) + 1) + var/amplitude = min(4, (jitteriness / 100) + 1) var/pixel_x_diff = rand(-amplitude, amplitude) - var/pixel_y_diff = rand(-amplitude/3, amplitude/3) + var/pixel_y_diff = rand(-amplitude / 3, amplitude / 3) + var/final_pixel_x = get_standard_pixel_x_offset(lying) + var/final_pixel_y = get_standard_pixel_y_offset(lying) animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff , time = 2, loop = loop_amount) - animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y) , time = 2) - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. + animate(pixel_x = final_pixel_x , pixel_y = final_pixel_y , time = 2) + floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. /mob/living/proc/get_temperature(datum/gas_mixture/environment) @@ -921,10 +945,10 @@ return 0 return 1 -/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE) +/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) if(!AM || !src) return FALSE - if(!(AM.can_be_pulled(src, state, force))) + if(!(AM.can_be_pulled(src, state, force, show_message))) return FALSE if(incapacitated()) return diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 1f0f1cfa0f4..4da8aa97e06 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -219,7 +219,7 @@ fire_stacks += L.fire_stacks IgniteMob() -/mob/living/can_be_pulled(user, grab_state, force) +/mob/living/can_be_pulled(user, grab_state, force, show_message = FALSE) return ..() && !(buckled && buckled.buckle_prevents_pull) /mob/living/water_act(volume, temperature, source, method = REAGENT_TOUCH) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index e230f7a2b99..f8a5f08b313 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -2,6 +2,11 @@ see_invisible = SEE_INVISIBLE_LIVING pressure_resistance = 10 + // Will be determined based on mob size if left null. Done in living/proc/determine_move_and_pull_forces() + move_resist = null + move_force = null + pull_force = null + //Health and life related vars var/maxHealth = 100 //Maximum health that should be possible. var/health = 100 //A mob's health @@ -28,7 +33,7 @@ var/on_fire = 0 //The "Are we on fire?" var var/fire_stacks = 0 //Tracks how many stacks of fire we have on, max is usually 20 - var/floating = 0 + var/floating = FALSE var/mob_size = MOB_SIZE_HUMAN var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature.. var/digestion_ratio = 1 //controls how quickly reagents metabolize; largely governered by species attributes. diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 630b2223321..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()) @@ -451,14 +449,14 @@ proc/get_radio_key_from_channel(var/channel) var/speech_bubble_test = say_test(message) for(var/mob/M in listening) - M.hear_say(message_pieces, verb, italics, src) + M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE) if(M.client) speech_bubble_recipients.Add(M.client) if(eavesdropping.len) stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. for(var/mob/M in eavesdropping) - M.hear_say(message_pieces, verb, italics, src) + M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE) if(M.client) speech_bubble_recipients.Add(M.client) 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/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index d23fedb09bc..faacf9ba359 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -142,7 +142,7 @@ acceleration = !acceleration to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") -/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(relay_speech) if(istype(ai)) ai.relay_speech(speaker, message_pieces, verb) 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/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 599063c2724..a4e90b50f8e 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -80,17 +80,26 @@ GLOBAL_VAR_INIT(announcing_vox, 0) // Stores the time of the last announcement
  • Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.
  • \ WARNING:
    Misuse of the announcement system will get you job banned.
    " - var/index = 0 - for(var/word in GLOB.vox_sounds) - index++ - dat += "[capitalize(word)]" - if(index != GLOB.vox_sounds.len) - dat += " / " + // Show alert and voice sounds separately + var/vox_words = GLOB.vox_sounds - GLOB.vox_alerts + dat = help_format(GLOB.vox_alerts, dat) + dat = help_format(vox_words, dat) var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400) popup.set_content(dat) popup.open() +/mob/living/silicon/ai/proc/help_format(word_list, dat) + var/index = 0 + for(var/word in word_list) + index++ + dat += "[capitalize(word)]" + if(index != length(word_list)) + dat += " / " + else + dat += "
    " + return dat + /mob/living/silicon/ai/proc/ai_announcement() if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) return diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 2a5a7b0aa08..0a4e4cee898 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -517,7 +517,7 @@ /mob/living/silicon/pai/Bumped() return -/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) return FALSE /mob/living/silicon/pai/update_canmove(delay_action_updates = 0) @@ -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 91970d030a6..270606361cb 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -19,6 +19,7 @@ ventcrawler = 2 magpulse = 1 mob_size = MOB_SIZE_SMALL + pull_force = MOVE_FORCE_VERY_WEAK // Can only drag small items modules_break = FALSE @@ -35,6 +36,14 @@ var/reboot_cooldown = 60 // one minute var/last_reboot var/emagged_time + var/list/pullable_drone_items = list( + /obj/item/pipe, + /obj/structure/disposalconstruct, + /obj/item/stack/cable_coil, + /obj/item/stack/rods, + /obj/item/stack/sheet, + /obj/item/stack/tile + ) holder_type = /obj/item/holder/drone // var/sprite[0] @@ -50,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") @@ -69,6 +78,10 @@ verbs -= /mob/living/silicon/robot/verb/Namepick module = new /obj/item/robot_module/drone(src) + //Allows Drones to hear the Engineering channel. + module.channels = list("Engineering" = 1) + radio.recalculateChannels() + //Grab stacks. stack_metal = locate(/obj/item/stack/sheet/metal/cyborg) in src.module stack_wood = locate(/obj/item/stack/sheet/wood) in src.module @@ -154,15 +167,17 @@ return else - user.visible_message("\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.") + var/confirm = alert("Using your ID on a Maintenance Drone will shut it down, are you sure you want to do this?", "Disable Drone", "Yes", "No") + if(confirm == ("Yes") && (user in range(3, src))) + user.visible_message("\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.") - if(emagged) - return + if(emagged) + return - if(allowed(W)) - shut_down() - else - to_chat(user, "Access denied.") + if(allowed(W)) + shut_down() + else + to_chat(user, "Access denied.") return @@ -321,20 +336,22 @@ /mob/living/silicon/robot/drone/Bumped(atom/movable/AM) return -/mob/living/silicon/robot/drone/start_pulling(var/atom/movable/AM) +/mob/living/silicon/robot/drone/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) + + if(is_type_in_list(AM, pullable_drone_items)) + ..(AM, force = INFINITY) // Drone power! Makes them able to drag pipes and such - if(istype(AM,/obj/item/pipe) || istype(AM,/obj/structure/disposalconstruct)) - ..() else if(istype(AM,/obj/item)) var/obj/item/O = AM if(O.w_class > WEIGHT_CLASS_SMALL) - to_chat(src, "You are too small to pull that.") + if(show_message) + to_chat(src, "You are too small to pull that.") return else ..() else - to_chat(src, "You are too small to pull that.") - return + if(show_message) + to_chat(src, "You are too small to pull that.") /mob/living/silicon/robot/drone/add_robot_verbs() src.verbs |= silicon_subsystems diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index a0582c135e4..16e81f53276 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -8,7 +8,6 @@ //Has a list of items that it can hold. var/list/can_hold = list( - /obj/item/stock_parts/cell, /obj/item/firealarm_electronics, /obj/item/airalarm_electronics, /obj/item/airlock_electronics, @@ -24,6 +23,8 @@ /obj/item/mounted/frame/firealarm, /obj/item/mounted/frame/newscaster_frame, /obj/item/mounted/frame/intercom, + /obj/item/mounted/frame/extinguisher, + /obj/item/mounted/frame/light_switch, /obj/item/rack_parts, /obj/item/camera_assembly, /obj/item/tank, 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 faa51a58ec0..ee9cec18c89 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)) @@ -1063,7 +1119,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return 1 /mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + radio.interact(src) /mob/living/silicon/robot/proc/control_headlamp() if(stat || lamp_recharging || low_power_mode) @@ -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,11 +1487,12 @@ 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 module = new /obj/item/robot_module/destroyer(src) module.add_languages(src) module.add_subsystems_and_actions(src) @@ -1446,6 +1501,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( qdel(radio) radio = new /obj/item/radio/borg/ert/specops(src) radio.recalculateChannels() + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) /mob/living/silicon/robot/destroyer/borg_icons() if(base_icon == "") 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/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index e51dc527832..97fc3ac469c 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -94,7 +94,6 @@ hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST)//Diagnostic HUD views /obj/item/radio/headset/bot - subspace_transmission = 1 requires_tcomms = FALSE canhear_range = 0 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/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index f150005116e..4c2c2332924 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -17,7 +17,7 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 faction = list("cult") - flying = 1 + flying = TRUE pressure_resistance = 100 universal_speak = 1 AIStatus = AI_OFF //normal constructs don't have AI diff --git a/code/modules/mob/living/simple_animal/friendly/butterfly.dm b/code/modules/mob/living/simple_animal/friendly/butterfly.dm index 9aaffc6ef8f..ebf068ff130 100644 --- a/code/modules/mob/living/simple_animal/friendly/butterfly.dm +++ b/code/modules/mob/living/simple_animal/friendly/butterfly.dm @@ -15,6 +15,7 @@ harm_intent_damage = 1 friendly = "nudges" density = 0 + flying = TRUE pass_flags = PASSTABLE | PASSGRILLE | PASSMOB ventcrawler = 2 mob_size = MOB_SIZE_TINY diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index ea8e949f1c7..58e3ad54089 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -126,6 +126,7 @@ gold_core_spawnable = FRIENDLY_SPAWN blood_volume = BLOOD_VOLUME_NORMAL var/obj/item/udder/udder = null + gender = FEMALE /mob/living/simple_animal/cow/Initialize() udder = new() diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 2881141f4dd..be3f985db2e 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -85,8 +85,11 @@ get_scooped(M) ..() -/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things - to_chat(src, "You are too small to pull anything.") +/mob/living/simple_animal/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)//Prevents mouse from pulling things + if(istype(AM, /obj/item/reagent_containers/food/snacks/cheesewedge)) + return ..() // Get dem + if(show_message) + to_chat(src, "You are too small to pull anything except cheese.") return /mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index be04fc217e9..b47b0eb1f23 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -16,6 +16,7 @@ maxHealth = 20 health = 20 mob_size = MOB_SIZE_TINY + flying = TRUE harm_intent_damage = 8 melee_damage_lower = 10 melee_damage_upper = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index b8c72d7cf84..2f97001d3ae 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -241,6 +241,14 @@ Difficulty: Very Hard AT.pixel_y += random_y return ..() +/mob/living/simple_animal/hostile/megafauna/colossus/float(on) //we don't want this guy to float, messes up his animations + if(throwing) + return + if(on && !floating) + floating = TRUE + else if(!on && floating) + floating = FALSE + /obj/item/projectile/colossus name ="death bolt" icon_state= "chronobolt" diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm index f44e6c1f88a..ca6987d735c 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm @@ -26,21 +26,34 @@ flying = 1 var/carp_color = "carp" //holder for icon set - var/list/icon_sets = list("carp", "blue", "yellow", "grape", "rust", "teal", "purple") + var/static/list/carp_colors = list(\ + "lightpurple" = "#c3b9f1", \ + "lightpink" = "#da77a8", \ + "green" = "#70ff25", \ + "grape" = "#df0afb", \ + "swamp" = "#e5e75a", \ + "turquoise" = "#04e1ed", \ + "brown" = "#ca805a", \ + "teal" = "#20e28e", \ + "lightblue" = "#4d88cc", \ + "rusty" = "#dd5f34", \ + "beige" = "#bbaeaf", \ + "yellow" = "#f3ca4a", \ + "blue" = "#09bae1", \ + "palegreen" = "#7ef099", \ + ) /mob/living/simple_animal/hostile/retaliate/carp/Initialize(mapload) . = ..() carp_randomify() update_icons() -/mob/living/simple_animal/hostile/retaliate/carp/proc/carp_randomify() - if(prob(1)) - carp_color = pick("white", "black") - else - carp_color = pick(icon_sets) - icon_state = "[carp_color]" - icon_living = "[carp_color]" - icon_dead = "[carp_color]_dead" +/mob/living/simple_animal/hostile/retaliate/carp/proc/carp_randomify(rarechance) + // Simplified version of: /mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) + var/our_color + our_color = pick(carp_colors) + add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) + regenerate_icons() /mob/living/simple_animal/hostile/retaliate/carp/koi name = "space koi" @@ -61,13 +74,15 @@ maxbodytemp = 1500 gold_core_spawnable = HOSTILE_SPAWN + var/randomize_icon = TRUE /mob/living/simple_animal/hostile/retaliate/carp/koi/Initialize(mapload) . = ..() - var/koinum = rand(1, 4) - icon_state = "koi[koinum]" - icon_living = "koi[koinum]" - icon_dead = "koi[koinum]-dead" + if(randomize_icon) + var/koinum = rand(1, 4) + icon_state = "koi[koinum]" + icon_living = "koi[koinum]" + icon_dead = "koi[koinum]-dead" /mob/living/simple_animal/hostile/retaliate/carp/koi/Process_Spacemove(var/movement_dir) return TRUE @@ -76,3 +91,4 @@ icon_state = "koi5" icon_living = "koi5" icon_dead = "koi5-dead" + randomize_icon = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm index a3e32e9d35e..5d946662854 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm @@ -46,6 +46,7 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 + flying = TRUE pressure_resistance = 300 gold_core_spawnable = NO_SPAWN //too spooky for science faction = list("undead") // did I mention ghost diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index decfc7146cf..f992bb3cc10 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -362,7 +362,7 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 mob_size = MOB_SIZE_TINY - flying = 1 + flying = TRUE bubble_icon = "syndibot" gold_core_spawnable = HOSTILE_SPAWN del_on_death = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm index e7daced65ff..1946f1a32ee 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm @@ -51,6 +51,10 @@ if(!(eggtype in eggtypes)) to_chat(src, "Unrecognized egg type.") return 0 + if(fed < feedings_to_lay) + // We have to check this again after the popup, to account for people spam-clicking the button, then doing all the popups at once. + to_chat(src, "You must wrap more humanoid prey before you can do this!") + return visible_message("[src] lays a cluster of eggs.") if(eggtype == TS_DESC_RED) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 1) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm index 7e36b411bae..907e46b523e 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm @@ -47,7 +47,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/purple/Life(seconds, times_fired) . = ..() - if(.) // if mob is NOT dead + if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. if(!degenerate && spider_myqueen) if(dcheck_counter >= 10) dcheck_counter = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm index bf3c98bb86a..737b6eba22a 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm @@ -66,7 +66,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/Life(seconds, times_fired) . = ..() - if(.) // if mob is NOT dead + if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. if(ckey && canlay < 12 && hasnested) // max 12 eggs worth stored at any one time, realistically that's tons. if(world.time > (spider_lastspawn + spider_spawnfrequency)) if(eggslaid >= 20) @@ -287,6 +287,10 @@ if(eggtype == null || numlings == null) to_chat(src, "Cancelled.") return + if(canlay < numlings) + // We have to check this again after the popups, to account for people spam-clicking the button, then doing all the popups at once. + to_chat(src, "Too soon to do this again!") + return canlay -= numlings eggslaid += numlings if(eggtype == TS_DESC_RED) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 520143d71d0..ab36c6ca313 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -298,7 +298,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/Life(seconds, times_fired) . = ..() - if(!.) // if mob is dead + if(stat == DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. if(prob(2)) // 2% chance every cycle to decompose visible_message("\The dead body of the [src] decomposes!") diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 41786017ea2..8d6db8ade3c 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -86,6 +86,7 @@ //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. var/obj/item/held_item = null + flying = TRUE gold_core_spawnable = FRIENDLY_SPAWN @@ -710,7 +711,7 @@ ears.talk_into(src, message_pieces, message_mode, verb) used_radios += ears -/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(speaker != src && prob(50)) parrot_hear(html_decode(multilingual_to_message(message_pieces))) ..() diff --git a/code/modules/mob/living/simple_animal/posessed_object.dm b/code/modules/mob/living/simple_animal/posessed_object.dm index c3cb61d5970..729790d098c 100644 --- a/code/modules/mob/living/simple_animal/posessed_object.dm +++ b/code/modules/mob/living/simple_animal/posessed_object.dm @@ -32,8 +32,9 @@ animate_ghostly_presence(src, -1, 20, 1) // Restart the floating animation after the attack animation, as it will be cancelled. -/mob/living/simple_animal/possessed_object/start_pulling(var/atom/movable/AM) // Silly motherfuckers think they can pull things. - to_chat(src, "You are unable to pull [AM]!") +/mob/living/simple_animal/possessed_object/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) // Silly motherfuckers think they can pull things. + if(show_message) + to_chat(src, "You are unable to pull [AM]!") /mob/living/simple_animal/possessed_object/ghost() // Ghosting will return the object to normal, and will not disqualify the ghoster from various mid-round antag positions. diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 663838d3a45..1e3c723b278 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -24,6 +24,7 @@ status_flags = 0 faction = list("cult") status_flags = CANPUSH + flying = TRUE loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) del_on_death = 1 deathmessage = "lets out a contented sigh as their form unwinds." diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm index 74241dc676c..8679d64a699 100644 --- a/code/modules/mob/living/simple_animal/slime/say.dm +++ b/code/modules/mob/living/simple_animal/slime/say.dm @@ -9,7 +9,7 @@ return verb -/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(speaker != src && !stat) if(speaker in Friends) speech_buffer = list() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 15f483bef76..1b67b53119a 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -265,7 +265,7 @@ /mob/living/simple_animal/slime/unEquip(obj/item/I, force) return -/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) return /mob/living/simple_animal/slime/attack_ui(slot) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 7cd2b4414f7..ce266ba5b8f 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -141,18 +141,6 @@ if(updating && val_change) update_canmove() -/mob/living/proc/StartFlying() - var/val_change = !flying - flying = TRUE - if(val_change) - update_animations() - -/mob/living/proc/StopFlying() - var/val_change = !!flying - flying = FALSE - if(val_change) - update_animations() - // SCALAR STATUS EFFECTS diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 5345bda5b94..c222f76eadb 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -28,6 +28,7 @@ /mob/Login() GLOB.player_list |= src + last_known_ckey = ckey update_Login_details() world.update_status() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 1c7f8fa6ae9..848606be3ec 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -19,7 +19,6 @@ for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) AA.viewers -= src viewing_alternate_appearances = null - logs.Cut() LAssailant = null return ..() @@ -578,6 +577,8 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ changeNext_move(CLICK_CD_POINT) var/obj/P = new /obj/effect/temp_visual/point(tile) P.invisibility = invisibility + P.pixel_x = A.pixel_x + P.pixel_y = A.pixel_y return 1 /mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag) @@ -1238,10 +1239,13 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ create_log_in_list(debug_log, text, collapse, world.timeofday) /mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src)) - LAZYINITLIST(logs[log_type]) - var/list/log_list = logs[log_type] + if(!ckey) + return + var/real_ckey = ckey + if(ckey[1] == "@") // Admin aghosting will do this + real_ckey = copytext(ckey, 2) var/datum/log_record/record = new(log_type, src, what, target, where, world.time) - log_list.Add(record) + GLOB.logging.add_log(real_ckey, record) /proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc //this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar." diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 1477be47d89..80404460b50 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -36,7 +36,7 @@ var/list/attack_log_old = list( ) var/list/debug_log = null - var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm + var/last_known_ckey = null // Used in logging var/last_log = 0 var/obj/machinery/machine = null diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 5fb5bcb06c8..ff01fbe5cc9 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -113,7 +113,7 @@ var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]" if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes") question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]" - var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M) var/mob/dead/observer/theghost = null if(LAZYLEN(candidates)) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 4797c685fe1..0325d13faf0 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -369,8 +369,9 @@ step(pulling, get_dir(pulling.loc, A)) return -/mob/proc/update_gravity() +/mob/proc/update_gravity(has_gravity) return + /client/proc/check_has_body_select() return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 0417e28f546..7a26de718b6 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -44,17 +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/mob/update_status.dm b/code/modules/mob/update_status.dm index 1708bcfb7a2..b603fc5974e 100644 --- a/code/modules/mob/update_status.dm +++ b/code/modules/mob/update_status.dm @@ -55,12 +55,6 @@ // Procs that update other things about the mob -// Does various animations - Jitter, Flying, Spinning -/mob/proc/update_animations() - if(flying) - animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING) - animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING) - /mob/proc/update_stat() return 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/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index b85fb710e38..cae4b5ed2e2 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -222,7 +222,7 @@ if(is_authenticated(usr) && modify) var/t1 = href_list["assign_target"] if(t1 == "Custom") - var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN)) + var/temp_t = sanitize(reject_bad_name(copytext(input("Enter a custom job assignment.", "Assignment"), 1, MAX_MESSAGE_LEN), TRUE)) //let custom jobs function as an impromptu alt title, mainly for sechuds if(temp_t && modify) SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name) @@ -251,7 +251,8 @@ message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name) - SSjobs.slot_job_transfer(modify.rank, t1) + if(modify.owner_uid) + SSjobs.slot_job_transfer(modify.rank, t1) var/mob/living/carbon/human/H = modify.getPlayer() if(istype(H)) @@ -266,7 +267,7 @@ if("PRG_reg") if(is_authenticated(usr)) - var/temp_name = reject_bad_name(href_list["reg"]) + var/temp_name = reject_bad_name(href_list["reg"], TRUE) if(temp_name) modify.registered_name = temp_name else @@ -290,6 +291,8 @@ if(is_authenticated(usr)) var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE) if(delcount) + message_admins("[key_name_admin(usr)] has wiped all ID computer logs.") + usr.create_log(MISC_LOG, "wiped all ID computer logs.") playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) if("PRG_print") @@ -331,9 +334,14 @@ if("PRG_terminate") if(is_authenticated(usr)) var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") - message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") + var/reason = sanitize(copytext(input("Enter legal reason for termination. Enter nothing to cancel.", "Employment Termination"), 1, MAX_MESSAGE_LEN)) + if(!reason || !is_authenticated(usr) || !modify) + return + log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for: \"[reason]\".") + message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for: \"[reason]\".") + usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\"") SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name) + SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".") modify.assignment = "Terminated" modify.access = list() 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/ninja/suit/suit_initialisation.dm b/code/modules/ninja/suit/suit_initialisation.dm index 59303b4707c..b9200d0e771 100644 --- a/code/modules/ninja/suit/suit_initialisation.dm +++ b/code/modules/ninja/suit/suit_initialisation.dm @@ -14,30 +14,30 @@ suitBusy = 1 if(suitActive && (alert("Confirm suit systems shutdown? This cannot be halted once it has started.", "Confirm Shutdown", "Yes", "No") == "Yes")) - to_chat(usr, "Now de-initializing...") + to_chat(usr, "Now de-initializing...") sleep(15) - to_chat(usr, "Logging off, [usr.real_name]. Shutting down SpiderOS.") + to_chat(usr, "Logging off, [usr.real_name]. Shutting down SpiderOS.") sleep(10) - to_chat(usr, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.") + to_chat(usr, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.") sleep(5) - to_chat(usr, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.") + to_chat(usr, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.") //TODO: Shut down any active abilities sleep(10) - to_chat(usr, "Disconnecting neural-net interface... Success.") + to_chat(usr, "Disconnecting neural-net interface... Success.") QDEL_NULL(usr.hud_used) usr.create_mob_hud() usr.regenerate_icons() sleep(5) - to_chat(usr, "Disengaging neural-net interface... Success.") + to_chat(usr, "Disengaging neural-net interface... Success.") sleep(10) - to_chat(usr, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.") + to_chat(usr, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.") //TODO: Grant verbs toggle_suit_lock(usr) usr.regenerate_icons() @@ -45,24 +45,24 @@ suitActive = 0 else if(!suitActive) // Activate the suit. - to_chat(usr, "Now initializing...") + to_chat(usr, "Now initializing...") sleep(15) - to_chat(usr, "Now establishing neural-net interface...") + to_chat(usr, "Now establishing neural-net interface...") if(usr.mind.special_role != "Ninja") to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.") return sleep(10) - to_chat(usr, "Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding.") + to_chat(usr, "Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding.") sleep(10) - to_chat(usr, "Securing external locking mechanism...") + to_chat(usr, "Securing external locking mechanism...") if(!toggle_suit_lock(usr)) return sleep(5) - to_chat(usr, "Suit secured, extending neural-net interface...") + to_chat(usr, "Suit secured, extending neural-net interface...") QDEL_NULL(usr.hud_used) usr.hud_used = new /datum/hud/human(usr, 'icons/mob/screen_ninja.dmi', "#ffffff", 255) @@ -71,22 +71,22 @@ usr.regenerate_icons() sleep(10) - to_chat(usr, "VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE") + to_chat(usr, "VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE") sleep(5) - to_chat(usr, "Primary system status: ONLINE.\nBackup system status: ONLINE.") + to_chat(usr, "Primary system status: ONLINE.\nBackup system status: ONLINE.") if(suitCell) - to_chat(usr, "Current energy capacity: [suitCell.charge]/[suitCell.maxcharge].") + to_chat(usr, "Current energy capacity: [suitCell.charge]/[suitCell.maxcharge].") sleep(10) - to_chat(usr, "All systems operational. Welcome to SpiderOS, [usr.real_name].") + to_chat(usr, "All systems operational. Welcome to SpiderOS, [usr.real_name].") //TODO: Grant ninja verbs here. suitBusy = 0 suitActive = 1 else suitBusy = 0 - to_chat(usr, "NOTICE: Suit de-activation protocals aborted.") + to_chat(usr, "NOTICE: Suit de-activation protocals aborted.") else to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.") return diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 09858073142..32416a8d5e7 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -12,24 +12,32 @@ GLOBAL_LIST_EMPTY(fax_blacklist) insert_anim = "faxsend" pass_flags = PASSTABLE var/fax_network = "Local Fax Network" - var/syndie_restricted = FALSE //is it a syndicate base fax restricted from contacting NT assets? + /// If true, prevents fax machine from sending messages to NT machines + var/syndie_restricted = FALSE - var/long_range_enabled = 0 // Can we send messages off the station? + /// Can we send messages off-station? + var/long_range_enabled = FALSE req_one_access = list(ACCESS_LAWYER, ACCESS_HEADS, ACCESS_ARMORY) use_power = IDLE_POWER_USE idle_power_usage = 30 active_power_usage = 200 - var/obj/item/card/id/scan = null // identification + /// ID card inserted into the machine, used to log in with + var/obj/item/card/id/scan = null - var/authenticated = 0 - var/sendcooldown = 0 // to avoid spamming fax messages + /// Whether the machine is "logged in" or not + var/authenticated = FALSE + /// Next world.time at which this fax machine can send a message to CC/syndicate + var/sendcooldown = 0 + /// After sending a message to CC/syndicate, cannot send another to them for this many deciseconds var/cooldown_time = 1800 - var/department = "Unknown" // our department + /// Our department, determines whether this machine gets faxes sent to a department + var/department = "Unknown" - var/destination = "Not Selected" // the department we're sending to + /// Target department to send outgoing faxes to + var/destination /obj/machinery/photocopier/faxmachine/New() ..() @@ -44,7 +52,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist) /obj/machinery/photocopier/faxmachine/longrange name = "long range fax machine" fax_network = "Central Command Quantum Entanglement Network" - long_range_enabled = 1 + long_range_enabled = TRUE /obj/machinery/photocopier/faxmachine/longrange/syndie name = "syndicate long range fax machine" @@ -58,17 +66,17 @@ GLOBAL_LIST_EMPTY(fax_blacklist) GLOB.hidden_departments |= department /obj/machinery/photocopier/faxmachine/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/photocopier/faxmachine/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/photocopier/faxmachine/attackby(obj/item/item, mob/user, params) if(istype(item,/obj/item/card/id) && !scan) scan(item) else if(istype(item, /obj/item/paper) || istype(item, /obj/item/photo) || istype(item, /obj/item/paper_bundle)) ..() - SSnanoui.update_uis(src) + SStgui.update_uis(src) else return ..() @@ -80,42 +88,6 @@ GLOBAL_LIST_EMPTY(fax_blacklist) else to_chat(user, "You swipe the card through [src], but nothing happens.") -/obj/machinery/photocopier/faxmachine/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, "faxmachine.tmpl", "Fax Machine UI", 540, 450) - ui.open() - -/obj/machinery/photocopier/faxmachine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/is_authenticated = is_authenticated(user) - - if(scan) - data["scan_name"] = scan.name - else - data["scan_name"] = "-----" - data["authenticated"] = is_authenticated - if(!is_authenticated) - data["network"] = "Disconnected" - else if(!emagged) - data["network"] = fax_network - else - data["network"] = "ERR*?*%!*" - if(copyitem) - data["paper"] = copyitem.name - data["paperinserted"] = 1 - else - data["paper"] = "-----" - data["paperinserted"] = 0 - data["destination"] = destination - data["cooldown"] = sendcooldown - if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) - data["respectcooldown"] = 1 - else - data["respectcooldown"] = 0 - - return data - /obj/machinery/photocopier/faxmachine/proc/is_authenticated(mob/user) if(authenticated) return TRUE @@ -123,87 +95,134 @@ GLOBAL_LIST_EMPTY(fax_blacklist) return TRUE return FALSE -/obj/machinery/photocopier/faxmachine/Topic(href, href_list) - if(..()) - return 1 +/obj/machinery/photocopier/faxmachine/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, "FaxMachine", name, 540, 300, master_ui, state) + ui.open() +/obj/machinery/photocopier/faxmachine/tgui_data(mob/user) + var/list/data = list() + data["authenticated"] = is_authenticated(user) + data["scan_name"] = scan ? scan.name : FALSE + if(!data["authenticated"]) + data["network"] = "Disconnected" + else if(!emagged) + data["network"] = fax_network + else + data["network"] = "ERR*?*%!*" + data["paper"] = copyitem ? copyitem.name : FALSE + data["paperinserted"] = copyitem ? TRUE : FALSE + data["destination"] = destination ? destination : FALSE + data["sendError"] = FALSE + if(stat & (BROKEN|NOPOWER)) + data["sendError"] = "No Power" + else if(!data["authenticated"]) + data["sendError"] = "Not Logged In" + else if(!data["paper"]) + data["sendError"] = "Nothing Inserted" + else if(!data["destination"]) + data["sendError"] = "Destination Not Set" + else if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) + var/cooldown_seconds = cooldown_seconds() + if(cooldown_seconds) + data["sendError"] = "Re-aligning in [cooldown_seconds] seconds..." + return data + + +/obj/machinery/photocopier/faxmachine/tgui_act(action, params) + if(..()) + return var/is_authenticated = is_authenticated(usr) - if(href_list["send"]) - if(copyitem && is_authenticated) + . = TRUE + switch(action) + if("scan") // insert/remove your ID card + scan() + if("auth") // log in/out + if(!is_authenticated && scan) + if(scan.registered_name in GLOB.fax_blacklist) + to_chat(usr, "Login rejected: individual is blacklisted from fax network.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + . = FALSE + else if(check_access(scan)) + authenticated = TRUE + else // ID doesn't have access to this machine + to_chat(usr, "Login rejected: ID card does not have required access.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + . = FALSE + else if(is_authenticated) + authenticated = FALSE + if("paper") // insert/eject paper/paperbundle/photo + if(copyitem) + copyitem.forceMove(get_turf(src)) + if(ishuman(usr)) + if(!usr.get_active_hand() && Adjacent(usr)) + usr.put_in_hands(copyitem) + to_chat(usr, "You eject [copyitem] from [src].") + copyitem = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/paper) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle)) + usr.drop_item() + copyitem = I + I.forceMove(src) + to_chat(usr, "You insert [I] into [src].") + flick(insert_anim, src) + else + to_chat(usr, "[src] only accepts paper, paper bundles, and photos.") + . = FALSE + if("rename") // rename the item that is currently in the fax machine + if(copyitem) + var/n_name = sanitize(copytext(input(usr, "What would you like to label the fax?", "Fax Labelling", copyitem.name) as text, 1, MAX_MESSAGE_LEN)) + if((copyitem && copyitem.loc == src && usr.stat == 0)) + if(istype(copyitem, /obj/item/paper)) + copyitem.name = "[(n_name ? text("[n_name]") : initial(copyitem.name))]" + copyitem.desc = "This is a paper titled '" + copyitem.name + "'." + else if(istype(copyitem, /obj/item/photo)) + copyitem.name = "[(n_name ? text("[n_name]") : "photo")]" + else if(istype(copyitem, /obj/item/paper_bundle)) + copyitem.name = "[(n_name ? text("[n_name]") : "paper")]" + else + . = FALSE + else + . = FALSE + else + . = FALSE + if("dept") // choose which department receives the fax + if(is_authenticated) + var/lastdestination = destination + var/list/combineddepartments = GLOB.alldepartments.Copy() + if(long_range_enabled) + combineddepartments += GLOB.admin_departments.Copy() + if(emagged) + combineddepartments += GLOB.hidden_admin_departments.Copy() + combineddepartments += GLOB.hidden_departments.Copy() + if(syndie_restricted) + combineddepartments = GLOB.hidden_admin_departments.Copy() + combineddepartments += GLOB.hidden_departments.Copy() + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) + if(F.emagged)//we can contact emagged faxes on the station + combineddepartments |= F.department + destination = input(usr, "To which department?", "Choose a department", "") as null|anything in combineddepartments + if(!destination) + destination = lastdestination + if("send") // actually send the fax + if(!copyitem || !is_authenticated || !destination) + return + if(stat & (BROKEN|NOPOWER)) + return if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) + var/cooldown_seconds = cooldown_seconds() + if(cooldown_seconds > 0) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + to_chat(usr, "[src] is not ready for another [cooldown_seconds] seconds.") + return send_admin_fax(usr, destination) + sendcooldown = world.time + cooldown_time else sendfax(destination, usr) - - if(sendcooldown) - spawn(sendcooldown) // cooldown time - sendcooldown = 0 - SSnanoui.update_uis(src) - - if(href_list["paper"]) - if(copyitem) - copyitem.forceMove(get_turf(src)) - if(ishuman(usr)) - if(!usr.get_active_hand() && Adjacent(usr)) - usr.put_in_hands(copyitem) - to_chat(usr, "You eject \the [copyitem] from \the [src].") - copyitem = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/paper) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle)) - usr.drop_item() - copyitem = I - I.forceMove(src) - to_chat(usr, "You insert \the [I] into \the [src].") - flick(insert_anim, src) - - if(href_list["scan"]) - scan() - - if(href_list["dept"]) - if(is_authenticated) - var/lastdestination = destination - var/list/combineddepartments = GLOB.alldepartments.Copy() - if(long_range_enabled) - combineddepartments += GLOB.admin_departments.Copy() - - if(emagged) - combineddepartments += GLOB.hidden_admin_departments.Copy() - combineddepartments += GLOB.hidden_departments.Copy() - - if(syndie_restricted) - combineddepartments = GLOB.hidden_admin_departments.Copy() - combineddepartments += GLOB.hidden_departments.Copy() - for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) - if(F.emagged)//we can contact emagged faxes on the station - combineddepartments |= F.department - - destination = input(usr, "To which department?", "Choose a department", "") as null|anything in combineddepartments - if(!destination) - destination = lastdestination - - if(href_list["auth"]) - if(!is_authenticated && scan) - if(scan.registered_name in GLOB.fax_blacklist) - playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - else if(check_access(scan)) - authenticated = 1 - else if(is_authenticated) - authenticated = 0 - - if(href_list["rename"]) - if(copyitem) - var/n_name = sanitize(copytext(input(usr, "What would you like to label the fax?", "Fax Labelling", copyitem.name) as text, 1, MAX_MESSAGE_LEN)) - if((copyitem && copyitem.loc == src && usr.stat == 0)) - if(istype(copyitem, /obj/item/paper)) - copyitem.name = "[(n_name ? text("[n_name]") : initial(copyitem.name))]" - copyitem.desc = "This is a paper titled '" + copyitem.name + "'." - else if(istype(copyitem, /obj/item/photo)) - copyitem.name = "[(n_name ? text("[n_name]") : "photo")]" - else if(istype(copyitem, /obj/item/paper_bundle)) - copyitem.name = "[(n_name ? text("[n_name]") : "paper")]" - - SSnanoui.update_uis(src) + if(.) + add_fingerprint(usr) /obj/machinery/photocopier/faxmachine/proc/scan(var/obj/item/card/id/card = null) if(scan) // Card is in machine @@ -226,7 +245,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist) usr.drop_item() card.forceMove(src) scan = card - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/photocopier/faxmachine/verb/eject_id() set category = null @@ -237,25 +256,20 @@ GLOBAL_LIST_EMPTY(fax_blacklist) return if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") + to_chat(usr, "You remove [scan] from [src].") scan.forceMove(get_turf(src)) if(!usr.get_active_hand() && Adjacent(usr)) usr.put_in_hands(scan) scan = null else - to_chat(usr, "There is nothing to remove from \the [src].") + to_chat(usr, "There is nothing to remove from [src].") /obj/machinery/photocopier/faxmachine/proc/sendfax(var/destination,var/mob/sender) - if(stat & (BROKEN|NOPOWER)) - return - - use_power(200) - + use_power(active_power_usage) var/success = 0 for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.department == destination) success = F.receivefax(copyitem) - if(success) var/datum/fax/F = new /datum/fax() F.name = copyitem.name @@ -272,10 +286,10 @@ GLOBAL_LIST_EMPTY(fax_blacklist) /obj/machinery/photocopier/faxmachine/proc/receivefax(var/obj/item/incoming) if(stat & (BROKEN|NOPOWER)) - return 0 + return FALSE if(department == "Unknown") - return 0 //You can't send faxes to "Unknown" + return FALSE //You can't send faxes to "Unknown" flick("faxreceive", src) @@ -291,19 +305,13 @@ GLOBAL_LIST_EMPTY(fax_blacklist) else if(istype(incoming, /obj/item/paper_bundle)) bundlecopy(incoming) else - return 0 + return FALSE use_power(active_power_usage) - return 1 + return TRUE /obj/machinery/photocopier/faxmachine/proc/send_admin_fax(var/mob/sender, var/destination) - if(stat & (BROKEN|NOPOWER)) - return - - if(sendcooldown) - return - - use_power(200) + use_power(active_power_usage) if(!(istype(copyitem, /obj/item/paper) || istype(copyitem, /obj/item/paper_bundle) || istype(copyitem, /obj/item/photo))) visible_message("[src] beeps, \"Error transmitting message.\"") @@ -327,10 +335,12 @@ GLOBAL_LIST_EMPTY(fax_blacklist) for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.department == destination) F.receivefax(copyitem) - sendcooldown = cooldown_time - spawn(50) - visible_message("[src] beeps, \"Message transmitted successfully.\"") + visible_message("[src] beeps, \"Message transmitted successfully.\"") +/obj/machinery/photocopier/faxmachine/proc/cooldown_seconds() + if(sendcooldown < world.time) + return 0 + return round((sendcooldown - world.time) / 10) /obj/machinery/photocopier/faxmachine/proc/message_admins(var/mob/sender, var/faxname, var/faxtype, var/obj/item/sent, font_colour="#9A04D1") var/msg = "[faxname]: [key_name_admin(sender)] | REPLY: (RADIO) (FAX) ([ADMIN_SM(sender,"SM")]) | REJECT: (TEMPLATE) ([ADMIN_BSA(sender,"BSA")]) (EVILFAX) : Receiving '[sent.name]' via secure connection... view message" diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm index 2234e2911a6..0492d3da694 100644 --- a/code/modules/paperwork/ticketmachine.dm +++ b/code/modules/paperwork/ticketmachine.dm @@ -7,6 +7,7 @@ icon_state = "ticketmachine" desc = "A marvel of bureaucratic engineering encased in an efficient plastic shell. It can be refilled with a hand labeler refill roll and linked to buttons with a multitool." density = FALSE + anchored = TRUE maptext_height = 26 maptext_width = 32 maptext_x = 7 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 3e8d71b3280..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) @@ -627,7 +643,7 @@ /obj/item/light/Crossed(mob/living/L) if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || L.flying) + if(L.incorporeal_move || L.flying || L.floating) return playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) if(status == LIGHT_BURNED || status == LIGHT_OK) 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/smes.dm b/code/modules/power/smes.dm index 59ce091b170..2fa2c4c1573 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -227,8 +227,8 @@ if(terminal) terminal.master = null terminal = null - return 1 - return 0 + return TRUE + return FALSE /obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc) // create a terminal object at the same position as original turf loc @@ -339,89 +339,91 @@ /obj/machinery/power/smes/attack_ai(mob/user) add_hiddenprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/power/smes/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/power/smes/attack_hand(mob/user) add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/power/smes/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) if(stat & BROKEN) 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) + 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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380) - // open the new ui window + ui = new(user, src, ui_key, "Smes", name, 340, 350, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["nameTag"] = name_tag - data["storedCapacity"] = round(100.0*charge/capacity, 0.1) - data["charging"] = inputting - data["chargeMode"] = input_attempt - data["chargeLevel"] = input_level - data["chargeMax"] = input_level_max - data["outputOnline"] = output_attempt - data["outputLevel"] = output_level - data["outputMax"] = output_level_max - data["outputLoad"] = round(output_used) - - if(outputting) - data["outputting"] = 2 // smes is outputting - else if(!outputting && output_attempt) - data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low - else - data["outputting"] = 0 // smes is not outputting +/obj/machinery/power/smes/tgui_data(mob/user) + var/list/data = list( + "capacity" = capacity, + "capacityPercent" = round(100*charge/capacity, 0.1), + "charge" = charge, + "inputAttempt" = input_attempt, + "inputting" = inputting, + "inputLevel" = input_level, + "inputLevel_text" = DisplayPower(input_level), + "inputLevelMax" = input_level_max, + "inputAvailable" = input_available, + "outputAttempt" = output_attempt, + "outputting" = outputting, + "outputLevel" = output_level, + "outputLevel_text" = DisplayPower(output_level), + "outputLevelMax" = output_level_max, + "outputUsed" = round(output_used), + ) return data -/obj/machinery/power/smes/Topic(href, href_list) +/obj/machinery/power/smes/tgui_act(action, params) if(..()) - return 1 + return + . = TRUE + switch(action) + if("tryinput") + inputting(!input_attempt) + update_icon() + if("tryoutput") + outputting(!output_attempt) + update_icon() + if("input") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "min") + target = 0 + else if(target == "max") + target = input_level_max + else if(adjust) + target = input_level + adjust + else if(text2num(target) != null) + target = text2num(target) + else + . = FALSE + if(.) + input_level = clamp(target, 0, input_level_max) + if("output") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "min") + target = 0 + else if(target == "max") + target = output_level_max + else if(adjust) + target = output_level + adjust + else if(text2num(target) != null) + target = text2num(target) + else + . = FALSE + if(.) + output_level = clamp(target, 0, output_level_max) + else + . = FALSE + if(.) + log_smes(usr) - if( href_list["cmode"] ) - inputting(!input_attempt) - update_icon() - - else if( href_list["online"] ) - outputting(!output_attempt) - update_icon() - - else if( href_list["input"] ) - switch( href_list["input"] ) - if("min") - input_level = 0 - if("max") - input_level = input_level_max - if("set") - input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num - input_level = max(0, min(input_level_max, input_level)) // clamp to range - - else if( href_list["output"] ) - switch( href_list["output"] ) - if("min") - output_level = 0 - if("max") - output_level = output_level_max - if("set") - output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num - output_level = max(0, min(output_level_max, output_level)) // clamp to range - - investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") - - return 1 +/obj/machinery/power/smes/proc/log_smes(mob/user) + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Charge: [charge] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [user ? key_name(user) : "outside forces"]", "singulo") /obj/machinery/power/smes/proc/ion_act() if(is_station_level(src.z)) @@ -448,6 +450,7 @@ smoke.attach(src) smoke.start() + /obj/machinery/power/smes/proc/inputting(var/do_input) input_attempt = do_input if(!input_attempt) @@ -459,14 +462,15 @@ outputting = 0 /obj/machinery/power/smes/emp_act(severity) - inputting(rand(0,1)) - outputting(rand(0,1)) + inputting(rand(0, 1)) + outputting(rand(0, 1)) output_level = rand(0, output_level_max) input_level = rand(0, input_level_max) charge -= 1e6/severity if(charge < 0) charge = 0 update_icon() + log_smes() ..() /obj/machinery/power/smes/engineering 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/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 4aee2398b68..080114554bb 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -270,7 +270,6 @@ M.mind.transfer_to(new_mob) else new_mob.attack_log_old = M.attack_log_old.Copy() - new_mob.logs = M.logs.Copy() new_mob.key = M.key to_chat(new_mob, "Your form morphs into that of a [randomize].") diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 91d5900610d..bacf2285c83 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -24,6 +24,7 @@ var/list/hacked_reagents = list("toxin") var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode." var/unhack_message = "You re-enable the safety safeguards, enabling the \"NT Standard\" mode." + var/is_drink = FALSE /obj/machinery/chem_dispenser/get_cell() return cell @@ -120,7 +121,6 @@ var/usedpower = cell.give(recharge_amount) if(usedpower) use_power(15 * recharge_amount) - SSnanoui.update_uis(src) // update all UIs attached to src recharge_counter = 0 return recharge_counter++ @@ -131,7 +131,6 @@ else spawn(rand(0, 15)) stat |= NOPOWER - SSnanoui.update_uis(src) // update all UIs attached to src /obj/machinery/chem_dispenser/ex_act(severity) if(severity < 3) @@ -145,19 +144,17 @@ beaker = null overlays.Cut() -/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) +/obj/machinery/chem_dispenser/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 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) + 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 - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "chem_dispenser.tmpl", ui_title, 390, 655) - // open the new ui window + ui = new(user, src, ui_key, "ChemDispenser", ui_title, 390, 655) ui.open() -/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/chem_dispenser/tgui_data(mob/user) var/data[0] + data["glass"] = is_drink data["amount"] = amount data["energy"] = cell.charge ? cell.charge * powerefficiency : "0" //To prevent NaN in the UI. data["maxEnergy"] = cell.maxcharge * powerefficiency @@ -187,63 +184,59 @@ return data -/obj/machinery/chem_dispenser/Topic(href, href_list) +/obj/machinery/chem_dispenser/tgui_act(actions, params) if(..()) - return TRUE + return + if(stat & (NOPOWER|BROKEN)) + return - if(href_list["amount"]) - amount = round(text2num(href_list["amount"]), 1) // round to nearest 1 - if(amount < 0) // Since the user can actually type the commands himself, some sanity checking - amount = 0 - if(amount > 100) - amount = 100 - - if(href_list["dispense"]) - if(!is_operational() || QDELETED(cell)) - return - if(beaker && dispensable_reagents.Find(href_list["dispense"])) + . = TRUE + switch(actions) + if("amount") + amount = clamp(round(text2num(params["amount"]), 1), 0, 50) // round to nearest 1 and clamp to 0 - 50 + if("dispense") + if(!is_operational() || QDELETED(cell)) + return + if(!beaker || !dispensable_reagents.Find(params["reagent"])) + return var/datum/reagents/R = beaker.reagents var/free = R.maximum_volume - R.total_volume var/actual = min(amount, (cell.charge * powerefficiency) * 10, free) - if(!cell.use(actual / powerefficiency)) atom_say("Not enough energy to complete operation!") return - - R.add_reagent(href_list["dispense"], actual) + R.add_reagent(params["reagent"], actual) overlays.Cut() if(!icon_beaker) - icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position. + icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position. icon_beaker.pixel_x = rand(-10, 5) overlays += icon_beaker - - if(href_list["remove"]) - if(beaker) - if(href_list["removeamount"]) - var/amount = text2num(href_list["removeamount"]) - if(isnum(amount) && (amount > 0)) - var/datum/reagents/R = beaker.reagents - var/id = href_list["remove"] - R.remove_reagent(id, amount) - else if(isnum(amount) && (amount == -1)) //Isolate instead - var/datum/reagents/R = beaker.reagents - var/id = href_list["remove"] - R.isolate_reagent(id) - - if(href_list["ejectBeaker"]) - if(beaker) + if("remove") + var/amount = text2num(params["amount"]) + if(!beaker || !amount) + return + var/datum/reagents/R = beaker.reagents + var/id = params["reagent"] + if(amount > 0) + R.remove_reagent(id, amount) + else if(amount == -1) //Isolate instead + R.isolate_reagent(id) + if("ejectBeaker") + if(!beaker) + return beaker.forceMove(loc) if(Adjacent(usr) && !issilicon(usr)) usr.put_in_hands(beaker) beaker = null overlays.Cut() + else + return FALSE add_fingerprint(usr) - return TRUE // update UIs attached to this object /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) if(exchange_parts(user, I)) - SSnanoui.update_uis(src) + SStgui.update_uis(src) return if(isrobot(user)) @@ -263,9 +256,9 @@ beaker = I I.forceMove(src) to_chat(user, "You set [I] on the machine.") - SSnanoui.update_uis(src) // update all UIs attached to src + SStgui.update_uis(src) // update all UIs attached to src if(!icon_beaker) - icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position. + icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position. icon_beaker.pixel_x = rand(-10, 5) overlays += icon_beaker return @@ -299,8 +292,7 @@ to_chat(user, unhack_message) dispensable_reagents -= hacked_reagents hackedcheck = FALSE - SSnanoui.update_uis(src) - + SStgui.update_uis(src) /obj/machinery/chem_dispenser/screwdriver_act(mob/user, obj/item/I) if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I)) @@ -321,14 +313,14 @@ return attack_hand(user) /obj/machinery/chem_dispenser/attack_ghost(mob/user) - if(user.can_admin_interact()) - return attack_hand(user) + if(stat & BROKEN) + return + tgui_interact(user) /obj/machinery/chem_dispenser/attack_hand(mob/user) if(stat & BROKEN) return - - ui_interact(user) + tgui_interact(user) /obj/machinery/chem_dispenser/soda icon_state = "soda_dispenser" @@ -342,6 +334,7 @@ hacked_reagents = list("thirteenloko") hack_message = "You change the mode from 'McNano' to 'Pizza King'." unhack_message = "You change the mode from 'Pizza King' to 'McNano'." + is_drink = TRUE /obj/machinery/chem_dispenser/soda/New() ..() @@ -377,6 +370,7 @@ hacked_reagents = list("goldschlager", "patron", "absinthe", "ethanol", "nothing", "sake") hack_message = "You disable the 'nanotrasen-are-cheap-bastards' lock, enabling hidden and very expensive boozes." unhack_message = "You re-enable the 'nanotrasen-are-cheap-bastards' lock, disabling hidden and very expensive boozes." + is_drink = TRUE /obj/machinery/chem_dispenser/beer/New() ..() diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 39aa91099c0..8fddc97537f 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -1,15 +1,19 @@ /obj/machinery/chem_heater name = "chemical heater" - density = 1 - anchored = 1 + density = TRUE + anchored = TRUE icon = 'icons/obj/chemical.dmi' icon_state = "mixer0b" use_power = IDLE_POWER_USE idle_power_usage = 40 - resistance_flags = FIRE_PROOF | ACID_PROOF + resistance_flags = FIRE_PROOF|ACID_PROOF var/obj/item/reagent_containers/beaker = null var/desired_temp = T0C var/on = FALSE + /// Whether this should auto-eject the beaker once done heating/cooling. + var/auto_eject = FALSE + /// The higher this number, the faster reagents will heat/cool. + var/speed_increase = 0 /obj/machinery/chem_heater/New() ..() @@ -19,35 +23,36 @@ component_parts += new /obj/item/stack/sheet/glass(null) RefreshParts() +/obj/machinery/chem_heater/RefreshParts() + speed_increase = initial(speed_increase) + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + speed_increase += 5 * (M.rating - 1) + /obj/machinery/chem_heater/process() ..() - if(stat & NOPOWER) + if(stat & (NOPOWER|BROKEN)) return - var/state_change = FALSE if(on) if(beaker) if(!beaker.reagents.total_volume) on = FALSE - SSnanoui.update_uis(src) return - beaker.reagents.temperature_reagents(desired_temp) - beaker.reagents.temperature_reagents(desired_temp) - if(abs(beaker.reagents.chem_temp - desired_temp) <= 3) + beaker.reagents.temperature_reagents(desired_temp, max(1, 35 - speed_increase)) + if(round(beaker.reagents.chem_temp) == round(desired_temp)) + playsound(loc, 'sound/machines/ding.ogg', 50, 1) on = FALSE - state_change = TRUE - - if(state_change) - SSnanoui.update_uis(src) + if(auto_eject) + eject_beaker() /obj/machinery/chem_heater/proc/eject_beaker(mob/user) if(beaker) beaker.forceMove(get_turf(src)) - if(Adjacent(user) && !issilicon(user)) + if(user && Adjacent(user) && !issilicon(user)) user.put_in_hands(beaker) beaker = null icon_state = "mixer0b" on = FALSE - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/chem_heater/power_change() if(powered()) @@ -55,7 +60,6 @@ else spawn(rand(0, 15)) stat |= NOPOWER - SSnanoui.update_uis(src) /obj/machinery/chem_heater/attackby(obj/item/I, mob/user) if(isrobot(user)) @@ -71,7 +75,7 @@ I.forceMove(src) to_chat(user, "You add the beaker to the machine!") icon_state = "mixer1b" - SSnanoui.update_uis(src) + SStgui.update_uis(src) return if(exchange_parts(user, I)) @@ -95,62 +99,62 @@ default_deconstruction_crowbar(user, I) /obj/machinery/chem_heater/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/chem_heater/attack_ghost(mob/user) - if(user.can_admin_interact()) - return attack_hand(user) + tgui_interact(user) /obj/machinery/chem_heater/attack_ai(mob/user) add_hiddenprint(user) return attack_hand(user) -/obj/machinery/chem_heater/Topic(href, href_list) +/obj/machinery/chem_heater/tgui_act(action, params) if(..()) - return FALSE + return + if(stat & (NOPOWER|BROKEN)) + return - if(href_list["toggle_on"]) - if(!beaker.reagents.total_volume) - return FALSE - on = !on - . = 1 - - if(href_list["adjust_temperature"]) - var/val = href_list["adjust_temperature"] - if(isnum(val)) - desired_temp = clamp(desired_temp+val, 0, 1000) - else if(val == "input") - var/target = input("Please input the target temperature", name) as num - desired_temp = clamp(target, 0, 1000) + . = TRUE + switch(action) + if("toggle_on") + on = !on + if("adjust_temperature") + desired_temp = clamp(text2num(params["target"]), 0, 1000) + if("eject_beaker") + eject_beaker(usr) + . = FALSE + if("toggle_autoeject") + auto_eject = !auto_eject else return FALSE - . = 1 + add_fingerprint(usr) - if(href_list["eject_beaker"]) - eject_beaker(usr) - . = 0 //updated in eject_beaker() already - -/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null) +/obj/machinery/chem_heater/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) if(user.stat || user.restrained()) 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) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "chem_heater.tmpl", "ChemHeater", 350, 270) + ui = new(user, src, ui_key, "ChemHeater", "Chemical Heater", 350, 270, master_ui, state) ui.open() -/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/chem_heater/tgui_data(mob/user) var/data[0] + var/cur_temp = beaker ? beaker.reagents.chem_temp : null data["targetTemp"] = desired_temp + data["targetTempReached"] = FALSE + data["autoEject"] = auto_eject data["isActive"] = on - data["isBeakerLoaded"] = beaker ? 1 : 0 + data["isBeakerLoaded"] = beaker ? TRUE : FALSE - data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null + data["currentTemp"] = cur_temp data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null data["beakerMaxVolume"] = beaker ? beaker.volume : null + if(cur_temp) + data["targetTempReached"] = round(cur_temp) == round(desired_temp) + //copy-pasted from chem dispenser var/beakerContents[0] if(beaker) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 4d1f577969c..f2b3247fe4b 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -1,3 +1,9 @@ +#define MAX_PILL_SPRITE 20 //max icon state of the pill sprites +#define MAX_MULTI_AMOUNT 20 // Max number of pills/patches that can be made at once +#define MAX_UNITS_PER_PILL 100 // Max amount of units in a pill +#define MAX_UNITS_PER_PATCH 30 // Max amount of units in a patch +#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever + /obj/machinery/chem_master name = "\improper ChemMaster 3000" density = TRUE @@ -14,10 +20,12 @@ var/useramount = 30 // Last used amount var/pillamount = 10 var/patchamount = 10 - var/bottlesprite = "bottle" - var/pillsprite = "1" + var/bottlesprite = 1 + var/pillsprite = 1 var/client/has_sprites = list() var/printing = FALSE + var/static/list/pill_bottle_wrappers + var/static/list/bottle_styles /obj/machinery/chem_master/New() ..() @@ -94,7 +102,7 @@ beaker = I I.forceMove(src) to_chat(user, "You add the beaker to the machine!") - SSnanoui.update_uis(src) + SStgui.update_uis(src) update_icon() else if(istype(I, /obj/item/storage/pill_bottle)) @@ -109,14 +117,10 @@ loaded_pill_bottle = I I.forceMove(src) to_chat(user, "You add [I] into the dispenser slot!") - SSnanoui.update_uis(src) + SStgui.update_uis(src) else return ..() - - - - /obj/machinery/chem_master/crowbar_act(mob/user, obj/item/I) if(!panel_open) return @@ -142,319 +146,127 @@ power_change() return TRUE -/obj/machinery/chem_master/Topic(href, href_list) +/obj/machinery/chem_master/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) if(..()) + return + if(stat & (NOPOWER|BROKEN)) + return + + if(tgui_act_modal(action, params, ui, state)) return TRUE add_fingerprint(usr) usr.set_machine(src) - - if(href_list["ejectp"]) - if(loaded_pill_bottle) - loaded_pill_bottle.forceMove(loc) - loaded_pill_bottle = null - else if(href_list["change_pillbottle"]) - if(loaded_pill_bottle) - var/list/wrappers = list("Default wrapper", "Red wrapper", "Green wrapper", "Pale green wrapper", "Blue wrapper", "Light blue wrapper", "Teal wrapper", "Yellow wrapper", "Orange wrapper", "Pink wrapper", "Brown wrapper") - var/chosen = input(usr, "Select a pillbottle wrapper", "Pillbottle wrapper", wrappers[1]) as null|anything in wrappers - if(!chosen) + . = TRUE + switch(action) + if("toggle") + mode = !mode + if("ejectp") + if(loaded_pill_bottle) + loaded_pill_bottle.forceMove(loc) + loaded_pill_bottle = null + if("print") + if(printing || condi) return - var/color - switch(chosen) - if("Default wrapper") - loaded_pill_bottle.cut_overlays() - return - if("Red wrapper") - color = COLOR_RED - if("Green wrapper") - color = COLOR_GREEN - if("Pink wrapper") - color = COLOR_PINK - if("Teal wrapper") - color = COLOR_TEAL - if("Blue wrapper") - color = COLOR_BLUE - if("Brown wrapper") - color = COLOR_MAROON - if("Light blue wrapper") - color = COLOR_CYAN_BLUE - if("Yellow wrapper") - color = COLOR_YELLOW - if("Pale green wrapper") - color = COLOR_PALE_BTL_GREEN - if("Orange wrapper") - color = COLOR_ORANGE - loaded_pill_bottle.wrapper_color = color - loaded_pill_bottle.apply_wrap() - else if(href_list["close"]) - usr << browse(null, "window=chem_master") - onclose(usr, "chem_master") - usr.unset_machine() - return - if(href_list["print_p"]) - if(!printing) + var/idx = text2num(params["idx"]) || 0 + var/from_beaker = text2num(params["beaker"]) || FALSE + var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list + if(idx < 1 || idx > length(reagent_list)) + return + + var/datum/reagent/R = reagent_list[idx] + printing = TRUE visible_message("[src] rattles and prints out a sheet of paper.") playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Chemical Analysis

    " + P.info = "
    Chemical Analysis

    " P.info += "Time of analysis: [station_time_timestamp()]

    " - P.info += "Chemical name: [href_list["name"]]
    " - if(href_list["name"] == "Blood") - var/datum/reagents/R = beaker.reagents - var/datum/reagent/blood/G - for(var/datum/reagent/F in R.reagent_list) - if(F.name == href_list["name"]) - G = F - break - var/B = G.data["blood_type"] - var/C = G.data["blood_DNA"] - P.info += "Description:
    Blood Type: [B]
    DNA: [C]" + P.info += "Chemical name: [R.name]
    " + if(istype(R, /datum/reagent/blood)) + var/datum/reagent/blood/B = R + P.info += "Description: N/A
    Blood Type: [B.data["blood_type"]]
    DNA: [B.data["blood_DNA"]]" else - P.info += "Description: [href_list["desc"]]" + P.info += "Description: [R.description]" P.info += "

    Notes:
    " - P.name = "Chemical Analysis - [href_list["name"]]" - printing = FALSE + P.name = "Chemical Analysis - [R.name]" + spawn(50) + printing = FALSE + else + . = FALSE - if(beaker) - var/datum/reagents/R = beaker.reagents - if(href_list["analyze"]) - var/dat = "" - if(!condi) - if(href_list["name"] == "Blood") - var/datum/reagent/blood/G - for(var/datum/reagent/F in R.reagent_list) - if(F.name == href_list["name"]) - G = F - break - var/A = G.name - var/B = G.data["blood_type"] - var/C = G.data["blood_DNA"] - dat += "Chemmaster 3000Chemical infos:

    Name:
    [A]

    Description:
    Blood Type: [B]
    DNA: [C]" - else - dat += "Chemmaster 3000Chemical infos:

    Name:
    [href_list["name"]]

    Description:
    [href_list["desc"]]" - dat += "

    (Print Analysis)
    " - dat += "(Back)" + if(. || !beaker) + return + + . = TRUE + var/datum/reagents/R = beaker.reagents + switch(action) + if("add") + var/id = params["id"] + var/amount = text2num(params["amount"]) + if(!id || !amount) + return + R.trans_id_to(src, id, amount) + if("remove") + var/id = params["id"] + var/amount = text2num(params["amount"]) + if(!id || !amount) + return + if(mode) + reagents.trans_id_to(beaker, id, amount) else - dat += "Condimaster 3000Condiment infos:

    Name:
    [href_list["name"]]

    Description:
    [href_list["desc"]]


    (Back)" - usr << browse(dat, "window=chem_master;size=575x500") - return - - else if(href_list["add"]) - - if(href_list["amount"]) - var/id = href_list["add"] - var/amount = text2num(href_list["amount"]) - R.trans_id_to(src, id, amount) - - else if(href_list["addcustom"]) - - var/id = href_list["addcustom"] - useramount = input("Select the amount to transfer.", 30, useramount) as num - useramount = isgoodnumber(useramount) - Topic(null, list("amount" = "[useramount]", "add" = "[id]")) - - else if(href_list["remove"]) - - if(href_list["amount"]) - var/id = href_list["remove"] - var/amount = text2num(href_list["amount"]) - if(mode) - reagents.trans_id_to(beaker, id, amount) - else - reagents.remove_reagent(id, amount) - - - else if(href_list["removecustom"]) - - var/id = href_list["removecustom"] - useramount = input("Select the amount to transfer.", 30, useramount) as num - useramount = isgoodnumber(useramount) - Topic(null, list("amount" = "[useramount]", "remove" = "[id]")) - - else if(href_list["toggle"]) - mode = !mode - - else if(href_list["main"]) - attack_hand(usr) - return - else if(href_list["eject"]) - if(beaker) - beaker.forceMove(get_turf(src)) - if(Adjacent(usr) && !issilicon(usr)) - usr.put_in_hands(beaker) - beaker = null - reagents.clear_reagents() - update_icon() - else if(href_list["createpill"] || href_list["createpill_multiple"]) - if(!condi) - var/count = 1 - if(href_list["createpill_multiple"]) - count = input("Select the number of pills to make.", 10, pillamount) as num|null - if(count == null) - return - count = isgoodnumber(count) - if(count > 20) - count = 20 //Pevent people from creating huge stacks of pills easily. Maybe move the number to defines? - if(count <= 0) - return - var/amount_per_pill = reagents.total_volume / count - if(amount_per_pill > 100) - amount_per_pill = 100 - var/name = clean_input("Name:","Name your pill!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)") - if(!name) - return - name = reject_bad_text(name) - while(count--) - if(reagents.total_volume <= 0) - to_chat(usr, "Not enough reagents to create these pills!") - return - var/obj/item/reagent_containers/food/pill/P = new/obj/item/reagent_containers/food/pill(loc) - if(!name) - name = reagents.get_master_reagent_name() - P.name = "[name] pill" - P.pixel_x = rand(-7, 7) //random position - P.pixel_y = rand(-7, 7) - P.icon_state = "pill"+pillsprite - reagents.trans_to(P, amount_per_pill) - if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle) - if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots) - P.forceMove(loaded_pill_bottle) - updateUsrDialog() - else - var/name = clean_input("Name:", "Name your bag!", reagents.get_master_reagent_name()) - if(!name) - return - name = reject_bad_text(name) - var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(loc) - if(!name) name = reagents.get_master_reagent_name() - P.originalname = name - P.name = "[name] pack" - P.desc = "A small condiment pack. The label says it contains [name]." - reagents.trans_to(P, 10) - else if(href_list["createpatch"] || href_list["createpatch_multiple"]) - if(!condi) - var/count = 1 - if(href_list["createpatch_multiple"]) - count = input("Select the number of patches to make.", 10, patchamount) as num|null - if(count == null) - return - count = isgoodnumber(count) - if(!count || count <= 0) - return - if(count > 20) - count = 20 //Pevent people from creating huge stacks of patches easily. Maybe move the number to defines? - var/amount_per_patch = reagents.total_volume/count - if(amount_per_patch > 30) - amount_per_patch = 30 - var/name = clean_input("Name:", "Name your patch!", "[reagents.get_master_reagent_name()] ([amount_per_patch]u)") - if(!name) - return - name = reject_bad_text(name) - var/is_medical_patch = chemical_safety_check(reagents) - while(count--) - var/obj/item/reagent_containers/food/pill/patch/P = new/obj/item/reagent_containers/food/pill/patch(loc) - if(!name) name = reagents.get_master_reagent_name() - P.name = "[name] patch" - P.pixel_x = rand(-7, 7) //random position - P.pixel_y = rand(-7, 7) - reagents.trans_to(P,amount_per_patch) - if(is_medical_patch) - P.instant_application = TRUE - P.icon_state = "bandaid_med" - if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle/patch_pack) - if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots) - P.forceMove(loaded_pill_bottle) - updateUsrDialog() - - else if(href_list["createbottle"]) - if(!condi) - var/name = clean_input("Name:", "Name your bottle!", reagents.get_master_reagent_name()) - if(!name) - return - name = reject_bad_text(name) - var/obj/item/reagent_containers/glass/bottle/reagent/P = new/obj/item/reagent_containers/glass/bottle/reagent(loc) - if(!name) - name = reagents.get_master_reagent_name() - P.name = "[name] bottle" - P.pixel_x = rand(-7, 7) //random position - P.pixel_y = rand(-7, 7) - P.icon_state = bottlesprite - reagents.trans_to(P, 50) - else - var/obj/item/reagent_containers/food/condiment/P = new/obj/item/reagent_containers/food/condiment(loc) - reagents.trans_to(P, 50) - else if(href_list["change_pill"]) - #define MAX_PILL_SPRITE 20 //max icon state of the pill sprites - var/dat = "" - var/j = 0 - for(var/i = 1 to MAX_PILL_SPRITE) - j++ - if(j == 1) - dat += "" - dat += "" - if(j == 5) - dat += "" - j = 0 - dat += "
    " - usr << browse(dat, "window=chem_master_iconsel;size=225x193") - return - else if(href_list["change_bottle"]) - var/dat = "" - var/j = 0 - for(var/i in list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle")) - j++ - if(j == 1) - dat += "" - dat += "" - if(j == 5) - dat += "" - j = 0 - dat += "
    " - usr << browse(dat, "window=chem_master_iconsel;size=225x193") - return - else if(href_list["pill_sprite"]) - pillsprite = href_list["pill_sprite"] - usr << browse(null, "window=chem_master_iconsel") - else if(href_list["bottle_sprite"]) - bottlesprite = href_list["bottle_sprite"] - usr << browse(null, "window=chem_master_iconsel") - - SSnanoui.update_uis(src) + reagents.remove_reagent(id, amount) + if("eject") + if(!beaker) + return + beaker.forceMove(get_turf(src)) + if(Adjacent(usr) && !issilicon(usr)) + usr.put_in_hands(beaker) + beaker = null + reagents.clear_reagents() + update_icon() + if("create_condi_bottle") + if(!condi || !reagents.total_volume) + return + var/obj/item/reagent_containers/food/condiment/P = new(loc) + reagents.trans_to(P, 50) + else + return FALSE /obj/machinery/chem_master/attack_ai(mob/user) return attack_hand(user) /obj/machinery/chem_master/attack_ghost(mob/user) - if(user.can_admin_interact()) - return attack_hand(user) + tgui_interact(user) /obj/machinery/chem_master/attack_hand(mob/user) if(..()) return TRUE - ui_interact(user) + tgui_interact(user) -/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE) +/obj/machinery/chem_master/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) var/datum/asset/chem_master/assets = get_asset_datum(/datum/asset/chem_master) assets.send(user) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "chem_master.tmpl", name, 575, 500) + ui = new(user, src, ui_key, "ChemMaster", name, 575, 500) ui.open() -/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/chem_master/tgui_data(mob/user) var/data[0] data["condi"] = condi - data["loaded_pill_bottle"] = (loaded_pill_bottle ? 1 : 0) + data["loaded_pill_bottle"] = loaded_pill_bottle ? TRUE : FALSE if(loaded_pill_bottle) + data["loaded_pill_bottle_name"] = loaded_pill_bottle.name data["loaded_pill_bottle_contents_len"] = loaded_pill_bottle.contents.len data["loaded_pill_bottle_storage_slots"] = loaded_pill_bottle.storage_slots - data["beaker"] = (beaker ? 1 : 0) + data["beaker"] = beaker ? TRUE : FALSE if(beaker) var/list/beaker_reagents_list = list() data["beaker_reagents"] = beaker_reagents_list @@ -464,13 +276,255 @@ data["buffer_reagents"] = buffer_reagents_list for(var/datum/reagent/R in reagents.reagent_list) buffer_reagents_list[++buffer_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "id" = R.id, "description" = R.description) + else + data["beaker_reagents"] = list() + data["buffer_reagents"] = list() data["pillsprite"] = pillsprite data["bottlesprite"] = bottlesprite data["mode"] = mode + data["printing"] = printing + + // Transfer modal information if there is one + data["modal"] = tgui_modal_data(src) return data +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/chem_master/proc/tgui_act_modal(action, params, datum/tgui/ui, datum/tgui_state/state) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("analyze") + var/idx = text2num(arguments["idx"]) || 0 + var/from_beaker = text2num(arguments["beaker"]) || FALSE + var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list + if(idx < 1 || idx > length(reagent_list)) + return + + var/datum/reagent/R = reagent_list[idx] + var/list/result = list("idx" = idx, "name" = R.name, "desc" = R.description) + if(!condi && istype(R, /datum/reagent/blood)) + var/datum/reagent/blood/B = R + result["blood_type"] = B.data["blood_type"] + result["blood_dna"] = B.data["blood_DNA"] + + arguments["analysis"] = result + tgui_modal_message(src, id, "", null, arguments) + if("change_pill_bottle_style") + if(!loaded_pill_bottle) + return + if(!pill_bottle_wrappers) + pill_bottle_wrappers = list( + "CLEAR" = "Default", + COLOR_RED = "Red", + COLOR_GREEN = "Green", + COLOR_PALE_BTL_GREEN = "Pale green", + COLOR_BLUE = "Blue", + COLOR_CYAN_BLUE = "Light blue", + COLOR_TEAL = "Teal", + COLOR_YELLOW = "Yellow", + COLOR_ORANGE = "Orange", + COLOR_PINK = "Pink", + COLOR_MAROON = "Brown" + ) + var/current = pill_bottle_wrappers[loaded_pill_bottle.wrapper_color] || "Default" + tgui_modal_choice(src, id, "Please select a pill bottle wrapper:", null, arguments, current, pill_bottle_wrappers) + if("addcustom") + if(!beaker || !beaker.reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount to transfer to buffer:", null, arguments, useramount) + if("removecustom") + if(!reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount to transfer to [mode ? "beaker" : "disposal"]:", null, arguments, useramount) + if("create_condi_pack") + if(!condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please name your new condiment pack:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN) + if("create_pill") + if(condi || !reagents.total_volume) + return + var/num = round(text2num(arguments["num"] || 1)) + if(!num) + return + arguments["num"] = num + var/amount_per_pill = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PILL) + var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_pill]u)" + var/pills_text = num == 1 ? "new pill" : "[num] new pills" + tgui_modal_input(src, id, "Please name your [pills_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN) + if("create_pill_multiple") + if(condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount of pills to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5) + if("change_pill_style") + var/list/choices = list() + for(var/i = 1 to MAX_PILL_SPRITE) + choices += "pill[i].png" + tgui_modal_bento(src, id, "Please select the new style for pills:", null, arguments, pillsprite, choices) + if("create_patch") + if(condi || !reagents.total_volume) + return + var/num = round(text2num(arguments["num"] || 1)) + if(!num) + return + arguments["num"] = num + var/amount_per_patch = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PATCH) + var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_patch]u)" + var/patches_text = num == 1 ? "new patch" : "[num] new patches" + tgui_modal_input(src, id, "Please name your [patches_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN) + if("create_patch_multiple") + if(condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount of patches to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5) + if("create_bottle") + if(condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please name your bottle:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN) + if("change_bottle_style") + if(!bottle_styles) + bottle_styles = list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle") + var/list/bottle_styles_png = list() + for(var/style in bottle_styles) + bottle_styles_png += "[style].png" + tgui_modal_bento(src, id, "Please select the new style for bottles:", null, arguments, bottlesprite, bottle_styles_png) + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("change_pill_bottle_style") + if(!pill_bottle_wrappers || !loaded_pill_bottle) // wat? + return + var/color = "CLEAR" + for(var/col in pill_bottle_wrappers) + var/col_name = pill_bottle_wrappers[col] + if(col_name == answer) + color = col + break + if(length(color) && color != "CLEAR") + loaded_pill_bottle.wrapper_color = color + loaded_pill_bottle.apply_wrap() + else + loaded_pill_bottle.wrapper_color = null + loaded_pill_bottle.cut_overlays() + if("addcustom") + var/amount = isgoodnumber(text2num(answer)) + if(!amount || !arguments["id"]) + return + tgui_act("add", list("id" = arguments["id"], "amount" = amount), ui, state) + if("removecustom") + var/amount = isgoodnumber(text2num(answer)) + if(!amount || !arguments["id"]) + return + tgui_act("remove", list("id" = arguments["id"], "amount" = amount), ui, state) + if("create_condi_pack") + if(!condi || !reagents.total_volume) + return + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/obj/item/reagent_containers/food/condiment/pack/P = new(loc) + P.originalname = answer + P.name = "[answer] pack" + P.desc = "A small condiment pack. The label says it contains [answer]." + reagents.trans_to(P, 10) + if("create_pill") + if(condi || !reagents.total_volume) + return + var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT) + if(!count) + return + + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/amount_per_pill = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PILL) + while(count--) + if(reagents.total_volume <= 0) + to_chat(usr, "Not enough reagents to create these pills!") + return + + var/obj/item/reagent_containers/food/pill/P = new(loc) + P.name = "[answer] pill" + P.pixel_x = rand(-7, 7) // Random position + P.pixel_y = rand(-7, 7) + P.icon_state = "pill[pillsprite]" + reagents.trans_to(P, amount_per_pill) + // Load the pills in the bottle if there's one loaded + if(istype(loaded_pill_bottle) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots) + P.forceMove(loaded_pill_bottle) + if("create_pill_multiple") + if(condi || !reagents.total_volume) + return + tgui_act("modal_open", list("id" = "create_pill", "arguments" = list("num" = answer)), ui, state) + if("change_pill_style") + var/new_style = clamp(text2num(answer) || 0, 0, MAX_PILL_SPRITE) + if(!new_style) + return + pillsprite = new_style + if("create_patch") + if(condi || !reagents.total_volume) + return + var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT) + if(!count) + return + + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/amount_per_patch = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PATCH) + var/is_medical_patch = chemical_safety_check(reagents) + while(count--) + if(reagents.total_volume <= 0) + to_chat(usr, "Not enough reagents to create these patches!") + return + + var/obj/item/reagent_containers/food/pill/patch/P = new(loc) + P.name = "[answer] patch" + P.pixel_x = rand(-7, 7) // random position + P.pixel_y = rand(-7, 7) + reagents.trans_to(P, amount_per_patch) + if(is_medical_patch) + P.instant_application = TRUE + P.icon_state = "bandaid_med" + // Load the patches in the bottle if there's one loaded + if(istype(loaded_pill_bottle, /obj/item/storage/pill_bottle/patch_pack) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots) + P.forceMove(loaded_pill_bottle) + if("create_patch_multiple") + if(condi || !reagents.total_volume) + return + tgui_act("modal_open", list("id" = "create_patch", "arguments" = list("num" = answer)), ui, state) + if("create_bottle") + if(condi || !reagents.total_volume) + return + + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/obj/item/reagent_containers/glass/bottle/reagent/P = new(loc) + P.name = "[answer] bottle" + P.pixel_x = rand(-7, 7) // random position + P.pixel_y = rand(-7, 7) + P.icon_state = length(bottle_styles) && bottle_styles[bottlesprite] || "bottle" + reagents.trans_to(P, 50) + if("change_bottle_style") + if(!bottle_styles) + return + var/new_sprite = text2num(answer) || 1 + if(new_sprite < 1 || new_sprite > length(bottle_styles)) + return + bottlesprite = new_sprite + else + return FALSE + else + return FALSE + /obj/machinery/chem_master/proc/isgoodnumber(num) if(isnum(num)) if(num > 200) @@ -481,7 +535,7 @@ num = round(num) return num else - return 0 + return FALSE /obj/machinery/chem_master/proc/chemical_safety_check(datum/reagents/R) var/all_safe = TRUE @@ -503,3 +557,9 @@ component_parts += new /obj/item/reagent_containers/glass/beaker(null) component_parts += new /obj/item/reagent_containers/glass/beaker(null) RefreshParts() + +#undef MAX_PILL_SPRITE +#undef MAX_MULTI_AMOUNT +#undef MAX_UNITS_PER_PILL +#undef MAX_UNITS_PER_PATCH +#undef MAX_CUSTOM_NAME_LEN diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index c6ff39aaead..b015b75be2a 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -1237,6 +1237,7 @@ description = "An advanced corruptive toxin produced by something terrible." reagent_state = LIQUID color = "#5EFF3B" //RGB: 94, 255, 59 + can_synth = FALSE taste_description = "decay" /datum/reagent/gluttonytoxin/reaction_mob(mob/living/L, method=REAGENT_TOUCH, reac_volume) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index ecf6b9887fc..e678ec43398 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -161,13 +161,13 @@ amount = 25 max_amount = 25 resistance_flags = FLAMMABLE + var/static/list/no_wrap = list(/obj/item/smallDelivery, /obj/structure/bigDelivery, /obj/item/evidencebag, /obj/structure/closet/body_bag, /obj/item/twohanded/required) /obj/item/stack/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity) if(!proximity) return if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete return - if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ - || istype(target, /obj/item/evidencebag) || istype(target, /obj/structure/closet/body_bag)) + if(is_type_in_list(target, no_wrap)) return if(target.anchored) return 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/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 7c35730f4aa..ac7249a8b63 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -198,7 +198,7 @@ being_used = 1 var/ghostmsg = "Play as [SM.name], pet of [user.name]?" - var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 0, 100) + var/list/candidates = SSghost_spawns.poll_candidates(ghostmsg, ROLE_SENTIENT, FALSE, 10 SECONDS, source = M) if(!src) return diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm index a25be3814a7..68ed3cb7749 100644 --- a/code/modules/response_team/ert.dm +++ b/code/modules/response_team/ert.dm @@ -63,7 +63,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) GLOB.active_team.setSlots(commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) GLOB.send_emergency_team = TRUE - var/list/ert_candidates = shuffle(pollCandidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 600, 1, GLOB.role_playtime_requirements[ROLE_ERT])) + var/list/ert_candidates = shuffle(SSghost_spawns.poll_candidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_ERT])) if(!ert_candidates.len) GLOB.active_team.cannot_send_team() GLOB.send_emergency_team = FALSE @@ -318,3 +318,4 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) name = "centcomm bounced radio" frequency = ERT_FREQ icon_state = "radio" + freqlock = TRUE diff --git a/code/modules/shuttle/ert.dm b/code/modules/shuttle/ert.dm index 5168ce89278..ddb7bb343b6 100644 --- a/code/modules/shuttle/ert.dm +++ b/code/modules/shuttle/ert.dm @@ -6,13 +6,12 @@ resistance_flags = INDESTRUCTIBLE flags = NODECONSTRUCT -/obj/machinery/computer/shuttle/ert/Topic(href, href_list) - if(href_list["move"]) +/obj/machinery/computer/shuttle/ert/can_call_shuttle(mob/user, action) + if(action == "move") var/authorized_roles = list(SPECIAL_ROLE_ERT, SPECIAL_ROLE_DEATHSQUAD) - if(!((usr.mind?.assigned_role in authorized_roles) || is_admin(usr))) - message_admins("Potential ERT shuttle hijack, ERT shuttle moved by unauthorized user: [key_name_admin(usr)]") - ..() - + if(!((user.mind?.assigned_role in authorized_roles) || is_admin(user))) + message_admins("Potential ERT shuttle hijack, ERT shuttle moved by unauthorized user: [key_name_admin(user)]") + return TRUE /obj/machinery/computer/camera_advanced/shuttle_docker/ert name = "specops navigation computer" diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index cfd3ab8e6f2..86cfe2a7444 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -727,13 +727,13 @@ name = "Shuttle Console" icon_screen = "shuttle" icon_keyboard = "tech_key" - req_access = list( ) + req_access = list() circuit = /obj/item/circuitboard/shuttle var/shuttleId var/possible_destinations = "" var/admin_controlled var/max_connect_range = 7 - var/docking_request = 0 + var/moved = FALSE //workaround for nukie shuttle, hope I find a better way to do this... /obj/machinery/computer/shuttle/New(location, obj/item/circuitboard/shuttle/C) ..() @@ -773,21 +773,20 @@ return connect() add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/shuttle/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/shuttle/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, "shuttle_console.tmpl", M ? M.name : "shuttle", 300, 200) + ui = new(user, src, ui_key, "ShuttleConsole", name, 350, 150, master_ui, state) ui.open() -/obj/machinery/computer/shuttle/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/computer/shuttle/tgui_data(mob/user) + var/list/data = list() var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) data["status"] = M ? M.getStatusText() : null if(M) - data["shuttle"] = 1 + data["shuttle"] = TRUE //this should just be boolean, right? var/list/docking_ports = list() data["docking_ports"] = docking_ports var/list/options = params2list(possible_destinations) @@ -799,34 +798,35 @@ docking_ports[++docking_ports.len] = list("name" = S.name, "id" = S.id) data["docking_ports_len"] = docking_ports.len data["admin_controlled"] = admin_controlled - data["docking_request"] = docking_request - return data -/obj/machinery/computer/shuttle/Topic(href, href_list) - if(..()) - return 1 - +/obj/machinery/computer/shuttle/tgui_act(action, params) + if(..()) //we can't actually interact, so no action + return TRUE if(!allowed(usr)) to_chat(usr, "Access denied.") - return - + return TRUE + if(!can_call_shuttle(usr, action)) + return TRUE var/list/options = params2list(possible_destinations) - if(href_list["move"]) - if(!options.Find(href_list["move"])) //I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. - // Seriously, though, NEVER trust a Topic with something like this. Ever. - // Sidenote for whoever did this last. Why did you set it to echo whatever the user entered to admin chat? You solved one exploit and created another. + if(action == "move") + var/destination = params["move"] + if(!options.Find(destination))//figure out if this translation works message_admins("EXPLOIT: [ADMIN_LOOKUPFLW(usr)] attempted to move [src] to an invalid location! [ADMIN_COORDJMP(src)]") return - switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1, usr)) + switch(SSshuttle.moveShuttle(shuttleId, destination, TRUE, usr)) if(0) atom_say("Shuttle departing! Please stand away from the doors.") usr.create_log(MISC_LOG, "used [src] to call the [shuttleId] shuttle") + if(!moved) + moved = TRUE + add_fingerprint(usr) + return TRUE if(1) to_chat(usr, "Invalid shuttle requested.") else to_chat(usr, "Unable to comply.") - return 1 + /obj/machinery/computer/shuttle/emag_act(mob/user) if(!emagged) @@ -834,6 +834,10 @@ emagged = 1 to_chat(user, "You fried the consoles ID checking system.") +//for restricting when the computer can be used, needed for some console subtypes. +/obj/machinery/computer/shuttle/proc/can_call_shuttle(mob/user, action) + return TRUE + /obj/machinery/computer/shuttle/ferry name = "transport ferry console" circuit = /obj/item/circuitboard/ferry @@ -844,25 +848,23 @@ /obj/machinery/computer/shuttle/ferry/request name = "ferry console" circuit = /obj/item/circuitboard/ferry/request - var/cooldown //prevents spamming admins + var/next_request //to prevent spamming admins possible_destinations = "ferry_home" - admin_controlled = 1 + admin_controlled = TRUE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF -/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list) - if(..()) - return 1 - if(href_list["request"]) - if(cooldown) +/obj/machinery/computer/shuttle/ferry/request/tgui_act(action, params) + if(..()) // Note that the parent handels normal shuttle movement on top of security checks + return + if(action == "request") + if(world.time < next_request) return - cooldown = 1 + next_request = world.time + 60 SECONDS //1 minute cooldown to_chat(usr, "Your request has been recieved by Centcom.") log_admin("[key_name(usr)] requested to move the transport ferry to Centcom.") message_admins("FERRY: [key_name_admin(usr)] (Move Ferry) is requesting to move the transport ferry to Centcom.") - . = 1 - SSnanoui.update_uis(src) - spawn(600) //One minute cooldown - cooldown = 0 + return TRUE + /obj/machinery/computer/shuttle/white_ship name = "White Ship Console" diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm index f9ec738b167..1ff2959ff08 100644 --- a/code/modules/shuttle/syndicate.dm +++ b/code/modules/shuttle/syndicate.dm @@ -11,20 +11,18 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF flags = NODECONSTRUCT var/challenge = FALSE - var/moved = FALSE /obj/machinery/computer/shuttle/syndicate/recall name = "syndicate shuttle recall terminal" circuit = /obj/item/circuitboard/shuttle/syndicate/recall possible_destinations = "syndicate_away" -/obj/machinery/computer/shuttle/syndicate/Topic(href, href_list) - if(href_list["move"]) +/obj/machinery/computer/shuttle/syndicate/can_call_shuttle(user, action) + if(action == "move") if(challenge && world.time < SYNDICATE_CHALLENGE_TIMER) - to_chat(usr, "You've issued a combat challenge to the station! You've got to give them at least [round(((SYNDICATE_CHALLENGE_TIMER - world.time) / 10) / 60)] more minutes to allow them to prepare.") - return 0 - moved = TRUE - ..() + to_chat(user, "You've issued a combat challenge to the station! You've got to give them at least [round(((SYNDICATE_CHALLENGE_TIMER - world.time) / 10) / 60)] more minutes to allow them to prepare.") + return FALSE + return TRUE /obj/machinery/computer/shuttle/syndicate/drop_pod name = "syndicate assault pod control" @@ -35,11 +33,11 @@ shuttleId = "steel_rain" possible_destinations = null -/obj/machinery/computer/shuttle/syndicate/drop_pod/Topic(href, href_list) - if(href_list["move"]) +/obj/machinery/computer/shuttle/syndicate/drop_pod/can_call_shuttle(user, action) + if(action == "move") if(z != level_name_to_num(CENTCOMM)) - to_chat(usr, "Pods are one way!") - return 0 + to_chat(user, "Pods are one way!") + return FALSE ..() /obj/machinery/computer/shuttle/sst 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/helpers.dm b/code/modules/surgery/organs/helpers.dm index ca2698f2ad4..4102319a3fe 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -104,7 +104,7 @@ /mob/living/carbon/human/get_leg_ignore() - if(flying == 1) + if(flying || floating) return TRUE var/obj/item/tank/jetpack/J 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_icon.dm b/code/modules/surgery/organs/organ_icon.dm index 2a3b94ca249..b41ef824508 100644 --- a/code/modules/surgery/organs/organ_icon.dm +++ b/code/modules/surgery/organs/organ_icon.dm @@ -107,7 +107,10 @@ GLOBAL_LIST_EMPTY(limb_icon_cache) add_overlay(eyes_icon) if(owner.lip_style && (LIPS in dna.species.species_traits)) - add_overlay(mutable_appearance('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s")) //Hefty icon not necessary. + var/icon/lips_icon = new('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s") + lips_icon.Blend(owner.lip_color, ICON_MULTIPLY) + mob_icon.Blend(lips_icon, ICON_OVERLAY) + add_overlay(lips_icon) var/head_marking = owner.m_styles["head"] if(head_marking) 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/code/modules/tgui/modal.dm b/code/modules/tgui/modal.dm new file mode 100644 index 00000000000..f09f54f627c --- /dev/null +++ b/code/modules/tgui/modal.dm @@ -0,0 +1,370 @@ +/** + * tgui modals + * + * Allows creation of modals within tgui. + */ + +GLOBAL_LIST(tgui_modals) + +/** + * Call this from a proc that is called in tgui_act() to process modal actions + * + * Example: /obj/machinery/chem_master/proc/tgui_act_modal + * You can then switch based on the return value and show different + * modals depending on the answer. + * Arguments: + * * source - The source datum + * * action - The called action + * * params - The params to the action + */ +/datum/proc/tgui_modal_act(datum/source = src, action = "", params) + ASSERT(istype(source)) + + . = null + switch(action) + if("modal_open") // Params: id, arguments + return TGUI_MODAL_OPEN + if("modal_answer") // Params: id, answer, arguments + params["answer"] = tgui_modal_preprocess_answer(source, params["answer"]) + if(tgui_modal_answer(source, params["id"], params["answer"])) // If there's a current modal with a delegate that returned TRUE, no need to continue + . = TGUI_MODAL_DELEGATE + else + . = TGUI_MODAL_ANSWER + tgui_modal_clear(source) + if("modal_close") // Params: id + tgui_modal_clear(source) + return TGUI_MODAL_CLOSE + +/** + * Call this from tgui_data() to return modal information if needed + + * Arguments: + * * source - The source datum + */ +/datum/proc/tgui_modal_data(datum/source = src) + ASSERT(istype(source)) + + var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(!current) + return null + + return current.to_data() + +/** + * Clears the current modal for a given datum + * + * Arguments: + * * source - The source datum + */ +/datum/proc/tgui_modal_clear(datum/source = src) + ASSERT(istype(source)) + + LAZYINITLIST(GLOB.tgui_modals) + var/datum/tgui_modal/previous = GLOB.tgui_modals[source.UID()] + if(!previous) + return FALSE + + for(var/i in 1 to length(GLOB.tgui_modals)) + var/key = GLOB.tgui_modals[i] + if(previous == GLOB.tgui_modals[key]) + GLOB.tgui_modals.Cut(i, i + 1) + break + + SStgui.update_uis(source) + return TRUE + +/** + * Opens a message TGUI modal + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when closed + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + */ +/datum/proc/tgui_modal_message(datum/source = src, id, text = "Default modal message", delegate, arguments) + ASSERT(length(id)) + + var/datum/tgui_modal/modal = new(id, text, delegate, arguments) + return tgui_modal_new(source, modal) + +/** + * Opens a text input TGUI modal + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when submitted + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * value - The default value of the input + * * max_length - The maximum char length of the input + */ +/datum/proc/tgui_modal_input(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", max_length = TGUI_MODAL_INPUT_MAX_LENGTH) + ASSERT(length(id)) + ASSERT(max_length > 0) + + var/datum/tgui_modal/input/modal = new(id, text, delegate, arguments, value, max_length) + return tgui_modal_new(source, modal) + +/** + * Opens a dropdown input TGUI modal + * + * Internally checks if the answer is in the list of choices. + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when submitted + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * value - The default value of the dropdown + * * choices - The list of available choices in the dropdown + */ +/datum/proc/tgui_modal_choice(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", choices) + ASSERT(length(id)) + + var/datum/tgui_modal/input/choice/modal = new(id, text, delegate, arguments, value, choices) + return tgui_modal_new(source, modal) + +/** + * Opens a bento input TGUI modal + * + * Internally checks if the answer is in the list of choices. + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when submitted + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * value - The default value of the bento + * * choices - The list of available choices in the bento + */ +/datum/proc/tgui_modal_bento(datum/source = src, id, text = "Default modal message", delegate, arguments, value, choices) + ASSERT(length(id)) + + var/datum/tgui_modal/input/bento/modal = new(id, text, delegate, arguments, value, choices) + return tgui_modal_new(source, modal) + +/** + * Opens a yes/no TGUI modal + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when "Yes" is pressed + * * delegate_no - The proc to call when "No" is pressed + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * yes_text - The text to show in the "Yes" button + * * no_text - The text to show in the "No" button + */ +/datum/proc/tgui_modal_boolean(datum/source = src, id, text = "Default modal message", delegate, delegate_no, arguments, yes_text = "Yes", no_text = "No") + ASSERT(length(id)) + + var/datum/tgui_modal/boolean/modal = new(id, text, delegate, delegate_no, arguments, yes_text, no_text) + return tgui_modal_new(source, modal) + +/** + * Registers a given modal to a source. Private. + * + * Arguments: + * * source - The source datum + * * modal - The datum/tgui_modal to register + * * replace_previous - Whether any modal currently assigned to source should be replaced + * * instant_update - Whether the changes should reflect immediately + */ +/datum/proc/tgui_modal_new(datum/source = src, datum/tgui_modal/modal = null, replace_previous = TRUE, instant_update = TRUE) + ASSERT(istype(source)) + ASSERT(istype(modal)) + + var/datum/tgui_modal/previous = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(previous && !replace_previous) + return FALSE + + modal.owning_source = source + + // Previous one should get GC'd + LAZYSET(GLOB.tgui_modals, source.UID(), modal) + if(instant_update) + SStgui.update_uis(source) + return TRUE + +/** + * Calls the source's currently assigned modal's (if there is one) on_answer() proc. Private. + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * answer - The provided answer + */ +/datum/proc/tgui_modal_answer(datum/source = src, id, answer = "") + ASSERT(istype(source)) + + var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(!current) + return FALSE + + return current.on_answer(answer) + +/** + * Passes an answer from JS through the modal's proc. + * + * Used namely for cutting the text short if it's longer + * than an input modal's max_length. + * Arguments: + * * source - The source datum + * * answer - The provided answer + */ +/datum/proc/tgui_modal_preprocess_answer(datum/source = src, answer = "") + ASSERT(istype(source)) + + var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(!current) + return answer + + return current.preprocess_answer(answer) + +/** + * Modal datum (contains base information for a modal) + */ +/datum/tgui_modal + var/datum/owning_source + var/id + var/text + var/delegate + var/list/arguments + var/modal_type = "message" + +/datum/tgui_modal/New(id, text, delegate, list/arguments) + src.id = id + src.text = text + src.delegate = delegate + src.arguments = arguments + +/** + * Called when it's time to pre-process the answer before using it + * + * Arguments: + * * answer - The answer, a nullable text + */ +/datum/tgui_modal/proc/preprocess_answer(answer) + return reject_bad_text(answer, TGUI_MODAL_INPUT_MAX_LENGTH) // bleh + +/** + * Called when a modal receives an answer + * + * Arguments: + * * answer - The answer, a nullable text + */ +/datum/tgui_modal/proc/on_answer(answer) + if(delegate) + return call(owning_source, delegate)(answer, arguments) + return FALSE + +/** + * Creates a list that describes a modal visually to be passed to JS + */ +/datum/tgui_modal/proc/to_data() + . = list() + .["id"] = id + .["text"] = text + .["args"] = arguments || list() + .["type"] = modal_type + +/** + * Input modal - has a text entry that can be used to enter an answer + */ +/datum/tgui_modal/input + modal_type = "input" + var/value + var/max_length + +/datum/tgui_modal/input/New(id, text, delegate, list/arguments, value, max_length) + ..(id, text, delegate, arguments) + src.value = value + src.max_length = max_length + +/datum/tgui_modal/input/preprocess_answer(answer) + . = ..(answer) + if(length(answer) > max_length) + . = copytext(., 1, max_length + 1) + +/datum/tgui_modal/input/to_data() + . = ..() + .["value"] = value + +/** + * Choice modal - has a dropdown menu that can be used to select an answer + */ +/datum/tgui_modal/input/choice + modal_type = "choice" + var/choices + +/datum/tgui_modal/input/choice/New(id, text, delegate, list/arguments, value, choices) + ..(id, text, delegate, arguments, value, TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in dropdowns, but whatever + src.choices = choices + +/datum/tgui_modal/input/choice/on_answer(answer) + if(answer in choices) // Make sure the answer is actually in our choices! + return ..(answer, arguments) + return FALSE + +/datum/tgui_modal/input/choice/to_data() + . = ..() + .["choices"] = choices + +/** + * Bento modal - Similar to choice, it displays the choices in a grid of images + * + * The returned answer is the index of the choice. + */ +/datum/tgui_modal/input/bento + modal_type = "bento" + var/choices + +/datum/tgui_modal/input/bento/New(id, text, delegate, list/arguments, value, choices) + ..(id, text, delegate, arguments, text2num(value), TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in here, but whatever + src.choices = choices + +/datum/tgui_modal/input/bento/preprocess_answer(answer) + return text2num(answer) || 0 + +/datum/tgui_modal/input/bento/on_answer(answer) + if(answer >= 1 && answer <= length(choices)) // Make sure the answer index is actually in our indexes! + return ..(answer, arguments) + return FALSE + +/datum/tgui_modal/input/bento/to_data() + . = ..() + .["choices"] = choices + +/** + * Boolean modal - has yes/no buttons that do different actions depending on which is pressed + */ +/datum/tgui_modal/boolean + modal_type = "boolean" + var/delegate_no + var/yes_text + var/no_text + +/datum/tgui_modal/boolean/New(id, text, delegate, delegate_no, list/arguments, yes_text, no_text) + ..(id, text, delegate, arguments) + src.delegate_no = delegate_no + src.yes_text = yes_text + src.no_text = no_text + +/datum/tgui_modal/boolean/preprocess_answer(answer) + return text2num(answer) || FALSE + +/datum/tgui_modal/boolean/on_answer(answer) + if(answer) + return ..(answer, arguments) + else if(delegate_no) + return call(owning_source, delegate_no)(arguments) + return FALSE + +/datum/tgui_modal/boolean/to_data() + . = ..() + .["yes_text"] = yes_text + .["no_text"] = no_text diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css index e7b79557619..529827ad369 100644 --- a/goon/browserassets/css/browserOutput-dark.css +++ b/goon/browserassets/css/browserOutput-dark.css @@ -38,7 +38,7 @@ a.popt {text-decoration: none;} * CUSTOM FONTS * ******************************************/ -@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } +@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } /***************************************** * @@ -250,6 +250,7 @@ em {font-style: normal; font-weight: bold;} {color: #638500; text-decoration: underline;} .motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #638500;} +.darkmblue {color: #6685f5;} .prefix { font-weight: bold;} .ooc { font-weight: bold;} .looc {color: #6699CC;} @@ -261,32 +262,32 @@ em {font-style: normal; font-weight: bold;} .mentorhelp {color: #0077bb; font-weight: bold;} .adminhelp {color: #aa0000; font-weight: bold;} .playerreply {color: #8800bb; font-weight: bold;} -.pmsend {color: #0000ff;} +.pmsend {color: #6685f5;} .name { font-weight: bold;} .say {} .yell { font-weight: bold;} .siliconsay {font-family: 'Courier New', Courier, monospace;} -.deadsay {color: #cc00c6;} +.deadsay {color: #B800B1;} .radio {color: #408010;} .deptradio {color: #993399;} -.comradio {color: #2040ff;} +.comradio {color: #526aff;} .syndradio {color: #993F40;} .dsquadradio {color: #998599;} .resteamradio {color: #18BC46;} -.airadio {color: #FF00FF;} -.centradio {color: #5C5C7C;} +.airadio {color: #FF94FF;} +.centradio {color: #78789B;} .secradio {color: #CF0000;} .engradio {color: #A66300;} .medradio {color: #009190;} .sciradio {color: #993399;} .supradio {color: #9F8545;} .srvradio {color: #80A000;} -.admin_channel {color: #9A04D1; font-weight: bold;} +.admin_channel {color: #fcba03; font-weight: bold;} .mentor_channel {color: #775BFF; font-weight: bold;} .mentor_channel_admin {color: #A35CFF; font-weight: bold;} .djradio {color: #996600;} .binaryradio {color: #1B00FB; font-family: 'Courier New', Courier, monospace;} -.mommiradio {color: #1B00AB;} +.mommiradio {color: #6685f5;} .alert {color: #ff0000;} h1.alert, h2.alert {color: #FFF;} .ghostalert {color: #cc00c6; font-style: italic; font-weight: bold;} @@ -304,10 +305,10 @@ h1.alert, h2.alert {color: #FFF;} .userdanger {color: #ff0000; font-weight: bold; font-size: 120%;} .biggerdanger {color: #ff0000; font-weight: bold; font-size: 150%;} -.info {color: #0044DD;} -.notice {color: #0044DD;} -.bnotice {color: #0044DD; font-weight: bold;} -.boldnotice {color: #0044DD; font-weight: bold;} +.info {color: #6685f5;} +.notice {color: #6685f5;} +.bnotice {color: #6685f5; font-weight: bold;} +.boldnotice {color: #6685f5; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} .green {color: #03bb39;} .announce {color: #228b22; font-weight: bold;} @@ -358,7 +359,7 @@ h1.alert, h2.alert {color: #FFF;} .cultlarge {color: #A60000; font-weight: bold; font-size: 120%;} .narsie {color: #A60000; font-weight: bold; font-size: 300%;} .narsiesmall {color: #A60000; font-weight: bold; font-size: 200%;} -.interface {color: #330033;} +.interface {color: #9031C4;} .big {font-size: 150%;} .reallybig {font-size: 175%;} .greentext {color: #00FF00; font-size: 150%;} @@ -366,7 +367,7 @@ h1.alert, h2.alert {color: #FFF;} .bold {font-weight: bold;} .center {text-align: center;} .red {color: #FF0000;} -.purple {color: #5e2d79;} +.purple {color: #9031C4;} .skeleton {color: #C8C8C8; font-weight: bold; font-style: italic;} .gutter {color: #7092BE; font-family: "Trebuchet MS", cursive, sans-serif;} .orange {color: #ffa500;} @@ -374,14 +375,14 @@ h1.alert, h2.alert {color: #FFF;} .orangeb {color: #ffa500; font-weight: bold;} .resonate {color: #298F85;} -.revennotice {color: #1d29C3;} -.revenboldnotice {color: #1d29C3; font-weight: bold;} -.revenbignotice {color: #1d29C3; font-weight: bold; font-size: 120%;} +.revennotice {color: #6685F5;} +.revenboldnotice {color: #6685F5; font-weight: bold;} +.revenbignotice {color: #6685F5; font-weight: bold; font-size: 120%;} .revenminor {color: #823abb} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} -.specialnotice {color: #36525e; font-weight: bold; font-size: 120%;} +.specialnotice {color: #4A6F82; font-weight: bold; font-size: 120%;} /* /vg/ */ .good {color: green;} @@ -398,7 +399,11 @@ h1.alert, h2.alert {color: #FFF;} .connectionClosed, .fatalError {background: red; color: white; padding: 5px;} .connectionClosed.restored {background: green;} -.internal.boldnshit {color: blue; font-weight: bold;} +.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;} diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index 962ce7bb8e7..31da7aa96f5 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -37,7 +37,7 @@ a.popt {text-decoration: none;} * CUSTOM FONTS * ******************************************/ -@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } +@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } /***************************************** * @@ -247,6 +247,7 @@ em {font-style: normal; font-weight: bold;} {color: #638500; text-decoration: underline;} .motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #638500;} +.darkmblue {color: #0000ff;} .prefix { font-weight: bold;} .ooc { font-weight: bold;} .looc {color: #6699CC;} @@ -399,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('
    The server is restarting. Reconnect (' + timeLeftSecs + ')
    ', '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(//gi, '\n').replace(/\t/gi, ''), { breaks: false, gfm: false }) - // marked.js wraps content into

    tags, which is looks atrocious when we call it recursively. - // The following line unwraps it. - if (node.children.length == 1) { - node.innerHTML = node.children[0].innerHTML; - } + for (var i = 0; i < node.childNodes.length; i++) + parse(node.childNodes[i]); + + if (!node.innerHTML) + return; + + if (node.children.length == 0) { + node.innerHTML = marked(node.innerHTML.replace(/
    /gi, '\n').replace(/\t/gi, ''), { breaks: false, gfm: false }); + // marked.js wraps content into

    tags, which is looks atrocious when we call it recursively. + // The following line unwraps it. + if (node.children.length == 1 && node.children[0].tagName == "P") + node.innerHTML = node.children[0].innerHTML; + } } - + window.onload = function() { - if ($('#markdown')) { - parse($('#markdown')); - } + if ($('#markdown')) + parse($('#markdown')); } 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/hud.dmi b/icons/mob/hud.dmi index 92006f54cc2..07593d09c0c 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.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/helmet.dmi b/icons/mob/species/grey/helmet.dmi index cf244cec6b2..3e592369adc 100644 Binary files a/icons/mob/species/grey/helmet.dmi and b/icons/mob/species/grey/helmet.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/atmos.dmi b/icons/obj/atmos.dmi index 2209f3bd769..b2668980aec 100644 Binary files a/icons/obj/atmos.dmi and b/icons/obj/atmos.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/bureaucracy.dmi b/icons/obj/bureaucracy.dmi index 1847e985d89..3f66fa90cd6 100644 Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi 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/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index bb7a1ce9188..39789326c49 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.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/contraband.dmi b/icons/obj/contraband.dmi index 97a11d05cad..444863abd71 100644 Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.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/hydroponics/equipment.dmi b/icons/obj/hydroponics/equipment.dmi index 9ad33b40e9d..3cb9e63cb38 100644 Binary files a/icons/obj/hydroponics/equipment.dmi and b/icons/obj/hydroponics/equipment.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/paper.dmi b/icons/obj/paper.dmi deleted file mode 100644 index 55781076833..00000000000 Binary files a/icons/obj/paper.dmi and /dev/null 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/adv_med.tmpl b/nano/templates/adv_med.tmpl deleted file mode 100644 index a1ca2815679..00000000000 --- a/nano/templates/adv_med.tmpl +++ /dev/null @@ -1,231 +0,0 @@ - - -{{if !data.occupied}} -

    No occupant detected.

    -{{else}} -

    Occupant Data:

    -
    -
    - Name: -
    -
    - {{:data.occupant.name}} -
    -
    -
    -
    - Health: -
    - {{:helper.displayBar(data.occupant.health, -100, 100, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 0) ? 'average' : 'bad')}} -
    - {{:helper.smoothRound(data.occupant.health)}}% -
    -
    -
    -
    - Status: -
    -
    - {{if data.occupant.stat==0}} - Alive - {{else data.occupant.stat==1}} - Critical - {{else}} - Dead - {{/if}} -
    -
    - -
    -
    - Temperature: -
    -
    - {{:helper.smoothRound(data.occupant.bodyTempC)}}°C, {{:helper.smoothRound(data.occupant.bodyTempF)}}°F -
    -
    - {{if data.occupant.hasBorer}} - - Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
    - {{/if}} - {{if data.occupant.blind}} - Cataracts detected.
    - {{/if}} - {{if data.occupant.colourblind}} - Photoreceptor abnormalities detected.
    - {{/if}} - {{if data.occupant.nearsighted}} - Retinal misalignment detected.
    - {{/if}} - -
    - {{:helper.link('Print', 'print', {'print_p' : 1, 'name' : data.occupant.name})}} - {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.occupied ? null : 'disabled')}} -

    - - Damage: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Brute Damage: Brain Damage:
    {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth+100, (data.occupant.bruteLoss < 15) ? 'notgood' : (data.occupant.bruteLoss < 40) ? 'average' : 'bad', data.occupant.bruteLoss) }} {{:helper.displayBar(data.occupant.brainLoss, 0, data.occupant.maxHealth+100, (data.occupant.brainLoss < 15) ? 'notgood' : (data.occupant.brainLoss < 40) ? 'average' : 'bad', data.occupant.brainLoss)}}
    Resp. Damage: Radiation Level:
    {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth+100, (data.occupant.oxyLoss < 15) ? 'notgood' : (data.occupant.oxyLoss < 40) ? 'average' : 'bad', data.occupant.oxyLoss)}} {{:helper.displayBar(data.occupant.radLoss, 0, data.occupant.maxHealth+100, (data.occupant.radLoss < 15) ? 'notgood' : (data.occupant.radLoss < 40) ? 'average' : 'bad', data.occupant.radLoss)}}
    Toxin Damage: Genetic Damage:
    {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth+100, (data.occupant.toxLoss < 15) ? 'notgood' : (data.occupant.toxLoss < 40) ? 'average' : 'bad', data.occupant.toxLoss)}} {{:helper.displayBar(data.occupant.cloneLoss, 0, data.occupant.maxHealth+100, (data.occupant.cloneLoss < 15) ? 'notgood' : (data.occupant.cloneLoss < 40) ? 'average' : 'bad', data.occupant.cloneLoss)}}
    Burn Severity: Paralysis %: ({{:helper.smoothRound(data.occupant.paralysisSeconds)}} seconds left)
    {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth+100, (data.occupant.fireLoss < 15) ? 'notgood' : (data.occupant.fireLoss < 40) ? 'average' : 'bad', data.occupant.fireLoss)}} {{:helper.displayBar(data.occupant.paralysis, 0, 100, (data.occupant.paralysis < 25) ? 'notgood' : (data.occupant.paralysis < 50) ? 'average' : 'bad', data.occupant.paralysis)}}
    - {{if data.occupant.blood.hasBlood}} -
    - Blood: -
    -
    Pulse:
    {{:data.occupant.blood.pulse}} bpm
    -
    -
    -
    Blood Level:
    - {{:helper.displayBar(data.occupant.blood.bloodLevel, 0, data.occupant.blood.bloodMax, (data.occupant.blood.percent <= 60) ? 'bad' : (data.occupant.blood.percent <= 90) ? 'average' : 'good' )}} -
    {{:helper.smoothRound(data.occupant.blood.percent)}}%, {{:helper.smoothRound(data.occupant.blood.bloodLevel)}}cl
    -
    - {{/if}} -
    - {{if data.occupant.hasVirus}} - - Viral pathogen detected in blood stream. -
    - {{/if}} - {{if data.occupant.implant_len}} -
    - Implants - - - {{for data.occupant.implant}} - - {{/for}} - -
    {{:value.name}}
    -
    - {{/if}} - External Organs - - - - - - - - - - - {{for data.occupant.extOrgan}} - - - - - - - - {{/for}} - -
    Name Total damage Brute damage Burn damage Injuries
    {{:value.name}} {{:helper.displayBar(value.totalLoss, 0, value.maxHealth, (value.totalLoss < value.bruised) ? 'notgood' : (value.totalLoss < value.broken) ? 'average' : 'bad', value.totalLoss) }} {{if value.bruteLoss> 0}} {{else}} {{/if}} {{:helper.smoothRound(value.bruteLoss)}} {{if value.fireLoss> 0}} {{else}} {{/if}} {{:helper.smoothRound(value.fireLoss)}} - - {{if value.internalBleeding}} internal bleeding
    {{/if}} - {{if value.lungRuptured}} lung ruptured
    {{/if}} - {{if value.status.broken}} {{:value.status.broken}}
    {{/if}} - {{if value.germ_level > 100}} - {{if value.germ_level < 300}} - mild infection
    - {{else value.germ_level < 400}} - mild infection+
    - {{else value.germ_level < 500}} - mild infection++
    - {{else value.germ_level < 700}} - acute infection
    - {{else value.germ_level < 800}} - acute infection+
    - {{else value.germ_level < 900}} - acute infection++
    - {{else value.germ_level >= 900}} - septic
    - {{/if}} - {{/if}} - {{if value.open}} open incision
    {{/if}} -
    - {{if value.status.splinted}} splinted
    {{/if}} - {{if value.status.robotic}} robotic
    {{/if}} - {{if value.status.dead}} DEAD
    - {{/for}} - {{/if}} -
    -
    - Internal Organs - - - - - - - - - {{for data.occupant.intOrgan}} - - - - - - {{/for}} - -
    Name Brute damage Injuries
    {{:value.name}}
    {{:helper.displayBar(value.damage, 0, value.maxHealth, (value.damage < value.bruised) ? 'notgood' : (value.damage < value.broken) ? 'average' : 'bad', value.damage) }} - - {{if value.germ_level > 100}} - {{if value.germ_level < 300}} - mild infection
    - {{else value.germ_level < 400}} - mild infection+
    - {{else value.germ_level < 500}} - mild infection++
    - {{else value.germ_level < 700}} - acute Infection
    - {{else value.germ_level < 800}} - acute Infection+
    - {{else value.germ_level < 900}} - acute Infection++
    - {{else value.germ_level >= 900}} - septic
    - {{/if}} - {{/if}} -
    - {{if value.robotic == 1}} robotic
    {{/if}} - {{if value.robotic == 2}} assisted
    {{/if}} - {{if value.dead}} DEAD
    {{/if}} -
    -{{/if}} 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/canister.tmpl b/nano/templates/canister.tmpl deleted file mode 100644 index 5aaa15d4fe3..00000000000 --- a/nano/templates/canister.tmpl +++ /dev/null @@ -1,158 +0,0 @@ -{{if data.menu == 0}} -

    Tank Status

    -
    -
    - Tank Label: -
    -
    -
    {{:data.name}}
    {{:helper.link('Relabel', 'pencil', {'choice' : 'menu', 'mode_target' : 1}, data.canLabel ? null : 'disabled')}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.tankPressure)}} kPa -
    -
    - -
    -
    - Port Status: -
    -
    - {{:data.portConnected ? 'Connected' : 'Disconnected'}} -
    -
    - -

    Holding Tank Status

    - {{if data.hasHoldingTank}} -
    -
    - Tank Label: -
    -
    -
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.holdingTank.tankPressure)}} kPa -
    -
    - {{else}} -
    No holding tank inserted.
    -
     
    - {{/if}} - - -

    Release Valve Status

    -
    -
    - Release Pressure: -
    -
    - {{:helper.displayBar(data.releasePressure, data.minReleasePressure, data.maxReleasePressure)}} -
    - {{:helper.link('-', null, {'pressure_adj' : -1000}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -100}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -10}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -1}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} -
     {{:data.releasePressure}} kPa 
    - {{:helper.link('+', null, {'pressure_adj' : 1}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 10}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 100}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 1000}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Release Valve: -
    -
    - {{:helper.link('Open', 'unlock', {'toggle' : 1}, data.valveOpen ? 'selected' : null)}}{{:helper.link('Close', 'lock', {'toggle' : 1}, data.valveOpen ? null : 'selected')}} -
    -
    -{{else}} - {{:helper.link('Go back', 'home', {'choice' : 'menu', 'mode_target' : 0}, null)}} -

    Relabel

    -
    - Name: -
    -
    -
    {{:data.name}}
    {{:helper.link('Rename', 'pencil', {'rename' : 1}, data.canLabel ? null : 'disabled')}} -
    - - -

    Colors

    -
    -
    -
    {{:data.colorContainer.prim.name}}
    - {{for data.colorContainer.prim.options}} -
    - {{if value.icon == data.canister_color.prim}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.prim.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.prim.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -
    {{:data.colorContainer.sec.name}}
    - {{:helper.link("None", '', {'choice' : data.colorContainer.sec.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.sec.anycolor ? null : 'selected')}} - {{for data.colorContainer.sec.options}} -
    - {{if value.icon == data.canister_color.sec}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.sec.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.sec.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -
    {{:data.colorContainer.ter.name}}
    - {{:helper.link("None", '', {'choice' : data.colorContainer.ter.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.ter.anycolor ? null : 'selected')}} - {{for data.colorContainer.ter.options}} -
    - {{if value.icon == data.canister_color.ter}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.ter.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.ter.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -
    {{:data.colorContainer.quart.name}}
    - {{:helper.link("None", '', {'choice' : data.colorContainer.quart.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.quart.anycolor ? null : 'selected')}} - {{for data.colorContainer.quart.options}} -
    - {{if value.icon == data.canister_color.quart}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.quart.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.quart.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -

    Decals

    -
    -
    Pick and choose:
    -
    - {{for data.possibleDecals}} - {{:helper.link(value.name, '', {'choice' : 'decals', 'icon' : value.icon}, data.canLabel ? null : 'disabled', value.active ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/chem_dispenser.tmpl b/nano/templates/chem_dispenser.tmpl deleted file mode 100644 index 74d694530c9..00000000000 --- a/nano/templates/chem_dispenser.tmpl +++ /dev/null @@ -1,93 +0,0 @@ - -
    -
    - Energy: -
    -
    - {{:helper.displayBar(data.energy, 0, data.maxEnergy, 'good', data.energy + ' Units')}} -
    -
    - -
    -
    - Dispense: -
    -
    - {{:helper.link('1', 'gear', {'amount' : 1}, (data.amount == 1) ? 'selected' : null)}} - {{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}} - {{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}} - {{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}} - {{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}} - {{:helper.link('50', 'gear', {'amount' : 50}, (data.amount == 50) ? 'selected' : null)}} -
    -
    -
     
    -
    -
    - {{if data.glass}} - Drink Dispenser - {{else}} - Chemical Dispenser - {{/if}} -
    -
    -
    -
    - {{for data.chemicals}} - {{:helper.link(value.title, 'arrow-circle-down', value.commands, null, data.glass ? 'fixedLeftWide' : 'fixedLeft')}} - {{/for}} - -
    -
    -
     
    -
    -
    - {{if data.glass}} - Glass - {{else}} - Beaker - {{/if}} Contents -
    -
    - {{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}} -
    -
    -
    -
    -
    - {{if data.isBeakerLoaded}} - {{if data.beakerCurrentVolume}} - Dispose: - {{/if}} - Volume: {{:data.beakerCurrentVolume}} / {{:data.beakerMaxVolume}}
    - {{for data.beakerContents}} -
    -
    - - {{:value.volume}} units of {{:value.name}} - -
    -
    - {{:helper.link('Isolate', null, {'remove' : value.id, 'removeamount' : -1})}} - {{:helper.link('1', null, {'remove' : value.id, 'removeamount' : 1})}} - {{:helper.link('5', null, {'remove' : value.id, 'removeamount' : 5})}} - {{:helper.link('10', null, {'remove' : value.id, 'removeamount' : 10})}} - {{:helper.link('ALL', null, {'remove' : value.id, 'removeamount' : value.volume})}} -
    -
    -
    - {{empty}} - {{if data.glass}}Glass{{else}}Beaker{{/if}} is empty - - {{/for}} - {{else}} - - No {{if data.glass}}Glass{{else}}Beaker{{/if}} loaded - - {{/if}} -
    -
    -
    diff --git a/nano/templates/chem_heater.tmpl b/nano/templates/chem_heater.tmpl deleted file mode 100644 index 9d9e16fa7e1..00000000000 --- a/nano/templates/chem_heater.tmpl +++ /dev/null @@ -1,47 +0,0 @@ - -
    -
    - Status: -
    -
    - {{:helper.link(data.isActive ? 'On' : 'Off', 'power-off', {'toggle_on' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
    -
    -
    -
    - Target: -
    -
    - {{:helper.link(data.targetTemp + 'K', 'gear', {'adjust_temperature' : 'input'}, null)}} -
    -
    -
    -
    - Beaker -
    -
    - {{:helper.link('Eject', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}} -
    -
    -
    -
    -
    -
    -
    - {{if data.isBeakerLoaded}} - Volume: {{:helper.smoothRound(data.beakerCurrentVolume)}} / {{:data.beakerMaxVolume}}
    - Temperature: {{:helper.smoothRound(data.currentTemp)}} Kelvin
    - {{for data.beakerContents}} - {{:value.volume}} units of {{:value.name}}
    - {{/for}} - {{else}} - No beaker loaded - {{/if}} -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/chem_master.tmpl b/nano/templates/chem_master.tmpl deleted file mode 100644 index e91a43d357b..00000000000 --- a/nano/templates/chem_master.tmpl +++ /dev/null @@ -1,106 +0,0 @@ -{{if data.beaker}} -
    {{:helper.link('Eject Beaker and Clear Buffer', 'eject', {'eject': 1})}}
    -
    -

    Add to buffer:

    - {{for data.beaker_reagents}} -
    -
    {{:value.name}}, {{:value.volume}} units
    - {{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}} - {{:helper.link('1', 'plus', {'add': value.id, 'amount': 1})}} - {{:helper.link('5', 'plus', {'add': value.id, 'amount': 5})}} - {{:helper.link('10', 'plus', {'add': value.id, 'amount': 10})}} - {{:helper.link('All', 'plus', {'add': value.id, 'amount': value.volume})}} - {{:helper.link('Custom', 'plus', {'addcustom': value.id})}} -
    - {{/for}} -
    -
    -

    -
    - Transfer to {{:helper.link(data.mode ? 'beaker' : 'disposal', 'arrows-h', {'toggle': 1})}} -
    -

    - {{for data.buffer_reagents}} -
    -
    {{:value.name}}, {{:value.volume}} units
    - {{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}} - {{:helper.link('1', 'plus', {'remove': value.id, 'amount': 1})}} - {{:helper.link('5', 'plus', {'remove': value.id, 'amount': 5})}} - {{:helper.link('10', 'plus', {'remove': value.id, 'amount': 10})}} - {{:helper.link('All', 'plus', {'remove': value.id, 'amount': value.volume})}} - {{:helper.link('Custom', 'plus', {'removecustom': value.id})}} -
    - {{/for}} -
    -
    -

    Containers:

    -
    - {{if data.condi}} - {{:helper.link('Create pack (10u max)', 'arrow-right', {'createpill': 1})}} - {{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}} - {{else}} - - - - - - - - - - - - - - - - -
    - {{:helper.link('Create pill (100u max)', 'arrow-right', {'createpill': 1})}} - - {{:helper.link('Multiple', 'arrow-right', {'createpill_multiple': 1})}} - - {{:helper.link('Style', 'pencil', {'change_pill': 1})}} - - -
    - {{:helper.link('Create patch (30u max)', 'arrow-right', {'createpatch': 1})}} - - {{:helper.link('Multiple', 'arrow-right', {'createpatch_multiple': 1})}} -
    - {{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}} - - {{:helper.link('Style', 'pencil', {'change_bottle': 1})}} - - -
    - {{/if}} -
    -
    -{{else}} - No beaker loaded
    -{{/if}} -
    - - {{if data.loaded_pill_bottle}} - - - - - -
    - {{:helper.link('Pill Bottle (' + data.loaded_pill_bottle_contents_len + '/' + data.loaded_pill_bottle_storage_slots + ')', 'eject', {'ejectp': 1})}} - - {{:helper.link('Change Wrapper', 'pencil', {'change_pillbottle': 1})}} -
    - {{else}} - - - - -
    - {{:helper.link('No Pill Bottle', 'eject', {}, 'disabled')}} -
    - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/cloning_console.tmpl b/nano/templates/cloning_console.tmpl deleted file mode 100644 index c541ca6d6b4..00000000000 --- a/nano/templates/cloning_console.tmpl +++ /dev/null @@ -1,148 +0,0 @@ - -
    -
    -
    Menu
    -
    - {{:helper.link('Main', 'home', {'menu' : 1}, data.menu == 1 ? 'selected' : '')}}{{:helper.link('Records', 'folder-open', {'menu' : 2}, data.menu == 2 ? 'selected' : '')}} -
    -
    -
    -
    Refresh
    -
    - {{:helper.link('Refresh', 'refresh', {'refresh' : 1})}} -
    -
    -
    -
    Autoprocessing
    -
    - {{if data.autoallowed}} - {{:helper.link('Enabled', 'check', {'task' : 'autoprocess'}, data.autoprocess ? 'selected' : '')}}{{:helper.link('Disabled', 'close', {'task' : 'stopautoprocess'}, !data.autoprocess ? 'selected' : '')}} - {{else}} - {{:helper.link('Enabled', 'check', null, 'disabled')}}{{:helper.link('Disabled', 'close', null, 'selected')}} - {{/if}} -
    -
    - {{if data.disk}} -
    -
    Disk
    -
    {{:helper.link('Eject', 'eject', {'disk' : 'eject'})}}
    -
    - {{/if}} -
    -{{if data.menu == 1}} -

    Modules

    -
    -
    Scanner
    -
    - {{if data.scanner}} Found!{{else}} Error: Not detected!{{/if}} -
    -
    -
    -
    Pods
    -
    - {{if data.numberofpods}} Found {{:data.numberofpods}}!{{else}} Error: None detected!{{/if}} -
    -
    - {{if data.scanner}} -

    Scanner Functions

    - {{if data.loading}} - Scanning... - {{else}} - {{:data.scantemp}} - {{/if}} -
    -
    Scan Occupant
    -
    {{:helper.link('Scan', 'search', {'scan' : 1}, !data.occupant ? 'disabled' : '')}}
    -
    -
    -
    Scanner Lock
    -
    {{if data.occupant}}{{:helper.link('Engaged', 'lock', {'lock' : 1}, data.locked ? 'selected' : '')}}{{else}} {{:helper.link('Engaged', 'lock', null, 'disabled')}}{{/if}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, !data.locked ? 'selected' : '')}}
    -
    - {{if data.can_brainscan}} -
    -
    Scan Mode
    -
    - {{:helper.link('Brain', null, {'toggle_mode' : 1}, !data.scan_mode ? '' : 'selected')}}{{:helper.link('Body', null, {'toggle_mode' : 1}, data.scan_mode ? '' : 'selected')}} -
    -
    - {{/if}} - {{/if}} - {{if data.numberofpods}} -

    Pods

    - {{for data.pods}} - {{:value.name}}
    -
    -
    Biomass
    -
    - {{:value.biomass}} -
    -
    - -
    -
    Select
    -
    - {{:helper.link('Select', 'check', {'selectpod' : value.pod}, value.pod == data.selected_pod ? 'selected' : '')}} -
    -
    - - {{/for}} - {{/if}} -{{/if}} -{{if data.menu == 2}} -

    Current Records

    - {{if data.temp}} -
    - {{:data.temp}} -
    - {{/if}} - {{for data.records}} -
    {{:helper.link(value.realname, 'arrow-circle-down', {'view_rec' : value.record})}}
    - {{empty}} - No records found! - {{/for}} -{{/if}} -{{if data.menu == 3}} -

    Selected Record

    - {{if data.temp}} -
    - {{:data.temp}} -
    - {{/if}} - {{if !data.activerecord}} -
    Error: Record not found!
    - {{else}} -
    - Real Name: {{:data.realname}}
    - Health: {{:data.health}} (OXY - BURN - TOX - BRUTE) -
    -
    - UI: {{:data.unidentity}}
    - SE:
    {{:data.strucenzymes}}
    -
    - {{if data.disk}} -
    -
    Disk
    -
    {{:helper.link('Load from Disk', 'floppy-o', {'disk' : 'load'})}}{{:helper.link('Save UI + UE', 'arrow-circle-up', {'save_disk' : 'ue'})}}{{:helper.link('Save UI', 'arrow-circle-up', {'save_disk' : 'ui'})}}{{:helper.link('Save SE', 'arrow-circle-up', {'save_disk' : 'se'})}}
    -
    - {{/if}} -
    -
    Actions
    -
    {{:helper.link('Clone', 'arrow-right', {'clone' : data.activerecord}, !data.podready ? 'disabled' : '')}}{{:helper.link('Delete', 'trash', {'del_rec' : 1})}}
    -
    - {{/if}} -{{/if}} -{{if data.menu == 4}} -

    Confirm Record Deletion

    - {{if data.temp}} -
    - {{:data.temp}} -
    - {{/if}} -
    - {{:helper.link('Scan Card and Confirm', 'trash', {'del_rec' : 1})}}{{:helper.link('Cancel', 'close', {'menu' : 3})}} -
    -{{/if}} - diff --git a/nano/templates/cryo.tmpl b/nano/templates/cryo.tmpl deleted file mode 100644 index de949c855f2..00000000000 --- a/nano/templates/cryo.tmpl +++ /dev/null @@ -1,117 +0,0 @@ - -

    Cryo Cell Status

    - -
    - {{if !data.hasOccupant}} -
    Cell Unoccupied
    - {{else}} -
    - {{:data.occupant.name}}   - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - - {{if data.occupant.stat < 2}} -
    -
    Health:
    - {{if data.occupant.health >= 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    Brute Damage:
    - {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.bruteLoss)}}
    -
    - -
    -
    Resp. Damage:
    - {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.oxyLoss)}}
    -
    - -
    -
    Toxin Damage:
    - {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.toxLoss)}}
    -
    - -
    -
    Burn Severity:
    - {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.fireLoss)}}
    -
    - {{/if}} - {{/if}} -
    -
    -
    -
    Cell Temperature:
    - {{:helper.smoothRound(data.cellTemperature)}} K -
    -
    -
    - -

    Cryo Cell Operation

    -
    -
    - Cryo Cell Status: -
    -
    - {{:helper.link('On', 'power-off', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}} -
    -
    - {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}} -
    -
    -
     
    -
    -
    - Beaker: -
    -
    - {{if data.isBeakerLoaded}} - {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
    - {{if data.beakerVolume}} - {{:helper.smoothRound(data.beakerVolume)}} units remaining
    - {{else}} - Beaker is empty - {{/if}} - {{else}} - No beaker loaded - {{/if}} -
    -
    - {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
    -
    - -
     
    -
    -
    - Auto-eject healthy patients: -
    -
    - {{:helper.link('On', 'power-off', {'auto_eject_healthy_on' : 1}, data.auto_eject_healthy ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_healthy_off' : 0}, data.auto_eject_healthy ? null : 'selected')}} -
    -
    -
    -
    - Auto-eject dead patients: -
    -
    - {{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}} -
    -
    diff --git a/nano/templates/dna_modifier.tmpl b/nano/templates/dna_modifier.tmpl deleted file mode 100644 index 2789a4a7881..00000000000 --- a/nano/templates/dna_modifier.tmpl +++ /dev/null @@ -1,313 +0,0 @@ - -

    Status

    - -
    - {{if !data.hasOccupant}} -
    Cell Unoccupied
    - {{else}} -
    - {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - - {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}} -
    - The occupant's DNA structure is ruined beyond recognition, please insert a subject with an intact DNA structure. -
    - {{else data.occupant.stat < 2}} -
    -
    Health:
    - {{if data.occupant.health >= 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    Radiation:
    - {{:helper.displayBar(data.occupant.radiationLevel, 0, 100, 'average')}} -
    {{:helper.smoothRound(data.occupant.radiationLevel)}}
    -
    - -
    -
    Unique Enzymes:
    -
    {{:data.occupant.uniqueEnzymes ? data.occupant.uniqueEnzymes : 'Unknown'}}
    -
    - - {{/if}} - {{/if}} -
    -
    - -

    Operations

    - {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}} -
    - No operation possible on this subject -
    - {{else}} -
    - {{:helper.link('Modify U.I.', 'link', {'selectMenuKey' : 'ui'}, data.selectedMenuKey == 'ui' ? 'selected' : null)}} - {{:helper.link('Modify S.E.', 'link', {'selectMenuKey' : 'se'}, data.selectedMenuKey == 'se' ? 'selected' : null)}} - {{:helper.link('Transfer Buffers', 'floppy-o', {'selectMenuKey' : 'buffer'}, data.selectedMenuKey == 'buffer' ? 'selected' : null)}} - {{:helper.link('Rejuvenators', 'plus', {'selectMenuKey' : 'rejuvenators'}, data.selectedMenuKey == 'rejuvenators' ? 'selected' : null)}} -
    - -
     
    - - {{if !data.selectedMenuKey || data.selectedMenuKey == 'ui'}} -

    Modify Unique Identifier

    - {{:helper.displayDNABlocks(data.occupant.uniqueIdentity, data.selectedUIBlock, data.selectedUISubBlock, data.dnaBlockSize, 'UI')}} -
    -
    -
    - Target: -
    -
    - {{:helper.link('-', null, {'changeUITarget' : 0}, (data.selectedUITarget > 0) ? null : 'disabled')}} -
     {{:data.selectedUITargetHex}} 
    - {{:helper.link('+', null, {'changeUITarget' : 1}, (data.selectedUITarget < 15) ? null : 'disabled')}} -
    -
    -
    -
    - {{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseUIRadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}} -
    -
    - {{else data.selectedMenuKey == 'se'}} -

    Modify Structural Enzymes

    - {{:helper.displayDNABlocks(data.occupant.structuralEnzymes, data.selectedSEBlock, data.selectedSESubBlock, data.dnaBlockSize, 'SE')}} -
    -
    -
    - {{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseSERadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}} -
    -
    - {{else data.selectedMenuKey == 'buffer'}} -

    Transfer Buffers

    - {{for data.buffers}} -

    Buffer {{:(index + 1)}}

    -
    -
    -
    - Load Data: -
    -
    - {{:helper.link('Subject U.I.', 'link', {'bufferOption' : 'saveUI', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('Subject U.I. + U.E.', 'link', {'bufferOption' : 'saveUIAndUE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('Subject S.E.', 'link', {'bufferOption' : 'saveSE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('From Disk', 'floppy-o', {'bufferOption' : 'loadDisk', 'bufferId' : (index + 1)}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}} -
    -
    - {{if value.data}} -
    -
    - Label: -
    -
    - {{:helper.link(value.label, 'file', {'bufferOption' : 'changeLabel', 'bufferId' : (index + 1)})}} -
    -
    -
    -
    - Subject: -
    -
    - {{:value.owner ? value.owner : 'Unknown'}} -
    -
    -
    -
    - Stored Data: -
    -
    - {{:value.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}} - {{:value.ue ? ' + Unique Enzymes' : ''}} -
    -
    - {{else}} -
    -
    - This buffer is empty. -
    -
    - {{/if}} -
    -
    - Options: -
    -
    - {{:helper.link('Clear', 'trash', {'bufferOption' : 'clear', 'bufferId' : (index + 1)}, !value.data ? 'disabled' : null)}} - {{:helper.link('Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1)}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}} - {{:helper.link('Block Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1), 'createBlockInjector' : 1}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}} - {{:helper.link('Transfer', 'exclamation-circle', {'bufferOption' : 'transfer', 'bufferId' : (index + 1)}, (!data.hasOccupant || !value.data) ? 'disabled' : null)}} - {{:helper.link('Save To Disk', 'floppy-o', {'bufferOption' : 'saveDisk', 'bufferId' : (index + 1)}, (!value.data || !data.hasDisk) ? 'disabled' : null)}} -
    -
    -
    - {{/for}} - -

    Data Disk

    -
    - {{if data.hasDisk}} - {{if data.disk.data}} -
    -
    - Label: -
    -
    - {{:data.disk.label ? data.disk.label : 'No Label'}} -
    -
    -
    -
    - Subject: -
    -
    - {{:data.disk.owner ? data.disk.owner : 'Unknown'}} -
    -
    -
    -
    - Stored Data: -
    -
    - {{:data.disk.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}} - {{:data.disk.ue ? ' + Unique Enzymes' : ''}} -
    -
    - {{else}} -
    -
    - Disk is blank. -
    -
    - {{/if}} - {{else}} -
    -
    - No disk inserted. -
    -
    - {{/if}} -
    -
    - Options: -
    -
    - {{:helper.link('Wipe Disk', 'trash', {'bufferOption' : 'wipeDisk'}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}} - {{:helper.link('Eject Disk', 'eject', {'bufferOption' : 'ejectDisk'}, !data.hasDisk ? 'disabled' : null)}} -
    -
    -
    - {{else data.selectedMenuKey == 'rejuvenators'}} -

    Rejuvenators

    -
    -
    - Inject: -
    -
    - {{:helper.link('5', 'pencil', {'injectRejuvenators' : 5}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('10', 'pencil', {'injectRejuvenators' : 10}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('20', 'pencil', {'injectRejuvenators' : 20}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('30', 'pencil', {'injectRejuvenators' : 30}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('50', 'pencil', {'injectRejuvenators' : 50}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} -
    -
    -
     
    -
    -
    - Beaker: -
    -
    - {{if data.isBeakerLoaded}} - {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
    - {{if data.beakerVolume}} - {{:data.beakerVolume}} units remaining
    - {{else}} - Beaker is empty - {{/if}} - {{else}} - No beaker loaded - {{/if}} -
    -
    - {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
    -
    - {{/if}} - -
     
    - - {{if !data.selectedMenuKey || data.selectedMenuKey == 'ui' || data.selectedMenuKey == 'se'}} -

    Radiation Emitter Settings

    -
    -
    - Intensity: -
    -
    - {{:helper.link('-', null, {'radiationIntensity' : 0}, (data.radiationIntensity > 1) ? null : 'disabled')}} -
     {{:data.radiationIntensity}} 
    - {{:helper.link('+', null, {'radiationIntensity' : 1}, (data.radiationIntensity < 10) ? null : 'disabled')}} -
    -
    -
    -
    - Duration: -
    -
    - {{:helper.link('-', null, {'radiationDuration' : 0}, (data.radiationDuration > 2) ? null : 'disabled')}} -
     {{:data.radiationDuration}} 
    - {{:helper.link('+', null, {'radiationDuration' : 1}, (data.radiationDuration < 20) ? null : 'disabled')}} -
    -
    -
    -
    -   -
    -
    - {{:helper.link('Pulse Radiation', 'exclamation-circle', {'pulseRadiation' : 1}, !data.hasOccupant ? 'disabled' : null)}} -
    -
    - {{/if}} -{{/if}} -
     
    - -
    - -
    -
    - Occupant: -
    -
    - {{:helper.link('Eject Occupant', 'eject', {'ejectOccupant' : 1}, data.locked || !data.hasOccupant || data.irradiating ? 'disabled' : null)}} -
    -
    -
    -
    - Door Lock: -
    -
    - {{:helper.link('Engaged', 'lock', {'toggleLock' : 1}, data.locked ? 'selected' : !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('Disengaged', 'unlock', {'toggleLock' : 1}, !data.locked ? 'selected' : data.irradiating ? 'disabled' : null)}} -
    -
    - -{{if data.irradiating}} -
    -
    -

    Irradiating Subject

    -

    For {{:data.irradiating}} seconds.

    -
    -
    -{{/if}} 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}} - - - - - - {{/for}} -
    {{: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)}}
    diff --git a/nano/templates/faxmachine.tmpl b/nano/templates/faxmachine.tmpl deleted file mode 100644 index e52c40eb927..00000000000 --- a/nano/templates/faxmachine.tmpl +++ /dev/null @@ -1,67 +0,0 @@ - -
    -

    Authorization

    -
    -
    -
    - Confirm identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'scan' : 1})}} -
    -
    -
    -
    - Authorize: -
    -
    - {{:helper.link(data.authenticated ? 'Log Out' : 'Log In', data.authenticated ? 'unlock' : 'lock', {'auth' : 1})}} -
    -
    -
    -
    -

    Fax Menu

    -
    -
    -
    - Network: -
    - -
    - {{:data.network}} -
    -
    -
    -
    - Currently sending: -
    -
    - {{:helper.link(data.paper, 'eject', {'paper' : 1})}} - {{if data.paperinserted}} - {{:helper.link('Rename', 'pencil', {'rename' : 1})}} - {{/if}} -
    -
    -
    -
    - Sending to: -
    -
    - {{:helper.link(data.destination, 'print', {'dept' : 1}, !data.authenticated ? 'disabled' : '')}} -
    -
    -
    -
    - Action: -
    -
    - {{if data.authenticated}} - {{:helper.link(data.cooldown && data.respectcooldown ? "Realigning" : "Send", data.cooldown && data.respectcooldown ? 'clock-o' : "envelope-o", {'send' : 1}, data.cooldown && data.respectcooldown ? 'disabled' : "")}} - {{else}} - {{:helper.link(data.cooldown && data.respectcooldown ? "Realigning" : "Send", data.cooldown && data.respectcooldown ? 'clock-o' : "envelope-o", null, !data.authenticated ? 'disabled' : "")}} - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/mech_bay_console.tmpl b/nano/templates/mech_bay_console.tmpl deleted file mode 100644 index 2a4cf5d6085..00000000000 --- a/nano/templates/mech_bay_console.tmpl +++ /dev/null @@ -1,55 +0,0 @@ -

    Subsystems

    -
    -
    - Power Port: -
    -
    - {{if data.recharge_port}} - OK - {{else}} - ERROR - {{/if}} -
    -
    -
    -
    - Mecha: -
    -
    - {{if data.has_mech}} - LOCATED ({{:data.mecha_name}}) - {{else}} - NONE - {{/if}} -
    -
    -{{if data.has_mech}} -

    Charging

    -
    -
    - Charge Level: -
    -
    - {{:helper.displayBar(data.mecha_charge_percentage, 0, 100)}} -
    - {{:helper.smoothRound(data.mecha_charge_percentage)}}%
    -
    -
    -
    -
    -
    - Cell Rating: -
    -
    - {{:data.mecha_maxcharge}} -
    -
    -
    -
    - Cell Charge: -
    -
    - {{:helper.smoothRound(data.mecha_charge)}} -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/med_data.tmpl b/nano/templates/med_data.tmpl deleted file mode 100644 index f47e13c6885..00000000000 --- a/nano/templates/med_data.tmpl +++ /dev/null @@ -1,171 +0,0 @@ - - - - -
    -
    - Confirm Identity: -
    -
    - {{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}} -
    -
    -
    -{{if data.authenticated}} - {{if data.screen == 1}} -

    Menu

    -
    -
    {{:helper.link('Search Records', 'search', {'search' : 1})}}
    -
    {{:helper.link('List Records', 'list', {'screen' : 2})}}
    -
    {{:helper.link('Virus Database', 'database', {'screen' : 5})}}
    -
    {{:helper.link('Medbot Tracking', 'plus-square', {'screen' : 6})}}
    -
    {{:helper.link('Record Maintenance', 'wrench', {'screen' : 3})}}
    -
    {{:helper.link('Logout', 'lock', {'logout' : 1})}}
    -
    - {{else data.screen == 2}} -

    Record List

    - {{for data.records}} -
    {{:helper.link(value.id + ': ' + value.name, 'user', {'d_rec' : value.ref})}}
    - {{/for}} -

    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{else data.screen == 3}} -

    Records Maintenance

    -
    {{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}
    -
    {{:helper.link('Upload From disk', 'upload', {'u_load' : 1}, 'disabled')}}
    -
    {{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{else data.screen == 4}} -

    Medical Record

    -

    General Data

    - {{if data.general.empty}} -
    General Record Lost!
    - {{else}} -
    - - - -
    - {{for data.general.fields}} -
    -
    {{:value.field}}
    -
    {{:helper.link(value.value, value.edit ? 'pencil' : 'user-times', {'field' : value.edit}, value.edit ? null : 'disabled')}}
    -
    - {{/for}} -
    - {{if data.general.has_photos}} - {{for data.general.photos}} - {{if value.photo}} - - {{/if}} - {{/for}} - {{/if}} -
    -
    - {{/if}} - -

    Medical Data

    - {{if data.medical.empty}} -
    Medical Record Lost!
    -
    -
    {{:helper.link('New Record', 'plus', {'new' : 1})}}
    -
    -

    Menu

    - {{else}} - {{for data.medical.fields}} -
    -
    {{:value.field}}
    -
    {{:helper.link(value.value, 'pencil', {'field' : value.edit})}}
    -
    - {{if value.line_break}} -
    - {{/if}} - {{/for}} - -

    Comments/Log

    - {{for data.medical.comments}} -
    {{:value}}
    -
    {{:helper.link('Remove entry', 'trash', {'del_c' : index})}}
    -

    - {{/for}} - -
    -
    {{:helper.link('Add Entry', 'plus', {'add_c' : 1})}}
    -
    - -

    Menu

    -
    {{:helper.link('Delete Record (Medical Only)', 'trash', {'del_r' : 1})}}
    - {{/if}} - -
    {{:helper.link('Print Record', 'print', {'print_p' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 2})}}
    - {{else data.screen == 5}} -

    Virus Database

    - {{for data.virus}} -
    {{:helper.link(value.name, 'arrow-right', {'vir' : value.D})}}
    - {{/for}} -

    - {{:helper.link('Back', 'arrow-left', {'screen' : 1})}} - {{else data.screen == 6}} -

    Medical Robot Monitor

    - {{for data.medbots}} -

    {{:value.name}}

    -
    -
    Location:
    -
    {{:value.x}}, {{:value.y}}
    -
    -
    -
    Status:
    -
    - {{:value.on ? "Online" : "Offline"}}. {{if value.use_beaker}}Reservoir: [{{:value.total_volume}}/{{:value.maximum_volume}}]{{else}}Using internal synthesizer.{{/if}} -
    -
    -
    - {{empty}} -

    None detected!

    - {{/for}} -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{/if}} -{{else}} -

    Menu

    - {{:helper.link('Login', 'unlock', {'login' : 1})}} -{{/if}} - -{{if data.temp}} -
    -
    - {{if data.temp.notice}} -
    {{:data.temp.text}}
    - {{else}} -
    {{:data.temp.text}}
    - {{/if}} - - - {{if data.temp.has_buttons}} -
    -
    -
    - {{for data.temp.buttons}} - {{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.href}, value.status)}} - {{/for}} -
    -
    -
    - {{/if}} - -
    -
    {{:helper.link('Clear screen', 'home', {'temp' : 1})}}
    -
    -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/op_computer.tmpl b/nano/templates/op_computer.tmpl deleted file mode 100644 index 77894bcba16..00000000000 --- a/nano/templates/op_computer.tmpl +++ /dev/null @@ -1,215 +0,0 @@ - -

    Patient Monitor

    - -
    - {{if data.choice==0}} - {{if !data.hasOccupant}} -
    No Patient Detected
    - {{else}} -
    - {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - -
    -
    Health:
    - {{if data.occupant.health >= data.occupant.maxHealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else data.occupant.health > 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}} - {{else data.occupant.health >= data.minhealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    Brute Damage:
    - {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.bruteLoss)}}
    -
    - -
    -
    Resp. Damage:
    - {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.oxyLoss)}}
    -
    - -
    -
    Toxin Damage:
    - {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.toxLoss)}}
    -
    - -
    -
    Burn Severity:
    - {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.fireLoss)}}
    -
    - -
    -
    Patient Temperature:
    - {{if data.occupant.temperatureSuitability == -3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 0}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{/if}} -
    - {{if data.occupant.hasBlood}} -
    -
    -
    Blood Level:
    - {{if data.occupant.bloodPercent <= 60}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else data.occupant.bloodPercent <= 90}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{/if}} -
    -
    Blood Type:
    {{:data.occupant.bloodType}}
    -
    -
    -
    -
    Pulse:
    {{:data.occupant.pulse}} bpm
    -
    - {{/if}} - {{if data.occupant.inSurgery}} -
    -
    Initiated Surgery Procedure:
    {{:data.occupant.surgeryName}}
    -
    -
    Next Step:
    {{:data.occupant.stepName}}
    - -
    - {{/if}} -
    -
    - {{/if}} -
    -
    - {{:helper.link('Options', 'gear', {'choiceOn' : 1})}} -
    -
    - {{else}} -
    -
    - Loudspeaker: -
    -
    - {{:helper.link('On', 'power-off', {'verboseOn' : 1}, data.verbose ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'verboseOff' : 1}, data.verbose ? null : 'selected')}} -
    -
    -
    -
    -
    - Health Announcer: -
    -
    - {{:helper.link('On', 'power-off', {'healthOn' : 1}, data.health ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'healthOff' : 1}, data.health ? null : 'selected')}} -
    -
    - -
    -
    -
    Health Announcer Threshold:
    - {{:helper.link('-', null, {'health_adj' : -100}, (data.healthAlarm >= 0) ? null : 'disabled')}} - {{:helper.link('-', null, {'health_adj' : -10}, (data.healthAlarm >= -90) ? null : 'disabled')}} - {{:helper.link('-', null, {'health_adj' : -1}, (data.healthAlarm > -100) ? null : 'disabled')}} - - {{if data.healthAlarm >= 100}} - {{:helper.displayBar(data.healthAlarm, 0, 100, 'good')}} - {{else data.healthAlarm > 0}} - {{:helper.displayBar(data.healthAlarm, 0, 100, 'average')}} - {{else data.healthAlarm >= -100}} - {{:helper.displayBar(data.healthAlarm, 0, -100, 'average alignRight')}} - {{else}} - {{:helper.displayBar(data.healthAlarm, 0, -100, 'bad alignRight')}} - {{/if}} - - {{:helper.link('+', null, {'health_adj' : 1}, (data.healthAlarm < 100) ? null : 'disabled')}} - {{:helper.link('+', null, {'health_adj' : 10}, (data.healthAlarm <= 90) ? null : 'disabled')}} - {{:helper.link('+', null, {'health_adj' : 100}, (data.healthAlarm <= 0) ? null : 'disabled')}} -
     {{: data.healthAlarm }}  
    -
    -
    -
    -
    -
    - Oxygen Alarm: -
    -
    - {{:helper.link('On', 'power-off', {'oxyOn' : 1}, data.oxy ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'oxyOff' : 1}, data.oxy ? null : 'selected')}} -
    -
    - -
    -
    -
    Oxygen Alert Threshold:
    - {{:helper.link('-', null, {'oxy_adj' : -100}, (data.oxyAlarm >= 100) ? null : 'disabled')}} - {{:helper.link('-', null, {'oxy_adj' : -10}, (data.oxyAlarm >= 10) ? null : 'disabled')}} - {{:helper.link('-', null, {'oxy_adj' : -1}, (data.oxyAlarm >= 0) ? null : 'disabled')}} - {{:helper.displayBar( data.oxyAlarm, 0, 200, 'good')}} - {{:helper.link('+', null, {'oxy_adj' : 1}, (data.oxyAlarm < 200) ? null : 'disabled')}} - {{:helper.link('+', null, {'oxy_adj' : 10}, (data.oxyAlarm <= 190) ? null : 'disabled')}} - {{:helper.link('+', null, {'oxy_adj' : 100}, (data.oxyAlarm <= 100) ? null : 'disabled')}} -
     {{: data.oxyAlarm }}  
    -
    -
    -
    -
    -
    - Critical Alert: -
    -
    - {{:helper.link('On', 'power-off', {'critOn' : 1}, data.crit ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'critOff' : 1}, data.crit ? null : 'selected')}} -
    -
    -
    -
    -
    - {{:helper.link('Return', 'arrow-left', {'choiceOff' : 1})}} -
    -
    - {{/if}} -
    diff --git a/nano/templates/radio_basic.tmpl b/nano/templates/radio_basic.tmpl deleted file mode 100644 index c6a59d59691..00000000000 --- a/nano/templates/radio_basic.tmpl +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - -{{if data.useSyndMode}} - {{:helper.syndicateMode()}} -{{/if}} - -
    -
    - Microphone -
    -
    - {{if data.mic_cut}} - {{:helper.link('On', null, null, 'disabled')}} - {{:helper.link('Off', null, null, 'disabled')}} - {{else}} - {{:helper.link('On', null, {'talk' : 0}, data.mic_status ? 'selected' : null)}} - {{:helper.link('Off', null, {'talk' : 1}, data.mic_status ? null : 'selected')}} - {{/if}} -
    -
    - -
    -
    - Speaker -
    -
    - {{if data.spk_cut}} - {{:helper.link('On', null, null, 'disabled')}} - {{:helper.link('Off', null, null, 'disabled')}} - {{else}} - {{:helper.link('On', null, {'listen' : 0}, data.speaker ? 'selected' : null)}} - {{:helper.link('Off', null, {'listen' : 1}, data.speaker ? null : 'selected')}} - {{/if}} -
    -
    - -{{if data.has_subspace}} -
    -
    - Subspace Transmission: -
    -
    - {{:helper.link('On', null, {'mode' : 1}, data.subspace ? 'selected' : null)}} - {{:helper.link('Off', null, {'mode' : 0}, data.subspace ? null : 'selected')}} -
    -
    -{{/if}} - -{{if data.has_loudspeaker}} -
    -
    - Loudspeaker: -
    -
    - {{:helper.link('On', null, {'shutup' : 0}, data.loudspeaker ? 'selected' : null)}} - {{:helper.link('Off', null, {'shutup' : 1}, data.loudspeaker ? null : 'selected')}} -
    -
    -{{/if}} - -
    -
    - Frequency: {{:data.freq}} -
    -
    - {{:helper.link('--', null, {'freq' : -10})}} - {{:helper.link('-', null, {'freq' : -2})}} - {{:helper.link('+', null, {'freq' : 2})}} - {{:helper.link('++', null, {'freq' : 10})}} -
    -
    - -{{if data.chan_list_len >= 1}} -

    Channels

    -
    - {{for data.chan_list}} -
    - {{:value.display_name}} -
    -
    - {{if value.secure_channel}} - {{:helper.link('On', null, {'ch_name' : value.chan, 'listen' : value.sec_channel_listen}, value.sec_channel_listen ? null : 'selected')}} - {{:helper.link('Off', null, {'ch_name' : value.chan, 'listen' : value.sec_channel_listen}, value.sec_channel_listen ? 'selected' : null)}} - {{else}} - {{:helper.link('Switch', null, {'spec_freq' : value.chan}, data.rawfreq == value.chan ? 'selected' : null)}} - {{/if}} -
    - {{/for}} -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/radio_electro.tmpl b/nano/templates/radio_electro.tmpl deleted file mode 100644 index 9fb8183ad01..00000000000 --- a/nano/templates/radio_electro.tmpl +++ /dev/null @@ -1,37 +0,0 @@ - -
    -
    - Power -
    -
    - {{:helper.link('On', null, {'power' : 1}, data.power ? 'selected' : null)}} - {{:helper.link('Off', null, {'power' : 1}, data.power ? null : 'selected')}} -
    -
    - -
    -
    - Frequency: {{:data.freq}} -
    -
    - {{:helper.link('--', null, {'freq' : -10})}} - {{:helper.link('-', null, {'freq' : -2})}} - {{:helper.link('+', null, {'freq' : 2})}} - {{:helper.link('++', null, {'freq' : 10})}} -
    -
    - -
    -
    - Code: {{:data.code}} -
    -
    - {{:helper.link('--', null, {'code' : -5})}} - {{:helper.link('-', null, {'code' : -1})}} - {{:helper.link('+', null, {'code' : 1})}} - {{:helper.link('++', null, {'code' : 5})}} -
    -
    \ No newline at end of file diff --git a/nano/templates/robot_control.tmpl b/nano/templates/robot_control.tmpl deleted file mode 100644 index 6e97ffaf3d7..00000000000 --- a/nano/templates/robot_control.tmpl +++ /dev/null @@ -1,100 +0,0 @@ -{{if data.robots}} - {{if !data.is_ai}} -
    -
    - Emergency Self-Destruct: -
    -
    - {{if data.safety}} - {{:helper.link('ARM', 'unlock', {'arm' : 1}, !data.allowed ? 'disabled' : null)}} - {{:helper.link('DETONATE', 'exclamation-circle', {'nuke' : 1}, 'disabled')}} - {{else}} - {{:helper.link('DISARM', 'lock', {'arm' : 1}, !data.allowed ? 'disabled' : null)}} - {{:helper.link('DETONATE', 'exclamation-circle', {'nuke' : 1}, !data.allowed ? 'disabled' : null, 'redButton')}} - {{/if}} -
    -
    -
    - {{/if}} - {{for data.robots}} -
    -
    -

    {{:value.name}}

    -

    Information

    - - Status: - - - {{:value.status}} - - - System Integrity: - - - {{:helper.smoothRound(value.health)}}% - - - Location: - - - ({{:value.xpos}}, {{:value.ypos}}, {{:value.zpos}}): {{:value.area}} - - - Master AI: - - - {{:value.master_ai}} - - - Module: - - - {{:value.module}} - - - {{if value.hackable}} - - Safeties: - - - ENABLED - - {{else value.hacked}} - - Safeties: - - - DISABLED - - {{/if}} -

    Power Cell

    - {{if value.cell}} - - Rating: - - - {{:value.cell_capacity}} - - {{:helper.displayBar(value.cell_percentage, 0, 100, (value.cell_percentage >= 50) ? 'good' : (value.cell_percentage >= 25) ? 'average' : 'bad')}} - {{:helper.smoothRound(value.cell_percentage)}}% - {{else}} - Not Installed - {{:helper.displayBar(100, 0, 100, 'bad')}} - {{/if}} -

    Actions

    - {{if value.status == "Operational"}} - {{:helper.link('Lockdown', 'lock', {'lockdown' : value.name}, !data.allowed ? 'disabled' : null)}} - {{else}} - {{:helper.link('Unlock', 'unlock', {'lockdown' : value.name}, !data.allowed ? 'disabled' : null)}} - {{/if}} - {{:helper.link('Self-Destruct', 'exclamation-circle', {'detonate' : value.name}, !data.allowed ? 'disabled' : null, 'redButton')}} - {{if value.hackable}} - {{:helper.link('Hack', 'calculator', {'hack' : value.name}, !data.allowed ? 'disabled' : null, 'redButton')}} - {{/if}} -
    - {{/for}} -{{else}} -
    -

    No robots were found.

    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/shuttle_console.tmpl b/nano/templates/shuttle_console.tmpl deleted file mode 100644 index 8277e92eb76..00000000000 --- a/nano/templates/shuttle_console.tmpl +++ /dev/null @@ -1,37 +0,0 @@ - - -
    -
    Location:
    -
    {{:data.status ? data.status : '*Missing*'}}
    -
    -{{if data.shuttle}} - {{if data.docking_ports_len}} -
    -
    - {{for data.docking_ports}} -
    {{:helper.link('Send to ' + value.name, 'sign-out', {'move' : value.id})}}
    - {{/for}} -
    - {{else}} -
    -
    Status:
    -
    Shuttle locked - {{if data.admin_controlled}} - , authorized personnel only -
    -
    -
    {{:helper.link('Request authorization', 'share-square-o', {'request' : 1})}}
    - {{else}} -
    -
    -
    - {{/if}} - {{/if}} - - {{if data.docking_request}} -
    {{:helper.link('Request docking at the station', 'share-square-o', {'request' : 1})}}
    - {{/if}} -{{/if}} diff --git a/nano/templates/sleeper.tmpl b/nano/templates/sleeper.tmpl deleted file mode 100644 index cacf6c83ff5..00000000000 --- a/nano/templates/sleeper.tmpl +++ /dev/null @@ -1,211 +0,0 @@ - -

    Sleeper Status

    - -
    - {{if !data.hasOccupant}} -
    Sleeper Unoccupied
    - {{else}} -
    - {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - -
    -
    Health:
    - {{if data.occupant.health >= data.occupant.maxHealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else data.occupant.health > 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}} - {{else data.occupant.health >= data.minhealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    => Brute Damage:
    - {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.bruteLoss)}}
    -
    - -
    -
    => Resp. Damage:
    - {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.oxyLoss)}}
    -
    - -
    -
    => Toxin Damage:
    - {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.toxLoss)}}
    -
    - -
    -
    => Burn Severity:
    - {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.fireLoss)}}
    -
    - -
    -
    - -
    Patient Temperature:
    - {{if data.occupant.temperatureSuitability == -3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 0}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{/if}} -
    - - - {{if data.occupant.hasBlood}} - -
    -
    -
    Pulse:
    {{:data.occupant.pulse}} bpm
    -
    -
    -
    Blood Level:
    - {{if data.occupant.bloodPercent <= 60}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else data.occupant.bloodPercent <= 90}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{/if}} -
    - {{/if}} - {{/if}} -
    - -

    Sleeper Operation

    -
    -
    - Sleeper Status: -
    -
    - {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.hasOccupant ? null : 'disabled')}} -
    -
    -
     
    -
    -
    - Dialysis Beaker: -
    -
    - {{if data.isBeakerLoaded}} - {{:helper.smoothRound(data.beakerFreeSpace)}} units of space remaining
    - {{else}} - No Dialysis Output Beaker Loaded - {{/if}} -
    -
    - {{:helper.link('Eject Beaker', 'eject', {'removebeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} - {{if data.isBeakerLoaded}} -
    -
    - {{:helper.link('On', 'power-off', {'togglefilter' : 1}, data.occupant.hasBlood ? (data.dialysis ? 'selected' : null) : 'disabled')}}{{:helper.link('Off', 'close', {'togglefilter' : 1}, data.dialysis ? null : 'selected')}} -
    -
    - {{/if}} -
    -
    -
    -
    - Chemicals: -
    - - {{for data.chemicals}} -
    {{:value.title}}
    -
    - {{if value.overdosing}} - {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'bad')}} - {{else value.od_warning}} - {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'average')}} - {{else}} - {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'good')}} - {{/if}} -
    {{:helper.smoothRound(value.pretty_amount)}}/{{:data.maxchem}}
    -
    -
    -
    -
    - {{:helper.link('5', 'gear', {'chemical' : value.id, 'amount' : 5}, (!value.injectable || ((value.occ_amount + 5) > data.maxchem)) ? 'disabled' : null)}} - {{:helper.link('10', 'gear', {'chemical' : value.id, 'amount' : 10}, (!value.injectable || ((value.occ_amount + 10) > data.maxchem)) ? 'disabled' : null)}} -
    -
    - {{/for}} - -
     
    -
    -
    - Auto-eject dead patients: -
    -
    - {{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}} -
    -
    -
    - 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/smes.tmpl b/nano/templates/smes.tmpl deleted file mode 100644 index 2657c0f724e..00000000000 --- a/nano/templates/smes.tmpl +++ /dev/null @@ -1,89 +0,0 @@ -
    -
    - Stored Capacity: -
    -
    - {{:helper.displayBar(data.storedCapacity, 0, 100, data.charging ? 'good' : 'average')}} -
    - {{:helper.smoothRound(data.storedCapacity)}}% -
    -
    -
    - -

    Input Management

    -
    -
    - Charge Mode: -
    -
    - {{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}} -   - {{if data.charging == 2}} - [Charging] - {{else data.charging == 1}} - [Partially Charging] - {{else}} - [Not Charging] - {{/if}} -
    -
    - -
    -
    - Input Level: -
    -
    - {{:helper.displayBar(data.chargeLevel, 0, data.chargeMax)}} -
    - {{:helper.link('MIN', null, {'input' : 'min'}, (data.chargeLevel > 0) ? null : 'disabled')}} - {{:helper.link('SET', null, {'input' : 'set'}, null)}} - {{:helper.link('MAX', null, {'input' : 'max'}, (data.chargeLevel < data.chargeMax) ? null : 'disabled')}} -
     {{:data.chargeLevel}} W 
    -
    -
    -
    - -

    Output Management

    -
    -
    - Output Status: -
    -
    - {{:helper.link('Online', 'power-off', {'online' : 1}, data.outputOnline ? 'selected' : null)}}{{:helper.link('Offline', 'close', {'online' : 1}, data.outputOnline ? null : 'selected')}} -   - {{if data.outputting == 2}} - [Outputting] - {{else data.outputting == 1}} - [Stored energy too low] - {{else}} - [Not Outputting] - {{/if}} -
    -
    - -
    -
    - Output Level: -
    -
    - {{:helper.displayBar(data.outputLevel, 0, data.outputMax)}} -
    - {{:helper.link('MIN', null, {'output' : 'min'}, (data.outputLevel > 0) ? null : 'disabled')}} - {{:helper.link('SET', null, {'output' : 'set'}, null)}} - {{:helper.link('MAX', null, {'output' : 'max'}, (data.outputLevel < data.outputMax) ? null : 'disabled')}} -
     {{:data.outputLevel}} W 
    -
    -
    -
    - -
    -
    - Output Load: -
    -
    - {{:helper.displayBar(data.outputLoad, 0, data.outputMax, (data.outputLoad < data.outputLevel) ? 'good' : 'average')}} -
    - {{:helper.smoothRound(data.outputLoad)}} W -
    -
    -
    \ No newline at end of file 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/tanks.tmpl b/nano/templates/tanks.tmpl deleted file mode 100644 index 2cf12746f01..00000000000 --- a/nano/templates/tanks.tmpl +++ /dev/null @@ -1,47 +0,0 @@ -{{if data.maskConnected}} -
    This tank is connected to a mask.
    -{{else}} -
    This tank is NOT connected to a mask.
    -{{/if}} - -
    -
    - Tank Pressure: -
    -
    - {{:helper.displayBar(data.tankPressure, 0, 1013, (data.tankPressure > 200) ? 'good' : ((data.tankPressure > 100) ? 'average' : 'bad'))}} -
    - {{:helper.smoothRound(data.tankPressure)}} kPa -
    -
    -
    - -
     
    - -
    -
    - Mask Release Pressure: -
    -
    - {{:helper.displayBar(data.releasePressure, 0, data.maxReleasePressure, (data.releasePressure >= 23) ? null : ((data.releasePressure >= 17) ? 'average' : 'bad'))}} -
    - {{:helper.link('-', null, {'dist_p' : -10}, (data.releasePressure > 0) ? null : 'disabled')}} - {{:helper.link('-', null, {'dist_p' : -1}, (data.releasePressure > 0) ? null : 'disabled')}} -
     {{:data.releasePressure}} kPa 
    - {{:helper.link('+', null, {'dist_p' : 1}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'dist_p' : 10}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('Max', null, {'dist_p' : 'max'}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('Reset', null, {'dist_p' : 'reset'}, (data.releasePressure != data.defaultReleasePressure) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Mask Release Valve: -
    -
    - {{:helper.link('Open', 'unlock', {'stat' : 1}, (!data.maskConnected) ? 'disabled' : (data.valveOpen ? 'selected' : null))}}{{:helper.link('Close', 'lock', {'stat' : 1}, data.valveOpen ? null : 'selected')}} -
    -
    - diff --git a/nano/templates/transfer_valve.tmpl b/nano/templates/transfer_valve.tmpl deleted file mode 100644 index be64b5cc6f4..00000000000 --- a/nano/templates/transfer_valve.tmpl +++ /dev/null @@ -1,56 +0,0 @@ -
    -
    - Attachment One: -
    -
    - {{if data.attachmentOne}} - {{:data.attachmentOne}} - {{else}} - None - {{/if}} - {{:helper.link('Remove', 'eject', {'tankone' : 1}, data.attachmentOne ? null : 'disabled')}} -
    -
    - -
    -
    - Attachment Two: -
    -
    - {{if data.attachmentTwo}} - {{:data.attachmentTwo}} - {{else}} - None - {{/if}} - {{:helper.link('Remove', 'eject', {'tanktwo' : 1}, data.attachmentTwo ? null : 'disabled')}} -
    -
    - -
    -
    - Valve Attachment: -
    -
    - {{if data.valveAttachment}} - {{:data.valveAttachment}} - {{else}} - None - {{/if}} - {{:helper.link('Remove', 'eject', {'rem_device' : 1}, data.valveAttachment ? null : 'disabled')}} - {{if data.valveAttachment}} - {{:helper.link('View', 'wrench', {'device' : 1})}} - {{/if}} -
    -
    - -
     
    - -
    -
    - Valve Status: -
    -
    - {{:helper.link('Open', 'unlock', {'open' : 1}, (!data.attachmentOne || !data.attachmentTwo) ? 'disabled' : (data.valveOpen ? 'selected' : null))}}{{:helper.link('Close', 'lock', {'open' : 1}, (!data.attachmentOne || !data.attachmentTwo) ? 'disabled' : (data.valveOpen ? null : 'selected'))}} -
    -
    - diff --git a/nano/templates/turret_control.tmpl b/nano/templates/turret_control.tmpl deleted file mode 100644 index 50f733ea80f..00000000000 --- a/nano/templates/turret_control.tmpl +++ /dev/null @@ -1,72 +0,0 @@ -
    - Behaviour controls are {{:data.locked ? "locked" : "unlocked"}}. -
    -
    -{{if data.access}} - {{if !data.screen}} -
    -
    - Turret Status: -
    -
    - {{:helper.link('Enabled', null, {'command' : 'enable', 'value' : 1}, data.enabled ?'redButton' : null)}} - {{:helper.link('Disabled',null, {'command' : 'enable', 'value' : 0}, !data.enabled ? 'selected' : null)}} -
    -
    - {{if data.accesses}} -
    -
    - Required Access: -
    -
    - {{:helper.link('Modify', null, {'command' : 'screen', 'value' : 1})}} -
    -
    - {{/if}} - {{if data.lethal_control}} -
    -
    - Lethal Mode: -
    -
    - {{:helper.link('On', null, {'command' : 'lethal', 'value' : 1}, data.lethal ?'redButton' : null)}} - {{:helper.link('Off',null, {'command' : 'lethal', 'value' : 0}, !data.lethal ? 'selected' : null)}} -
    -
    - {{/if}} - - {{for data.settings}} -
    -
    - {{:value.category}} -
    -
    - {{:helper.link('On', null, {'command' : value.setting, 'value' : 1}, value.value ? 'selected' : null)}} - {{:helper.link('Off',null, {'command' : value.setting, 'value' : 0}, !value.value ? 'selected' : null)}} -
    -
    - {{/for}} - {{else}} -
    -
    - Turret Settings: -
    -
    - {{:helper.link('Modify', null, {'command' : 'screen', 'value' : 0})}} -
    -
    -
    -
    - Access Type: -
    -
    - {{:helper.link('All', null, {'one_access' : 0}, !data.one_access ? 'selected' : null)}} - {{:helper.link('One', null, {'one_access' : 1}, data.one_access ? 'selected' : null)}} -
    -
    -

    Select Access

    - {{for data.accesses}} - {{:helper.link(value.name, null, {'access' : value.number}, null, value.active ? 'selected' : null)}} - {{/for}} - {{/if}} -{{/if}} diff --git a/nano/templates/vending_machine.tmpl b/nano/templates/vending_machine.tmpl deleted file mode 100644 index 1849e3e51d5..00000000000 --- a/nano/templates/vending_machine.tmpl +++ /dev/null @@ -1,68 +0,0 @@ - - -{{if data.mode == 0}} -

    Items available

    -
    - {{for data.products}} -
    -
    - {{if value.price > 0}} - {{:helper.link('Buy (' + value.price + ')', 'shopping-cart', { "vend" : value.key }, value.amount > 0 ? null : 'disabled')}} - {{else}} - {{:helper.link('Vend', 'arrow-circle-down', { "vend" : value.key }, value.amount > 0 ? null : 'disabled')}} - {{/if}} -
    -
    - {{if value.color}}{{:value.name}} - {{else}}{{:value.name}} - {{/if}} - ({{:value.amount ? value.amount : "NONE LEFT"}}) -
    -
    - {{empty}} - No items available! - {{/for}} -
    -{{if data.coin}} -

    Coin

    -
    -
    Coin deposited:
    -
    {{:helper.link(data.coin, 'eject', {'remove_coin' : 1})}}
    -
    -{{/if}} -{{if data.item_slot == 1}} -

    Item Slot

    -
    -
    Item Slot:
    - {{if data.inserted_item}} -
    {{:helper.link(data.inserted_item, 'eject', {'remove_item' : 1})}}
    - {{else}} -
    Empty
    - {{/if}} -
    -{{/if}} -{{else data.mode == 1}} -

    Item selected

    -
    -
    -
    Item selected:
    {{:data.product}}
    -
    Charge:
    {{:data.price}}
    -
    -
    - {{if data.message_err}} {{/if}} {{:data.message}} -
    -
    - {{:helper.link('Pay', 'shopping-cart', {'pay' : 1})}} - {{:helper.link('Cancel', 'arrow-left', {'cancelpurchase' : 1})}} -
    -
    -{{/if}} -{{if data.panel}} -

    Maintenance panel

    -
    -
    Speaker
    {{:helper.link(data.speaker ? 'Enabled' : 'Disabled', 'gear', {'togglevoice' : 1})}}
    -
    -{{/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}} - - - - - {{/for}} -
    {{: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})}} -
    -
    -{{if data.status_len}} -
    -
    -
    - {{for data.status}} -
    {{:value}}
    - {{/for}} -
    -
    -{{/if}} diff --git a/paradise.dme b/paradise.dme index abbedaf2208..4a5c6350f91 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" @@ -71,9 +72,11 @@ #include "code\__DEFINES\station_goals.dm" #include "code\__DEFINES\status_effects.dm" #include "code\__DEFINES\subsystems.dm" +#include "code\__DEFINES\tgui.dm" #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" @@ -215,10 +218,10 @@ #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" +#include "code\controllers\subsystem\ghost_spawns.dm" #include "code\controllers\subsystem\holiday.dm" #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\idlenpcpool.dm" @@ -238,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" @@ -250,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" @@ -271,6 +276,7 @@ #include "code\datums\hud.dm" #include "code\datums\log_record.dm" #include "code\datums\log_viewer.dm" +#include "code\datums\logging.dm" #include "code\datums\mind.dm" #include "code\datums\mixed.dm" #include "code\datums\mutable_appearance.dm" @@ -303,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" @@ -726,6 +733,7 @@ #include "code\game\machinery\computer\robot.dm" #include "code\game\machinery\computer\salvage_ship.dm" #include "code\game\machinery\computer\security.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" @@ -875,7 +883,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" @@ -936,6 +943,7 @@ #include "code\game\objects\items\tools\wrench.dm" #include "code\game\objects\items\weapons\AI_modules.dm" #include "code\game\objects\items\weapons\alien_specific.dm" +#include "code\game\objects\items\weapons\batons.dm" #include "code\game\objects\items\weapons\bee_briefcase.dm" #include "code\game\objects\items\weapons\cards_ids.dm" #include "code\game\objects\items\weapons\cash.dm" @@ -984,7 +992,6 @@ #include "code\game\objects\items\weapons\staff.dm" #include "code\game\objects\items\weapons\stock_parts.dm" #include "code\game\objects\items\weapons\stunbaton.dm" -#include "code\game\objects\items\weapons\swords_axes_etc.dm" #include "code\game\objects\items\weapons\tape.dm" #include "code\game\objects\items\weapons\teleportation.dm" #include "code\game\objects\items\weapons\teleprod.dm" @@ -1080,7 +1087,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" @@ -1202,8 +1208,8 @@ #include "code\modules\admin\verbs\adminjump.dm" #include "code\modules\admin\verbs\adminpm.dm" #include "code\modules\admin\verbs\adminsay.dm" -#include "code\modules\admin\verbs\alt_check.dm" #include "code\modules\admin\verbs\antag-ooc.dm" +#include "code\modules\admin\verbs\asays.dm" #include "code\modules\admin\verbs\atmosdebug.dm" #include "code\modules\admin\verbs\BrokenInhands.dm" #include "code\modules\admin\verbs\cinematic.dm" @@ -1231,7 +1237,6 @@ #include "code\modules\admin\verbs\randomverbs.dm" #include "code\modules\admin\verbs\serialization.dm" #include "code\modules\admin\verbs\space_transitions.dm" -#include "code\modules\admin\verbs\spawnfloorcluwne.dm" #include "code\modules\admin\verbs\striketeam.dm" #include "code\modules\admin\verbs\striketeam_syndicate.dm" #include "code\modules\admin\verbs\ticklag.dm" @@ -1241,14 +1246,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" @@ -1609,6 +1606,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" @@ -2066,7 +2082,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" @@ -2104,7 +2119,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" @@ -2441,6 +2455,7 @@ #include "code\modules\telesci\telepad.dm" #include "code\modules\telesci\telesci_computer.dm" #include "code\modules\tgui\external.dm" +#include "code\modules\tgui\modal.dm" #include "code\modules\tgui\states.dm" #include "code\modules\tgui\tgui.dm" #include "code\modules\tgui\modules\_base.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/machines/machine_vend.ogg b/sound/machines/machine_vend.ogg new file mode 100644 index 00000000000..92867a1f3d3 Binary files /dev/null and b/sound/machines/machine_vend.ogg 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/strings/tips.txt b/strings/tips.txt index b2df1c17482..4218e01bbfe 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -1,4 +1,4 @@ -Where the space map levels connect is randomized every round, but are otherwise kept consistent within rounds. +Space map levels' connections are randomized between rounds, but are otherwise kept consistent in the same round. You can catch thrown items by toggling on your throw mode with an empty hand active. To crack the safe in the vault, use a stethoscope or thermal drill. You can climb onto a table by dragging yourself onto one. This takes some time. Clicking on a table that someone else is climbing onto will knock them down. @@ -9,8 +9,8 @@ You can change the control scheme by pressing tab. One uses WASD for movement, w Firesuits and winter coats offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. Glass shards can be welded to make glass, and metal rods can be welded to make metal. Ores can be welded too, but this takes a lot of fuel. If you need to drag multiple people either to safety or to space, bring a locker over and stuff them all in before hauling them off. -You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place them on a table by clicking on it, or throw them by toggling on throwing. -Holding alt and left clicking a tile will allow you to see its contents in the top right window pane, which is much faster than right clicking. +You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on the grab itself. An aggressive grab will allow you to place them on a table by clicking on it, or throw them by toggling on throwing. +Holding alt and left clicking a tile will allow you to see its contents in the top right window panel, which is much faster than right clicking. The resist button will allow you to resist out of handcuffs, being buckled to a chair or bed, out of locked lockers and more. Whenever you're stuck, try resisting! You can move an item out of the way by dragging it and then clicking on an adjacent tile with an empty hand. You can recolor certain items like jumpsuits and gloves in washing machines by also throwing in a crayon. @@ -21,7 +21,7 @@ When in doubt about technicial issues, clear your cache (byond launcher > cogwhe Most things have special interactions with your middle mouse button, alt, shift, and control click. Experiment! If you find yourself in a fistfight with another player, running away and calling for help is a perfectly viable option. Different weapons have different strengths. Some weapons, such as spears, floor tiles, and throwing stars, deal more damage when thrown compared to when attacked normally. -A thrown glass of water can make a slippery tile, allowing you to slow down your pursuers in a pinch. +Clicking on a tile on harm intent with a glass of water can make a slippery tile, allowing you to slow down your pursuers in a pinch. When dealing with security, you can often get your sentence negated entirely through cooperation and deception. The P2P chat function found on tablet computers allows for a stealthy way to communicate with people. We were all new once, be patient and guide new players in the right direction. @@ -36,8 +36,8 @@ As the Captain, you have absolute access and control over the station, but this As the Chief Medical Officer, your hypospray is like a refillable instant injection syringe that can hold 30 units and the unlike standard hypospray, yours is able to be filled with harmful reagents and injects without telling anyone what have you exactly injected the person with. As the Chief Medical Officer, coordinate and communicate with your doctors, chemists, and geneticists during a nuclear emergency, blob infestation, or some other crisis to keep people alive and fighting. As a Medical Doctor, you can surgically implant or extract things from people's chests. This can range from putting in a bomb to pulling out an alien larva. -As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. Using disarm intent will intentionally fail the surgery step. -As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable. +As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. +As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable right now, but they might be clonable later. As a Medical Doctor, treating plasmamen is not impossible! Salbutamol stops them from suffocating and showers stop them from burning alive. You can even perform surgery on them by doing the procedure on a roller bed under a shower. As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. Experiment! As a Chemist, some chemicals can only be synthesized by heating up the contents with a chemical heater or manually with lighters and similar tools. @@ -50,7 +50,7 @@ As the Research Director, you can take AIs out of their cores by loading them in As the Research Director, you can lock down cyborgs instead of blowing them up. Then you can have their laws reset or if that doesn't work, safely dismantled. As the Research Director, you can spy on and even forge PDA communications with the message monitor console! The key is in your office. As a Scientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract. -As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device, or if researched, hit the anomaly with an anomaly analyzer. This will leave behind an anomaly core, which can be used to construct a Phazon mech or reactive armors! +As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device. This will leave behind an anomaly core, which can be used to construct a Phazon mech! As a Scientist, researchable stock parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions. As a Roboticist, keep an ear out for anomaly announcements. If you get your hands on an anomaly core, you can build a Phazon mech! As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil. @@ -59,12 +59,12 @@ As a Roboticist, you can augment people with cyborg limbs. Augmented limbs can e As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them, they aren't wearing agent IDs, or aren't using digital camouflage as changelings. As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt. As the AI, you can take pictures with your camera and upload them to newscasters. -As a Cyborg, choose your module carefully, as only cutting and mending your reset wire or using a cyborg reset module will let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs. +As a Cyborg, choose your module carefully, as only a cyborg reset module will let you repick it. As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands. As a Cyborg, you are impervious to fires and heat. If you are rogue, you can release plasma fires everywhere and walk through them without a care in the world! As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds. -As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them. -As a Medical Cyborg, you can fully perform surgery and even augment people. +As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by using your magnetic gripper. +As a Medical Cyborg, you can partially perform surgery, as you cannot replace organs, but you cannot fail any surgery steps. As a Janitor Cyborg, you are the bane of all slaughter demons. Cleaning up blood stains will severely gimp them. As the Chief Engineer, you can rename areas or create entirely new ones using your station blueprints. As the Chief Engineer, your hardsuit is significantly better than everybody else's. It has the best features of both engineering and atmospherics hardsuits, boasting nigh-invulnerability to radiation and all atmospheric conditions. @@ -73,10 +73,10 @@ As an Engineer, you can electrify grilles by placing wire "nodes" beneath them: As an Engineer, return to Engineering once in a while to check on the engine and SMES cells. It's always a good idea to make sure containment isn't compromised. As an Engineer, you can power the station solely with the solar arrays. They will provide just enough electricity to power the station, however their output is still much worse compared to the true engine. As an Engineer, you can cool a supermatter shard by spraying it with a fire extinguisher. Only for the brave! -As an Engineer, you can repair windows by using a welding tool on them while on any intent other than harm. +As an Engineer, you can repair windows by using a welding tool on them while on help intent. As an Engineer, you can lock APCs, fire alarms, emitters, and radiation collectors using your ID card to prevent others from disabling them. As an Engineer, don't underestimate the humble P.A.C.M.A.N. generators. With upgraded parts, a couple units working in tandem are sufficient to take over for an exploded engine or shattered solars. -As an Engineer, you can pry open secure storage blast doors by disabling the engine room APC's main breaker. This is obviously a bad idea if the engine is running. +As an Engineer, you can pry open secure storage blast doors by turning off the secure storage APC's enviroment power channel. As an Atmospheric Technician, look into replacing your gas pumps with volumetric gas pumps, as those move air in flat numerical amounts, rather than percentages which leave trace gases. As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, backpack firefighter tanks, and a completely heat and fire proof hardsuit. As an Atmospheric Technician, your backpack firefighter tank can launch cryofrost. This resin will extinguish fires and very quickly; it's ideal for plasma fires! @@ -88,7 +88,7 @@ As the Warden, keep a close eye on the armory at all times, as it is a favored s As the Warden, if a prisoner's crimes are heinous enough you can put them in permabrig or the gulag. Make sure to check on them once in a while! As the Warden, you can implant criminals you suspect might re-offend with devices that will track their location and allow you to remotely inject them with disabling chemicals. As a Security Officer, communicate and coordinate with your fellow officers using the security channel (:s) to avoid confusion. -As a Security Officer, your sechuds or HUDsunglasses can not only see crewmates' job assignments and criminal status, but also if they are mindshield implanted. You can tell by the flashing blue outline around their job icon. +As a Security Officer, your sechuds or HUDsunglasses let you see crewmates' job assignments, their criminal status, and whether they have a mindshield or not. A flashing green border around their job icon means they are mindshielded. As a Security Officer, mindshield implants can only prevent someone from being turned into a cultist. It will not de-cult them if they have already been converted. As a Security Officer, examining someone while wearing sechuds or HUDsunglasses will let you set their arrest level, which will cause Beepsky and other security bots to chase after them. As the Detective, keep in mind that people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department. @@ -111,7 +111,7 @@ As the Chaplain, you are much more likely to get a response by praying to the go As a Botanist, you can hack the MegaSeed Vendor to get access to more exotic seeds. These seeds can alternatively be ordered from cargo. As a Botanist, you can mutate the plants growing in your hydroponics trays with unstable mutagen or, as an alternative, crude radioactives from chemistry to get special variations. As a Botanist, you should look into increasing the potency of your plants. This increases the size, amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist. -As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine (blumpkin) + radium and phosphorus (glowshrooms) equals unstable mutagen! +As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine and plasma (blumpkin) + radium (glowshrooms) equals unstable mutagen! As the Chef, you can create a very wide variety of food with the crafting menu. You can find it by looking for the hammer icon near your intents. As the Chef, you can rename your custom made food with a pen. As the Chef, if you are low on ingredients, consider making something that has several servings to last longer among the crew. @@ -121,8 +121,8 @@ As a Janitor, mousetraps can be used to create bombs or booby-trap containers. As the Librarian, be sure to keep the shelves stocked and the library clean for crew. As a Cargo Technician, you can hack MULEbots to make them faster, run over people in their way, and even let you ride them! As a Cargo Technician, you can order contraband items from the supply shuttle console by de-constructing it and using a multitool on the circuit board, the re-assembling it. -As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, liquid containers, plasma sheets, rare seeds from hydroponics, and more! -As a Shaft Miner, the western side of the Asteroid has a lot more rare minerals than on the east, but is a lot more dangerous. +As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, plasma sheets, rare seeds from hydroponics, technology disks from R&D, and more! +As a Shaft Miner, the north side of Lavaland has a lot more rare minerals than on the south, but is a lot more dangerous. As a Shaft Miner, always have a GPS on you, so a fellow miner or cyborg can come to save you if you die. As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you can even order more traitor gear or contact the Syndicate. Experiment! As a Traitor, subverting the AI to serve you can make it an extremely powerful ally. However, be careful of the wording in the laws you give it, as it may use your poorly written laws against you! @@ -130,14 +130,14 @@ As a Traitor, the Captain and the Head of Security are two of the most difficult As a Traitor, you can manufacture and recycle revolver bullets at a hacked autolathe, making the revolver an extremely powerful tool. As a Traitor, you may sometimes be assigned to hunt other traitors, and in turn be hunted by others. As a Traitor, the syndicate encryption key is very useful for coordinating plans with your fellow traitors -- or, of course, betraying them. -As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, welding tools, cigars and e-cigs will all explode when used. +As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, cigars and e-cigs will all explode when used. As a Traitor, if you can find another Traitor and pool your TC you can buy a mega surplus crate, which costs 40TC but contains a lot of random syndicate gear. As a Nuclear Operative, communication is key! Use ; to speak to your fellow operatives and coordinate an attack plan. As a Nuclear Operative, you should look into purchasing a syndicate cyborg, as they can provide heavy fire support, full access, are immune to conventional stuns, and can easily take down the AI. As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire. As a Nuclear Operative, you might end up in a situation where the AI has bolted you into a room. Having some spare C4 in your pocket can save your life. As a Monkey, you can still wear a few human items, such as backpacks, gas masks, and hats, and still have two free hands. -As the Malfunctioning AI, you can shunt to an APC if the situation gets bad. This disables your doomsday device if it is active. +As the Malfunctioning AI, you can shunt to an APC if the situation gets bad. As the Malfunctioning AI, you should either order your cyborgs to dismantle the robotics console or blow it up yourself in order to protect them. As an Alien, your melee prowess is unmatched, but your ranged abilities are sorely lacking. Make use of corners to force a melee confrontation! As an Alien, you take double damage from all burn attacks, such as lasers, welding tools, and fires. Furthermore, fire can destroy your resin and eggs. Expose areas to space to starve away any flamethrower fires before they can do damage! @@ -155,11 +155,10 @@ As a Changeling, the Extract DNA sting counts for your genome absorb objective, As a Changeling, you can absorb someone by strangling them and using the Absorb verb; this gives you the ability to rechoose your powers, the DNA of whoever you absorbed, the memory of the absorbed, and some samples of things the absorbed said. As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win. As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face. -As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists of Nar'Sie nearby, but will create a fire where the rune stands on use. +As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists nearby, but will create a fire where the rune stands on use. As a Cultist, you can create an army of manifested goons using a combination of the Manifest rune, which creates homunculi from ghosts, and the Blood Drain rune, which drains life from anyone standing on any blood drain rune. -As a Cultist, check the alert in the upper-right of your screen for all the details about your cult's current status and objective. You can deconvert Cultists by feeding them large amounts of holy water. -The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against both cults and large amounts of it are a great contributor to success against them. +The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against cults and large amounts of it are a great contributor to success against them. As a Wizard, you can turn people to stone, then animate the resulting statue with a staff of animation to create an extremely powerful minion, for all of 5 minutes at least. As a Wizard, the fireball spell performs very poorly at close range, as it can easily catch you in the blast. It is best used as a form of artillery down long hallways. As a Wizard, summoning guns will give everyone anything from a floral somatoray to a pulse rifle. Use at your own risk! @@ -170,11 +169,10 @@ As an Abductor Agent, the combat mode vest has much higher resistance to every k As an Abductor, the baton can cycle between four modes: stun, sleep, cuff and probe. As a Revenant, the Chaplain is your worst enemy, as they can damage you massively with the null rod and make large swaths of the station impassable with holy water. As a Revenant, your essence is also your health, so revealing yourself in front of humans to harvest the essence of the living is much safer if you've already stocked up on essences from poorly guarded corpses. -As a Revenant, your Defile ability removes holy water from tiles in a small radius, along with salt, allowing you to reclaim the station from the chaplain if they've been covering the station in holy water. It can also be used to open morgue trays! +As a Revenant, your Defile ability removes holy water from tiles in a small radius, allowing you to reclaim the station from the chaplain if they've been covering the station in holy water. As a Revenant, your Overload Lights ability will only shock humans with lights if the lights are still on after a brief delay. As a Revenant, your Malfunction ability in general damages machinery and mechanical objects, possibly even emagging some objects. Experiment! -As a Revenant, the illness inflicted on humans by Blight can be easily cured by lying down or with holy water, making it best used on targets that have no time to lie down, such as humans in combat. -As a Revenant, fastmos is your friend. Breaking windows to space can cause massive damage and mayhem. +As a Revenant, space is your friend. Breaking windows to space can cause massive damage and mayhem. As a Swarmer, you can deconstruct more things than you think. Try deconstructing light switches, buttons, air alarms and more. Experiment! As a Swarmer, you can teleport fellow swarmers away if you think they are in danger. As a Ghost, you can double click on just about anything to follow it. Or just warp around! @@ -202,4 +200,4 @@ You can make lasertag turrets, for the ultimate lasertag tournament. Blob structures take half damage from brute damage. Use lasers. You can hide paper in vents, but you have to use a screwdriver to open it first. While the Standard Operating Procedures aren't fully rules, they are there for safety and professional reasons. -Killing the Wizard usually ends the round, unless they are a lich or Space Wizard Federation is RAGING. \ No newline at end of file +Killing the Wizard usually ends the round, unless they are a lich or Space Wizard Federation is RAGING. diff --git a/tgui/packages/tgui/components/Box.js b/tgui/packages/tgui/components/Box.js index 9d5e71c78a7..ada17f05d55 100644 --- a/tgui/packages/tgui/components/Box.js +++ b/tgui/packages/tgui/components/Box.js @@ -68,7 +68,10 @@ const mapColorPropTo = attrName => (style, value) => { const styleMapperByPropName = { // Direct mapping + display: mapRawPropTo('display'), position: mapRawPropTo('position'), + float: mapRawPropTo('float'), + clear: mapRawPropTo('clear'), overflow: mapRawPropTo('overflow'), overflowX: mapRawPropTo('overflow-x'), overflowY: mapRawPropTo('overflow-y'), @@ -88,6 +91,9 @@ const styleMapperByPropName = { opacity: mapRawPropTo('opacity'), textAlign: mapRawPropTo('text-align'), verticalAlign: mapRawPropTo('vertical-align'), + textTransform: mapRawPropTo('text-transform'), + wordWrap: mapRawPropTo('word-wrap'), + textOverflow: mapRawPropTo('text-overflow'), // Boolean props inline: mapBooleanPropTo('display', 'inline-block'), bold: mapBooleanPropTo('font-weight', 'bold'), @@ -125,6 +131,18 @@ const styleMapperByPropName = { color: mapColorPropTo('color'), textColor: mapColorPropTo('color'), backgroundColor: mapColorPropTo('background-color'), + // Flex props + order: mapRawPropTo('order'), + flexDirection: mapRawPropTo('flex-direction'), + flexGrow: mapRawPropTo('flex-grow'), + flexShrink: mapRawPropTo('flex-shrink'), + flexWrap: mapRawPropTo('flex-wrap'), + flexFlow: mapRawPropTo('flex-flow'), + flexBasis: mapRawPropTo('flex-basis'), + flex: mapRawPropTo('flex'), + alignItems: mapRawPropTo('align-items'), + justifyContent: mapRawPropTo('justify-content'), + alignSelf: mapRawPropTo('align-self'), // Utility props fillPositionedParent: (style, value) => { if (value) { @@ -140,6 +158,9 @@ const styleMapperByPropName = { export const computeBoxProps = props => { const computedProps = {}; const computedStyles = {}; + if (props.double) { + computedStyles["transform"] = "scale(2);"; + } // Compute props for (let propName of Object.keys(props)) { if (propName === 'style') { 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 ( - +
    ); } } diff --git a/tgui/packages/tgui/components/Flex.js b/tgui/packages/tgui/components/Flex.js index e63051438f3..26de30ef93c 100644 --- a/tgui/packages/tgui/components/Flex.js +++ b/tgui/packages/tgui/components/Flex.js @@ -8,9 +8,11 @@ export const computeFlexProps = props => { direction, wrap, align, + alignContent, justify, inline, spacing = 0, + spacingPrecise = 0, ...rest } = props; return { @@ -23,6 +25,7 @@ export const computeFlexProps = props => { ), inline && 'Flex--inline', spacing > 0 && 'Flex--spacing--' + spacing, + spacingPrecise > 0 && 'Flex--spacingPrecise--' + spacingPrecise, className, ]), style: { @@ -30,6 +33,7 @@ export const computeFlexProps = props => { 'flex-direction': direction, 'flex-wrap': wrap, 'align-items': align, + 'align-content': alignContent, 'justify-content': justify, }, ...rest, diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index 3c73d88a8fd..b7f2dcb1f15 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -76,6 +76,11 @@ export class Input extends Component { const input = this.inputRef.current; if (input) { input.value = toInputValue(nextValue); + if (this.props.autofocus) { + input.focus(); + input.selectionStart = 0; + input.selectionEnd = input.value.length; + } } } @@ -104,6 +109,7 @@ export class Input extends Component { value, maxLength, placeholder, + autofocus, ...boxProps } = props; // Box props diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.js index 501308aea8d..0e817111653 100644 --- a/tgui/packages/tgui/components/Knob.js +++ b/tgui/packages/tgui/components/Knob.js @@ -35,6 +35,7 @@ export const Knob = props => { size, bipolar, children, + popUpPosition, ...rest } = props; return ( @@ -102,7 +103,10 @@ export const Knob = props => {
    {dragging && ( -
    +
    {displayElement}
    )} diff --git a/tgui/packages/tgui/components/LabeledList.js b/tgui/packages/tgui/components/LabeledList.js index e2b515014a2..43dbd0d5a32 100644 --- a/tgui/packages/tgui/components/LabeledList.js +++ b/tgui/packages/tgui/components/LabeledList.js @@ -20,6 +20,7 @@ export const LabeledListItem = props => { labelColor = 'label', color, textAlign, + verticalAlign, buttons, content, children, @@ -33,6 +34,7 @@ export const LabeledListItem = props => { { as="td" color={color} textAlign={textAlign} + verticalAlign={verticalAlign} className={classes([ 'LabeledList__cell', 'LabeledList__content', diff --git a/tgui/packages/tgui/components/Modal.js b/tgui/packages/tgui/components/Modal.js index 916150de402..125593902b6 100644 --- a/tgui/packages/tgui/components/Modal.js +++ b/tgui/packages/tgui/components/Modal.js @@ -6,10 +6,20 @@ export const Modal = props => { const { className, children, + onEnter, ...rest } = props; + let handleKeyDown; + if (onEnter) { + handleKeyDown = e => { + if (e.keyCode === 13) { + onEnter(e); + } + }; + } return ( - +
    { level = 1, buttons, content, + stretchContents, + noTopPadding, children, + scrollable, ...rest } = props; const hasTitle = !isFalsy(title) || !isFalsy(buttons); @@ -18,6 +21,7 @@ export const Section = props => { className={classes([ 'Section', 'Section--level--' + level, + props.flexGrow && 'Section--flex', className, ])} {...rest}> @@ -32,10 +36,14 @@ export const Section = props => {
    )} {hasContent && ( -
    + {content} {children} -
    +
    )} ); diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.js index 20ba00cb759..94f32cd2697 100644 --- a/tgui/packages/tgui/constants.js +++ b/tgui/packages/tgui/constants.js @@ -54,6 +54,11 @@ export const RADIO_CHANNELS = [ freq: 1213, color: '#a52a2a', }, + { + name: 'SyndTeam', + freq: 1244, + color: '#a52a2a', + }, { name: 'Red Team', freq: 1215, @@ -65,8 +70,13 @@ export const RADIO_CHANNELS = [ color: '#3434fd', }, { - name: 'CentCom', - freq: 1337, + name: 'Response Team', + freq: 1345, + color: '#2681a5', + }, + { + name: 'Special Ops', + freq: 1341, color: '#2681a5', }, { @@ -106,7 +116,7 @@ export const RADIO_CHANNELS = [ }, { name: 'AI Private', - freq: 1447, + freq: 1343, color: '#d65d95', }, { diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index 59199f9edbe..45012a7e956 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -3,30 +3,30 @@ import 'core-js/es'; import 'core-js/web/immediate'; import 'core-js/web/queue-microtask'; import 'core-js/web/timers'; -import 'regenerator-runtime/runtime'; -import './polyfills/html5shiv'; -import './polyfills/ie8'; -import './polyfills/dom4'; -import './polyfills/css-om'; -import './polyfills/inferno'; - -// Themes -import './styles/main.scss'; -import './styles/themes/cardtable.scss'; -import './styles/themes/malfunction.scss'; -import './styles/themes/ntos.scss'; -import './styles/themes/hackerman.scss'; -import './styles/themes/retro.scss'; -import './styles/themes/syndicate.scss'; - import { loadCSS } from 'fg-loadcss'; import { render } from 'inferno'; +import 'regenerator-runtime/runtime'; import { setupHotReloading } from 'tgui-dev-server/link/client'; import { backendUpdate } from './backend'; import { IS_IE8 } from './byond'; import { setupDrag } from './drag'; import { logger } from './logging'; +import './polyfills/css-om'; +import './polyfills/dom4'; +import './polyfills/html5shiv'; +import './polyfills/ie8'; +import './polyfills/inferno'; import { createStore, StoreProvider } from './store'; +// Themes +import './styles/main.scss'; +import './styles/themes/cardtable.scss'; +import './styles/themes/hackerman.scss'; +import './styles/themes/malfunction.scss'; +import './styles/themes/ntos.scss'; +import './styles/themes/retro.scss'; +import './styles/themes/syndicate.scss'; + + const enteredBundleAt = Date.now(); const store = createStore(); diff --git a/tgui/packages/tgui/interfaces/APC.js b/tgui/packages/tgui/interfaces/APC.js new file mode 100644 index 00000000000..652188a3073 --- /dev/null +++ b/tgui/packages/tgui/interfaces/APC.js @@ -0,0 +1,195 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components'; +import { Window } from '../layouts'; +import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; + +export const APC = (props, context) => { + return ( + + + + + + ); +}; + +const powerStatusMap = { + 2: { + color: 'good', + externalPowerText: 'External Power', + chargingText: 'Fully Charged', + }, + 1: { + color: 'average', + externalPowerText: 'Low External Power', + chargingText: 'Charging', + }, + 0: { + color: 'bad', + externalPowerText: 'No External Power', + chargingText: 'Not Charging', + }, +}; + +const malfMap = { + 1: { + icon: 'terminal', + content: 'Override Programming', + action: 'hack', + }, + 2: { + icon: 'caret-square-down', + content: 'Shunt Core Process', + action: 'occupy', + }, + 3: { + icon: 'caret-square-left', + content: 'Return to Main Core', + action: 'deoccupy', + }, + 4: { + icon: 'caret-square-down', + content: 'Shunt Core Process', + action: 'occupy', + }, +}; + +const ApcContent = (props, context) => { + const { act, data } = useBackend(context); + const locked = data.locked && !data.siliconUser; + const normallyLocked = data.normallyLocked; + const externalPowerStatus = powerStatusMap[data.externalPower] + || powerStatusMap[0]; + const chargingStatus = powerStatusMap[data.chargingStatus] + || powerStatusMap[0]; + const channelArray = data.powerChannels || []; + const malfStatus = malfMap[data.malfStatus] || malfMap[0]; + const adjustedCellChange = data.powerCellStatus / 100; + + return ( + + +
    + + act('breaker')} /> + )}> + [ {externalPowerStatus.externalPowerText} ] + + + + + act('charge')} /> + )}> + [ {chargingStatus.chargingText} ] + + +
    +
    + + {channelArray.map(channel => { + const { topicParams } = channel; + return ( + + = 2 ? 'good' : 'bad'}> + {channel.status >= 2 ? 'On' : 'Off'} + +
    +
    + {!!data.malfStatus && ( +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/AiAirlock.js b/tgui/packages/tgui/interfaces/AiAirlock.js new file mode 100644 index 00000000000..ee47422d32d --- /dev/null +++ b/tgui/packages/tgui/interfaces/AiAirlock.js @@ -0,0 +1,202 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, LabeledList, Section } from '../components'; +import { Window } from '../layouts'; + +const dangerMap = { + 2: { + color: 'good', + localStatusText: 'Offline', + }, + 1: { + color: 'average', + localStatusText: 'Caution', + }, + 0: { + color: 'bad', + localStatusText: 'Optimal', + }, +}; + +export const AiAirlock = (props, context) => { + const { act, data } = useBackend(context); + const statusMain = dangerMap[data.power.main] || dangerMap[0]; + const statusBackup = dangerMap[data.power.backup] || dangerMap[0]; + const statusElectrify = dangerMap[data.shock] || dangerMap[0]; + return ( + + +
    + + act('disrupt-main')} /> + )}> + {data.power.main ? 'Online' : 'Offline'} + {' '} + {(!data.wires.main_power) + && '[Wires have been cut!]' + || (data.power.main_timeleft > 0 + && `[${data.power.main_timeleft}s]`)} + + act('disrupt-backup')} /> + )}> + {data.power.backup ? 'Online' : 'Offline'} + {' '} + {(!data.wires.backup_power) + && '[Wires have been cut!]' + || (data.power.backup_timeleft > 0 + && `[${data.power.backup_timeleft}s]`)} + + +
    +
    + + act('idscan-toggle')} /> + )}> + {!data.wires.id_scanner && '[Wires have been cut!]'} + + act('emergency-toggle')} /> + )} /> + + act('bolt-toggle')} /> + )}> + {!data.wires.bolts && '[Wires have been cut!]'} + + act('light-toggle')} /> + )}> + {!data.wires.lights && '[Wires have been cut!]'} + + act('safe-toggle')} /> + )}> + {!data.wires.safe && '[Wires have been cut!]'} + + act('speed-toggle')} /> + )}> + {!data.wires.timing && '[Wires have been cut!]'} + + + act('open-close')} /> + )}> + {!!(data.locked || data.welded) && ( + + [Door is {data.locked ? 'bolted' : ''} + {(data.locked && data.welded) ? ' and ' : ''} + {data.welded ? 'welded' : ''}!] + + )} + + +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.js b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js new file mode 100644 index 00000000000..8db992f61b5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js @@ -0,0 +1,47 @@ +import { useBackend } from '../backend'; +import { Button, Section } from '../components'; +import { Window } from '../layouts'; + +export const AtmosAlertConsole = (props, context) => { + const { act, data } = useBackend(context); + const priorityAlerts = data.priority || []; + const minorAlerts = data.minor || []; + return ( + + +
    +
      + {priorityAlerts.length === 0 && ( +
    • + No Priority Alerts +
    • + )} + {priorityAlerts.map(alert => ( +
    • +
    • + ))} + {minorAlerts.length === 0 && ( +
    • + No Minor Alerts +
    • + )} + {minorAlerts.map(alert => ( +
    • +
    • + ))} +
    +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BlueSpaceArtilleryControl.js b/tgui/packages/tgui/interfaces/BlueSpaceArtilleryControl.js new file mode 100644 index 00000000000..adc5b210aab --- /dev/null +++ b/tgui/packages/tgui/interfaces/BlueSpaceArtilleryControl.js @@ -0,0 +1,66 @@ +import { useBackend } from "../backend"; +import { Button, LabeledList, Section, Box, ProgressBar } from "../components"; +import { Window } from "../layouts"; + +export const BlueSpaceArtilleryControl = (props, context) => { + const { act, data } = useBackend(context); + let alertStatus; + if (data.ready) { + alertStatus = ( + + Ready + + ); + } else if (data.reloadtime_text) { + alertStatus = ( + + {data.reloadtime_text} + + ); + } else { + alertStatus = ( + + No cannon connected! + + ); + } + return ( + + +
    + + {data.notice && ( + + {data.notice} + + )} + {alertStatus} + +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner.js b/tgui/packages/tgui/interfaces/BodyScanner.js new file mode 100644 index 00000000000..48a4a08e20f --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner.js @@ -0,0 +1,433 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { AnimatedNumber, Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Table, Tooltip } from "../components"; +import { Window } from "../layouts"; + +const stats = [ + ['good', 'Alive'], + ['average', 'Critical'], + ['bad', 'DEAD'], +]; + +const abnormalities = [ + ['hasBorer', 'bad', 'Large growth detected in frontal lobe,' + + ' possibly cancerous. Surgical removal is recommended.'], + ['hasVirus', 'bad', 'Viral pathogen detected in blood stream.'], + ['blind', 'average', 'Cataracts detected.'], + ['colourblind', 'average', 'Photoreceptor abnormalities detected.'], + ['nearsighted', 'average', 'Retinal misalignment detected.'], +]; + +const damages = [ + ['Respiratory', 'oxyLoss'], + ['Brain', 'brainLoss'], + ['Toxin', 'toxLoss'], + ['Radioactive', 'radLoss'], + ['Brute', 'bruteLoss'], + ['Genetic', 'cloneLoss'], + ['Burn', 'fireLoss'], + ['Paralysis', 'paralysis'], +]; + +const damageRange = { + average: [0.25, 0.5], + bad: [0.5, Infinity], +}; + +const mapTwoByTwo = (a, c) => { + let result = []; + for (let i = 0; i < a.length; i += 2) { + result.push(c(a[i], a[i + 1], i)); + } + return result; +}; + +const reduceOrganStatus = A => { + return A.length > 0 + ? A + .filter(s => !!s && s.length > 0) + .reduce((a, s) => a === null + ? [s] + : {a}
    {s}
    , null + ) + : null; +}; + +const germStatus = i => { + if (i > 100) { + if (i < 300) { return "mild infection"; } + if (i < 400) { return "mild infection+"; } + if (i < 500) { return "mild infection++"; } + if (i < 700) { return "acute infection"; } + if (i < 800) { return "acute infection+"; } + if (i < 900) { return "acute infection++"; } + if (i >= 900) { return "septic"; } + } + + return ""; +}; + +export const BodyScanner = (props, context) => { + const { data } = useBackend(context); + const { + occupied, + occupant = {}, + } = data; + const body = occupied ? ( + + ) : ( + + ); + return ( + + + {body} + + + ); +}; + +const BodyScannerMain = props => { + const { + occupant, + } = props; + return ( + + + + + + + + ); +}; + +const BodyScannerMainOccupant = (props, context) => { + const { act, data } = useBackend(context); + const { + occupant, + } = data; + return ( +
    + + + + )}> + + + {occupant.name} + + + + + + {stats[occupant.stat][1]} + + + °C,  + °F + + + {occupant.implant_len ? ( + + {occupant.implant.map(im => im.name).join(', ')} + + ) : ( + + None + + )} + + +
    + ); +}; + +const BodyScannerMainAbnormalities = props => { + const { + occupant, + } = props; + if (!(occupant.hasBorer || occupant.blind + || occupant.colourblind || occupant.nearsighted + || occupant.hasVirus)) { + return ( +
    + + No abnormalities found. + +
    + ); + } + + return ( +
    + {abnormalities.map((a, i) => { + if (occupant[a[0]]) { + return ( + + {a[2]} + + ); + } + })} +
    + ); +}; + +const BodyScannerMainDamage = props => { + const { + occupant, + } = props; + return ( +
    + + {mapTwoByTwo(damages, (d1, d2, i) => ( + + + + {d1[0]}: + + + {!!d2 && d2[0] + ":"} + + + + + + + + {!!d2 && ( + + )} + + + + ))} +
    +
    + ); +}; + +const BodyScannerMainDamageBar = props => { + return ( + + {round(props.value, 0)} + + ); +}; + +const BodyScannerMainOrgansExternal = props => { + if (props.organs.length === 0) { + return ( +
    + + N/A + +
    + ); + } + + return ( +
    + + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + value={o.totalLoss / 100} + ranges={damageRange}> + + {!!o.bruteLoss && ( + + + {round(o.bruteLoss, 0)}  + + )} + {!!o.fireLoss && ( + + + {round(o.fireLoss, 0)} + + )} + + + {round(o.totalLoss, 0)} + + + + + + {reduceOrganStatus([ + o.internalBleeding && "Internal bleeding", + o.lungRuptured && "Ruptured lung", + !!o.status.broken && o.status.broken, + germStatus(o.germ_level), + !!o.open && "Open incision", + ])} + + + {reduceOrganStatus([ + !!o.status.splinted && "Splinted", + !!o.status.robotic && "Robotic", + !!o.status.dead && ( + + DEAD + + ), + ])} + {reduceOrganStatus(o.shrapnel.map( + s => s.known + ? s.name + : "Unknown object" + ))} + + + + ))} +
    +
    + ); +}; + +const BodyScannerMainOrgansInternal = props => { + if (props.organs.length === 0) { + return ( +
    + + N/A + +
    + ); + } + + return ( +
    + + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + ranges={damageRange}> + {round(o.damage, 0)} + + + + + {reduceOrganStatus([ + germStatus(o.germ_level), + ])} + + + {reduceOrganStatus([ + (o.robotic === 1) && "Robotic", + (o.robotic === 2) && "Assisted", + !!o.dead && ( + + DEAD + + ), + ])} + + + + ))} +
    +
    + ); +}; + +const BodyScannerEmpty = () => { + return ( +
    + + +
    + No occupant detected. +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.js new file mode 100644 index 00000000000..8c184f71e68 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Canister.js @@ -0,0 +1,248 @@ +import { toFixed } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { AnimatedNumber, Box, Button, Dropdown, Icon, Knob, LabeledControls, LabeledList, Section, Tooltip } from '../components'; +import { formatSiUnit } from '../format'; +import { Window } from '../layouts'; + +export const Canister = (props, context) => { + const { act, data } = useBackend(context); + const { + portConnected, + tankPressure, + releasePressure, + defaultReleasePressure, + minReleasePressure, + maxReleasePressure, + valveOpen, + name, + canLabel, + colorContainer, + color_index, + hasHoldingTank, + holdingTank, + } = data; + + + let preset_prim = ""; + if (color_index["prim"]) { + preset_prim = colorContainer.prim.options[color_index["prim"]]["name"]; + } + let preset_sec = ""; + if (color_index["sec"]) { + preset_sec = colorContainer.sec.options[color_index["sec"]]["name"]; + } + let preset_ter = ""; + if (color_index["ter"]) { + preset_ter = colorContainer.ter.options[color_index["ter"]]["name"]; + } + let preset_quart = ""; + if (color_index["quart"]) { + preset_quart + = colorContainer.quart.options[color_index["quart"]]["name"]; + } + + let array_prim = []; + let array_sec = []; + let array_ter = []; + let array_quart = []; + let i = 0; + + for (i = 0; i < colorContainer.prim.options.length; i++) { + array_prim.push(colorContainer.prim.options[i]["name"]); + } + for (i = 0; i < colorContainer.sec.options.length; i++) { + array_sec.push(colorContainer.sec.options[i]["name"]); + } + for (i = 0; i < colorContainer.ter.options.length; i++) { + array_ter.push(colorContainer.ter.options[i]["name"]); + } + for (i = 0; i < colorContainer.quart.options.length; i++) { + array_quart.push(colorContainer.quart.options[i]["name"]); + } + let paintSection = ""; + if (canLabel) { + paintSection = ( +
    + + + act('recolor', + { nc: array_prim.indexOf(value), + ctype: "prim" })} /> + + + act('recolor', + { nc: array_sec.indexOf(value), + ctype: "sec" })} /> + + + act('recolor', + { nc: array_ter.indexOf(value), + ctype: "ter" })} /> + + + act('recolor', + { nc: array_quart.indexOf(value), + ctype: "quart" })} /> + + +
    + ); + } + + return ( + + +
    act('relabel')} /> + )}> + + + { + if (value < 10000) { + return toFixed(value) + ' kPa'; + } + return formatSiUnit(value * 1000, 1, 'Pa'); + }} /> + + + + act('pressure', { + pressure: value, + })} /> +
    +
    act('eject')} /> + )}> + {!!hasHoldingTank && ( + + + {holdingTank.name} + + + kPa + + + )} + {!hasHoldingTank && ( + + No Holding Tank + + )} +
    + {paintSection} +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.js b/tgui/packages/tgui/interfaces/ChemDispenser.js new file mode 100644 index 00000000000..7a7300fec23 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser.js @@ -0,0 +1,181 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, LabeledList, ProgressBar, Section } from "../components"; +import { BeakerContents } from "../interfaces/common/BeakerContents"; +import { Window } from "../layouts"; + +const dispenseAmounts = [1, 5, 10, 20, 30, 50]; +const removeAmounts = [1, 5, 10]; + +export const ChemDispenser = (props, context) => { + return ( + + + + + + + + ); +}; + +const ChemDispenserSettings = (properties, context) => { + const { act, data } = useBackend(context); + const { + amount, + energy, + maxEnergy, + } = data; + return ( +
    + + + + {energy} / {maxEnergy} Units + + + + + {dispenseAmounts.map((a, i) => ( + +
    + ); +}; + +const ChemDispenserChemicals = (properties, context) => { + const { act, data } = useBackend(context); + const { + chemicals = [], + } = data; + const flexFillers = []; + for (let i = 0; i < (chemicals.length + 1) % 3; i++) { + flexFillers.push(true); + } + return ( +
    + + {chemicals.map((c, i) => ( + +
    + ); +}; + +const ChemDispenserBeaker = (properties, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents = [], + } = data; + return ( +
    + {!!isBeakerLoaded && ( + + {beakerCurrentVolume} / {beakerMaxVolume} units + + )} +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemHeater.js b/tgui/packages/tgui/interfaces/ChemHeater.js new file mode 100644 index 00000000000..e7ea7a6ace9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemHeater.js @@ -0,0 +1,106 @@ +import { round, toFixed } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { AnimatedNumber, Box, Button, LabeledList, NumberInput, Section } from "../components"; +import { BeakerContents } from '../interfaces/common/BeakerContents'; +import { Window } from "../layouts"; + +export const ChemHeater = (_props, _context) => { + return ( + + + + + + + ); +}; + +const ChemHeaterSettings = (_properties, context) => { + const { act, data } = useBackend(context); + const { + targetTemp, + targetTempReached, + autoEject, + isActive, + currentTemp, + isBeakerLoaded, + } = data; + return ( +
    +
    + ); +}; + +const ChemHeaterBeaker = (_properties, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents, + } = data; + return ( +
    + + {beakerCurrentVolume} / {beakerMaxVolume} units + +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster.js b/tgui/packages/tgui/interfaces/ChemMaster.js new file mode 100644 index 00000000000..d0a70d2b4ce --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster.js @@ -0,0 +1,408 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, Section } from "../components"; +import { Window } from "../layouts"; +import { BeakerContents } from './common/BeakerContents'; +import { ComplexModal, modalOpen, modalRegisterBodyOverride } from './common/ComplexModal'; + +const transferAmounts = [1, 5, 10]; +const bottleStyles = [ + "bottle.png", + "small_bottle.png", + "wide_bottle.png", + "round_bottle.png", + "reagent_bottle.png", +]; + +const analyzeModalBodyOverride = (modal, context) => { + const { act, data } = useBackend(context); + const result = modal.args.analysis; + return ( +
    + + + + {result.name} + + + {(result.desc || "").length > 0 ? result.desc : "N/A"} + + {result.blood_type && ( + + + {result.blood_type} + + + {result.blood_dna} + + + )} + {!data.condi && ( +
    + ); +}; + +export const ChemMaster = (props, context) => { + const { data } = useBackend(context); + const { + condi, + beaker, + beaker_reagents = [], + buffer_reagents = [], + mode, + } = data; + return ( + + + + 0} + /> + + 0} + /> + + + + ); +}; + +const ChemMasterBeaker = (props, context) => { + const { act } = useBackend(context); + const { + beaker, + beakerReagents, + bufferNonEmpty, + } = props; + return ( +
    act('eject')} + /> + :
    + ); +}; + +const ChemMasterBuffer = (props, context) => { + const { act } = useBackend(context); + const { + mode, + bufferReagents = [], + } = props; + return ( +
    + Transferring to  +
    + ); +}; + +const ChemMasterProduction = (props, context) => { + const { act } = useBackend(context); + if (!props.bufferNonEmpty) { + return ( +
    + + +
    + Buffer is empty. +
    +
    +
    + ); + } + + return ( +
    + {!props.isCondiment ? ( + + ) : ( + + )} +
    + ); +}; + +const ChemMasterProductionChemical = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + + + + + + + ); +}; + +const ChemMasterProductionCondiment = (props, context) => { + const { act } = useBackend(context); + return ( + + + + ))} + + ); + } else if (type === "boolean") { + modalFooter = ( + +