diff --git a/.gitignore b/.gitignore
index 56a447fdd1..ef738bcf38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -196,3 +196,5 @@ Temporary Items
/tools/LinuxOneShot/TGS_Config
/tools/LinuxOneShot/TGS_Instances
/tools/LinuxOneShot/TGS_Logs
+tools/UnstandardnessTestForDM/UnstandardnessTestForDM/obj/x86/Debug/UnstandardnessTestForDM.csproj.AssemblyReference.cache
+tools/UnstandardnessTestForDM/UnstandardnessTestForDM/obj/x86/Debug/.NETFramework,Version=v4.0,Profile=Client.AssemblyAttributes.cs
diff --git a/_maps/RandomRuins/StationRuins/Box/Engine/engine_rbmk.dmm b/_maps/RandomRuins/StationRuins/Box/Engine/engine_rbmk.dmm
new file mode 100644
index 0000000000..4c45f7e919
--- /dev/null
+++ b/_maps/RandomRuins/StationRuins/Box/Engine/engine_rbmk.dmm
@@ -0,0 +1,2285 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"au" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 6
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"ax" = (
+/obj/effect/mapping_helpers/airlock/cyclelink_helper,
+/obj/machinery/door/airlock/external{
+ name = "Engineering External Access";
+ req_access_txt = "10;13"
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"az" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 5
+ },
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"aN" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/structure/sign/warning/radiation/rad_area,
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"bf" = (
+/obj/machinery/portable_atmospherics/canister/nitrogen,
+/obj/effect/turf_decal/delivery,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/machinery/conveyor{
+ id = "rbmkn2"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"bQ" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"bU" = (
+/turf/open/pool,
+/area/engineering/main)
+"ce" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"dj" = (
+/obj/effect/turf_decal/stripes/corner{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"dw" = (
+/obj/effect/turf_decal/delivery,
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"dF" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"dK" = (
+/obj/structure/closet/radiation,
+/obj/effect/turf_decal/stripes/line{
+ dir = 10
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"dR" = (
+/obj/machinery/door/airlock/engineering/glass{
+ name = "Nuclear Reactor Control Room";
+ req_access_txt = "10"
+ },
+/obj/effect/turf_decal/stripes/line,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"ec" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/door/poddoor/shutters/radiation/preopen{
+ id = "engrbmk";
+ name = "Radiation Chamber Shutters"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 10
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"ee" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"eg" = (
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
+ dir = 1
+ },
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"eu" = (
+/obj/structure/table,
+/obj/effect/turf_decal/stripes/line,
+/obj/item/book/manual/wiki/rbmk{
+ name = "\improper Nuclear Reactors (for dummies)"
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"eM" = (
+/obj/structure/chair/office/light{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"fF" = (
+/obj/structure/sign/warning/radiation/rad_area,
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"fU" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
+/turf/open/space,
+/area/space/nearstation)
+"gn" = (
+/obj/structure/table,
+/obj/item/storage/toolbox/mechanical{
+ pixel_y = 5
+ },
+/obj/item/flashlight{
+ pixel_x = 1;
+ pixel_y = 5
+ },
+/obj/item/flashlight{
+ pixel_x = 1;
+ pixel_y = 5
+ },
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"gF" = (
+/obj/machinery/portable_atmospherics/canister/toxins,
+/obj/effect/turf_decal/delivery,
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/machinery/conveyor{
+ id = "rbmktoxins"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"ha" = (
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"hd" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
+ dir = 6
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"hi" = (
+/obj/structure/table,
+/obj/effect/turf_decal/stripes/line,
+/obj/item/geiger_counter{
+ desc = "3.6 roentgen, not great, not terrible.";
+ name = "\improper Roentgen counter"
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"hj" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/plating,
+/area/engineering/main)
+"hn" = (
+/obj/machinery/pool/filter{
+ pixel_y = 24
+ },
+/turf/open/pool,
+/area/engineering/main)
+"hH" = (
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"hK" = (
+/obj/structure/lattice/catwalk,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 6
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"hQ" = (
+/obj/structure/closet/firecloset,
+/obj/effect/turf_decal/stripes/line{
+ dir = 9
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"hT" = (
+/obj/machinery/atmospherics/pipe/manifold/yellow/visible{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"id" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/door/poddoor/shutters/radiation/preopen{
+ id = "engrbmk";
+ name = "Radiation Chamber Shutters"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/plating,
+/area/engineering/main)
+"if" = (
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
+ dir = 8
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"iA" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/structure/closet/radiation,
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"iQ" = (
+/obj/machinery/pool/drain,
+/turf/open/pool,
+/area/engineering/main)
+"iY" = (
+/obj/machinery/atmospherics/pipe/simple/yellow/visible{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"jg" = (
+/obj/machinery/atmospherics/pipe/simple/orange/visible{
+ dir = 6
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"ji" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 10
+ },
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
+/obj/machinery/airalarm{
+ dir = 4;
+ pixel_x = -22
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"jk" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 6
+ },
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"jv" = (
+/obj/structure/chair/office/light{
+ dir = 8
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"jw" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/engine,
+/area/engineering/main)
+"jx" = (
+/obj/machinery/conveyor_switch{
+ id = "rbmktoxins"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"ke" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"kx" = (
+/obj/structure/lattice,
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"kM" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 9
+ },
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"kR" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 5
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"kX" = (
+/obj/machinery/atmospherics/pipe/simple/orange/visible{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"lc" = (
+/obj/effect/turf_decal/delivery,
+/obj/machinery/door/window/westright{
+ dir = 2;
+ name = "Engineering Desk";
+ req_access_txt = "10"
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"ld" = (
+/obj/machinery/portable_atmospherics/canister/oxygen,
+/obj/effect/turf_decal/delivery,
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/machinery/conveyor{
+ id = "rbmkoxy"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"lg" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"lE" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/door/poddoor/shutters/radiation/preopen{
+ id = "engrbmk";
+ name = "Radiation Chamber Shutters"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"lW" = (
+/obj/structure/table,
+/obj/machinery/computer/reactor/pump/rbmk_output,
+/obj/effect/turf_decal/stripes/line{
+ dir = 5
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"mb" = (
+/obj/effect/turf_decal/delivery,
+/obj/effect/turf_decal/stripes/corner{
+ dir = 4
+ },
+/obj/effect/turf_decal/stripes/corner{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 9
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"mi" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/engine,
+/area/engineering/main)
+"mo" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"mx" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/light{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"my" = (
+/obj/structure/table,
+/obj/machinery/computer/reactor/stats,
+/obj/effect/turf_decal/stripes/line{
+ dir = 9
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"mE" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/machinery/airalarm{
+ dir = 1;
+ pixel_y = -24
+ },
+/obj/machinery/camera{
+ c_tag = "Reactor control room";
+ dir = 1;
+ network = list("engine");
+ pixel_x = 23
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"mT" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"mY" = (
+/turf/open/floor/engine,
+/area/engineering/main)
+"nh" = (
+/obj/structure/cable/yellow{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"nx" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 9
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/engine,
+/area/engineering/main)
+"nT" = (
+/obj/structure/rack,
+/obj/item/clothing/mask/gas{
+ pixel_x = 3;
+ pixel_y = 3
+ },
+/obj/item/clothing/mask/gas,
+/obj/item/clothing/mask/gas{
+ pixel_x = -3;
+ pixel_y = -3
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 10
+ },
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"nW" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"oe" = (
+/obj/effect/turf_decal/stripes/line,
+/turf/open/floor/engine,
+/area/engineering/main)
+"op" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/preset,
+/turf/open/floor/engine,
+/area/engineering/main)
+"os" = (
+/obj/effect/turf_decal/stripes/corner,
+/turf/open/floor/engine,
+/area/engineering/main)
+"pr" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/pool,
+/area/engineering/main)
+"pF" = (
+/obj/machinery/atmospherics/pipe/manifold/orange/visible,
+/turf/open/floor/engine,
+/area/engineering/main)
+"qy" = (
+/turf/closed/wall,
+/area/engineering/main)
+"rl" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"rQ" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/light{
+ dir = 8;
+ light_color = "#e8eaff"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/engine,
+/area/engineering/main)
+"rV" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"sA" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"sS" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"sV" = (
+/obj/structure/table,
+/obj/item/clothing/gloves/color/yellow,
+/obj/item/storage/toolbox/electrical{
+ pixel_y = 5
+ },
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"tt" = (
+/obj/machinery/atmospherics/pipe/simple/yellow/visible,
+/turf/open/floor/engine,
+/area/engineering/main)
+"tw" = (
+/obj/structure/lattice,
+/turf/open/space/basic,
+/area/space/nearstation)
+"tK" = (
+/obj/effect/turf_decal/delivery,
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"tR" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 6
+ },
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"tZ" = (
+/obj/machinery/atmospherics/components/binary/pump/rbmk_input{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"ue" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 5
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"ui" = (
+/obj/effect/turf_decal/stripes/corner{
+ dir = 1
+ },
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/machinery/camera{
+ c_tag = "Reactor cycling room";
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"uj" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"up" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/components/unary/portables_connector/visible{
+ name = "output gas connector port"
+ },
+/obj/machinery/portable_atmospherics/canister/toxins,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"uA" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"uQ" = (
+/obj/machinery/pool/controller,
+/obj/effect/turf_decal/stripes/line{
+ dir = 6
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/engine,
+/area/engineering/main)
+"uR" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 9
+ },
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"vg" = (
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/yellow/visible{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"vh" = (
+/obj/structure/table,
+/obj/machinery/computer/reactor/pump/rbmk_input,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"vX" = (
+/obj/structure/lattice/catwalk,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 5
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"vY" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 6
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"wL" = (
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"wN" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"xO" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"xZ" = (
+/obj/machinery/conveyor_switch{
+ id = "rbmkoxy"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"yf" = (
+/turf/open/space/basic,
+/area/space/nearstation)
+"yI" = (
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"zm" = (
+/obj/structure/table,
+/obj/machinery/computer/reactor/pump/rbmk_moderator,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"zx" = (
+/obj/item/fuel_rod,
+/turf/open/pool,
+/area/engineering/main)
+"zC" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/atmospherics/pipe/heat_exchanging/junction{
+ dir = 8
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"zS" = (
+/turf/open/space/basic,
+/area/space)
+"Am" = (
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"AA" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"AI" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Bl" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"Bn" = (
+/obj/machinery/conveyor_switch{
+ id = "rbmkn2"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Bx" = (
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"BM" = (
+/obj/item/fuel_rod,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/pool,
+/area/engineering/main)
+"BU" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/light{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 10
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Cg" = (
+/obj/machinery/atmospherics/components/binary/pump/rbmk_moderator,
+/turf/open/floor/engine,
+/area/engineering/main)
+"Ck" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/effect/turf_decal/delivery,
+/turf/open/floor/engine,
+/area/engineering/main)
+"CE" = (
+/obj/structure/table,
+/obj/machinery/computer/reactor/fuel_rods,
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"CX" = (
+/obj/structure/pool/ladder{
+ dir = 2;
+ pixel_y = 24
+ },
+/turf/open/pool,
+/area/engineering/main)
+"CZ" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/light{
+ dir = 4;
+ light_color = "#c1caff"
+ },
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 4
+ },
+/obj/machinery/airalarm{
+ dir = 8;
+ pixel_x = 24
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"DF" = (
+/obj/machinery/atmospherics/pipe/simple/yellow/visible{
+ dir = 9
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"DI" = (
+/obj/structure/sign/warning/vacuum/external{
+ pixel_x = 32
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"DP" = (
+/obj/machinery/door/airlock/engineering/glass{
+ name = "Nuclear Reactor Airlock";
+ req_access_txt = "10"
+ },
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/effect/mapping_helpers/airlock/cyclelink_helper{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/plating,
+/area/engineering/main)
+"Et" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 6
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Ex" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/machinery/light,
+/turf/open/floor/engine,
+/area/engineering/main)
+"EG" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 10
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"FT" = (
+/obj/machinery/atmospherics/pipe/simple/yellow/visible{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"GH" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/components/unary/portables_connector/visible{
+ name = "output gas connector port"
+ },
+/obj/machinery/portable_atmospherics/canister/oxygen,
+/obj/machinery/light{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Hj" = (
+/obj/machinery/atmospherics/pipe/manifold/yellow/visible{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Hq" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 10
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Hv" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/obj/machinery/door/poddoor/shutters/radiation/preopen{
+ id = "engrbmk";
+ name = "Radiation Chamber Shutters"
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"Ie" = (
+/obj/machinery/portable_atmospherics/canister/nitrogen,
+/obj/machinery/atmospherics/components/unary/portables_connector/visible,
+/turf/open/floor/engine,
+/area/engineering/main)
+"Ir" = (
+/obj/machinery/portable_atmospherics/canister/nitrogen,
+/obj/effect/turf_decal/delivery,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 1;
+ layer = 2.9
+ },
+/obj/machinery/conveyor{
+ id = "rbmkn2"
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"IL" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Ja" = (
+/obj/structure/lattice/catwalk,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
+/turf/open/space/basic,
+/area/space/nearstation)
+"Jm" = (
+/obj/machinery/firealarm{
+ dir = 4;
+ pixel_x = 24
+ },
+/obj/effect/turf_decal/stripes/line{
+ dir = 5
+ },
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Jy" = (
+/obj/structure/table,
+/obj/machinery/computer/reactor/control_rods,
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"JA" = (
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"JG" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/effect/turf_decal/stripes/corner{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"JS" = (
+/obj/machinery/portable_atmospherics/canister/toxins,
+/obj/effect/turf_decal/delivery,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/machinery/conveyor{
+ id = "rbmktoxins"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Le" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/structure/cable/yellow{
+ icon_state = "4-8"
+ },
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"Ll" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/machinery/camera{
+ c_tag = "Reactor Chamber";
+ network = list("engine");
+ pixel_x = 23
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"LC" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/orange/visible,
+/turf/open/floor/engine,
+/area/engineering/main)
+"Me" = (
+/obj/machinery/button/door{
+ id = "rbmkshut";
+ name = "Nuclear Blast Door Control";
+ pixel_y = -24;
+ req_access_txt = "10"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"Mz" = (
+/obj/machinery/button/door{
+ id = "engrbmk";
+ name = "Radiation Shutters Control";
+ pixel_y = -24;
+ req_access_txt = "10"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"MN" = (
+/obj/machinery/atmospherics/components/trinary/mixer/airmix/inverse,
+/turf/open/floor/engine,
+/area/engineering/main)
+"NB" = (
+/obj/effect/turf_decal/delivery,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"NE" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 9
+ },
+/turf/open/space,
+/area/space/nearstation)
+"NG" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/structure/table/reinforced,
+/obj/item/reagent_containers/food/snacks/pizza/meat,
+/turf/open/floor/engine,
+/area/engineering/main)
+"NY" = (
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"Oj" = (
+/turf/closed/wall/r_wall,
+/area/space/nearstation)
+"Om" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/machinery/atmospherics/pipe/simple/yellow/visible{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"On" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
+ dir = 9
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"OA" = (
+/obj/machinery/portable_atmospherics/canister/nitrogen,
+/obj/machinery/atmospherics/components/unary/portables_connector/visible{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"OT" = (
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 1
+ },
+/obj/effect/spawner/structure/window/plasma/reinforced,
+/turf/open/floor/plating,
+/area/engineering/main)
+"Pt" = (
+/obj/machinery/atmospherics/pipe/simple/orange/visible{
+ dir = 5
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Pv" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Pz" = (
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"PE" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
+/turf/open/space/basic,
+/area/space/nearstation)
+"Qs" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/light{
+ dir = 8;
+ light_color = "#e8eaff"
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"QA" = (
+/obj/machinery/light/small{
+ dir = 8
+ },
+/obj/structure/closet/emcloset/anchored,
+/turf/open/floor/plating,
+/area/engineering/main)
+"QK" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/effect/turf_decal/delivery,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"QR" = (
+/obj/effect/turf_decal/delivery,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Rh" = (
+/obj/structure/lattice,
+/turf/open/space,
+/area/space/nearstation)
+"RJ" = (
+/obj/machinery/door/airlock/engineering/glass{
+ name = "Nuclear Reactor";
+ req_access_txt = "10"
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"So" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 5
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/engine,
+/area/engineering/main)
+"St" = (
+/obj/structure/closet/radiation,
+/obj/effect/turf_decal/stripes/line{
+ dir = 9
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 5
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Su" = (
+/obj/effect/turf_decal/stripes/corner,
+/obj/effect/turf_decal/stripes/corner{
+ dir = 8
+ },
+/obj/effect/turf_decal/delivery,
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Sx" = (
+/obj/machinery/door/poddoor{
+ id = "rbmkshut"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"Tg" = (
+/obj/structure/lattice/catwalk,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 10
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"Tm" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/machinery/light{
+ light_color = "#c9d3e8"
+ },
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"UR" = (
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Vc" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/closed/wall/r_wall,
+/area/engineering/main)
+"Ve" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Vi" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/visible,
+/turf/open/floor/engine,
+/area/engineering/main)
+"Vt" = (
+/obj/structure/closet/firecloset,
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"Vu" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
+ },
+/obj/machinery/airalarm{
+ dir = 8;
+ pixel_x = 24
+ },
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"VM" = (
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/components/binary/pump/rbmk_output{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"Wa" = (
+/obj/effect/turf_decal/stripes/corner{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"WB" = (
+/obj/structure/lattice,
+/obj/structure/grille,
+/turf/open/space/basic,
+/area/space/nearstation)
+"WX" = (
+/obj/effect/turf_decal/delivery,
+/obj/machinery/door/window/westright{
+ dir = 2;
+ name = "Engineering Desk";
+ req_access_txt = "10"
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"XA" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 6
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"Ym" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/carpet/green,
+/area/engineering/main)
+"YD" = (
+/obj/effect/mapping_helpers/airlock/cyclelink_helper{
+ dir = 1
+ },
+/obj/machinery/door/airlock/external{
+ name = "Engineering External Access";
+ req_access_txt = "10;13"
+ },
+/turf/open/floor/plating,
+/area/engineering/main)
+"YJ" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 5
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"YM" = (
+/obj/machinery/door/firedoor,
+/obj/machinery/door/airlock/engineering/glass{
+ name = "Nuclear Reactor Airlock";
+ req_access_txt = "10"
+ },
+/obj/effect/mapping_helpers/airlock/cyclelink_helper,
+/turf/open/floor/plasteel,
+/area/engineering/main)
+"YO" = (
+/obj/effect/turf_decal/stripes/line{
+ dir = 10
+ },
+/obj/structure/table,
+/obj/machinery/microwave,
+/obj/item/storage/box/donkpockets,
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"YU" = (
+/obj/structure/lattice,
+/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
+ dir = 9
+ },
+/turf/open/space/basic,
+/area/space/nearstation)
+"YZ" = (
+/obj/structure/lattice,
+/obj/structure/grille,
+/turf/open/space,
+/area/space/nearstation)
+"Zd" = (
+/obj/machinery/portable_atmospherics/canister/oxygen,
+/obj/effect/turf_decal/delivery,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/machinery/conveyor{
+ id = "rbmkoxy"
+ },
+/turf/open/floor/engine,
+/area/engineering/main)
+"ZN" = (
+/obj/machinery/shower{
+ dir = 4
+ },
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
+ },
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/engineering/main)
+"ZT" = (
+/turf/template_noop,
+/area/template_noop)
+
+(1,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+yf
+Rh
+yf
+yf
+Rh
+yf
+yf
+Rh
+yf
+yf
+yf
+YZ
+YZ
+YZ
+ZT
+ZT
+ZT
+ZT
+ZT
+"}
+(2,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+hK
+PE
+Ja
+Ja
+PE
+Ja
+Ja
+PE
+Ja
+Ja
+vX
+YZ
+Oj
+YZ
+ZT
+ZT
+ZT
+ZT
+ZT
+"}
+(3,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+EG
+ue
+XA
+PE
+PE
+PE
+PE
+PE
+PE
+fU
+NE
+YZ
+Oj
+YZ
+ZT
+ZT
+ZT
+ZT
+ZT
+"}
+(4,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+hK
+YU
+Tg
+Ja
+PE
+Ja
+Ja
+PE
+Ja
+Ja
+vX
+YZ
+Oj
+YZ
+ZT
+ZT
+ZT
+ZT
+ZT
+"}
+(5,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+EG
+ue
+XA
+PE
+PE
+PE
+PE
+PE
+PE
+fU
+NE
+YZ
+Oj
+YZ
+ZT
+ZT
+ZT
+ZT
+ZT
+"}
+(6,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+ZT
+hK
+YU
+Tg
+Ja
+PE
+Ja
+Ja
+PE
+Ja
+Ja
+vX
+YZ
+Oj
+YZ
+ZT
+ZT
+ZT
+ZT
+ZT
+"}
+(7,1,1) = {"
+ZT
+ZT
+ZT
+ZT
+qy
+qy
+Bx
+EG
+ue
+XA
+PE
+PE
+PE
+PE
+PE
+PE
+fU
+NE
+YZ
+Oj
+YZ
+yf
+yf
+yf
+yf
+zS
+"}
+(8,1,1) = {"
+ZT
+ZT
+sV
+gn
+qy
+QA
+Bx
+hK
+YU
+Tg
+Ja
+PE
+Ja
+Ja
+PE
+Ja
+Ja
+vX
+YZ
+Oj
+YZ
+yf
+yf
+Rh
+yf
+yf
+"}
+(9,1,1) = {"
+ZT
+ZT
+Pz
+Pz
+ax
+DI
+YD
+EG
+ue
+tw
+XA
+PE
+PE
+PE
+PE
+PE
+fU
+NE
+YZ
+YZ
+YZ
+tw
+Rh
+Rh
+Rh
+yf
+"}
+(10,1,1) = {"
+ZT
+Le
+lg
+NY
+Bx
+Bx
+Bx
+Bx
+zC
+NY
+zC
+NY
+tR
+az
+tw
+yf
+yf
+yf
+Rh
+yf
+yf
+yf
+yf
+Rh
+yf
+yf
+"}
+(11,1,1) = {"
+eg
+nh
+nx
+jw
+jw
+jw
+rQ
+jw
+nW
+jw
+Om
+jw
+ji
+Vc
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+WB
+YZ
+WB
+yf
+"}
+(12,1,1) = {"
+aN
+kM
+Wa
+ld
+Zd
+Zd
+lc
+xZ
+dF
+mY
+FT
+mY
+rV
+Vc
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+YZ
+Oj
+YZ
+yf
+"}
+(13,1,1) = {"
+uR
+Ve
+uj
+gF
+JS
+JS
+WX
+jx
+dF
+mY
+FT
+mY
+Ck
+Sx
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+YZ
+Oj
+YZ
+yf
+"}
+(14,1,1) = {"
+ee
+YJ
+uj
+Ir
+bf
+bf
+WX
+Bn
+dF
+mY
+FT
+mY
+Ck
+Sx
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+Rh
+"}
+(15,1,1) = {"
+Bx
+bQ
+uj
+mY
+mY
+mY
+hd
+Vi
+On
+mY
+FT
+mY
+Ck
+Sx
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+YZ
+Oj
+YZ
+yf
+"}
+(16,1,1) = {"
+fF
+mx
+hH
+ke
+ke
+wN
+VM
+JA
+mY
+mY
+FT
+mY
+Ck
+Sx
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+yf
+"}
+(17,1,1) = {"
+NY
+bQ
+mY
+mY
+mY
+mT
+IL
+mT
+mY
+mY
+FT
+mY
+Ex
+Vc
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+yf
+YZ
+Oj
+YZ
+yf
+"}
+(18,1,1) = {"
+NY
+Ll
+mY
+jg
+Cg
+LC
+op
+mT
+mY
+mY
+FT
+mY
+NG
+Vc
+Hv
+Hv
+Hv
+Bx
+Bx
+yf
+yf
+yf
+YZ
+Oj
+YZ
+yf
+"}
+(19,1,1) = {"
+NY
+bQ
+mY
+kX
+mY
+UR
+vg
+UR
+mY
+mY
+FT
+mY
+oe
+lE
+my
+Jy
+CE
+YO
+Bx
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+yf
+"}
+(20,1,1) = {"
+fF
+GH
+Pt
+kX
+mY
+mY
+tZ
+mY
+mY
+mY
+FT
+mY
+oe
+lE
+sA
+jv
+yI
+mE
+Bx
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+yf
+"}
+(21,1,1) = {"
+Bx
+up
+MN
+pF
+mY
+mY
+Hj
+tt
+tt
+tt
+DF
+mY
+oe
+ec
+QR
+Ym
+if
+mo
+Bx
+yf
+yf
+yf
+YZ
+Oj
+YZ
+yf
+"}
+(22,1,1) = {"
+NY
+bQ
+mY
+kX
+mY
+mY
+iY
+mY
+os
+Pv
+Pv
+Pv
+Et
+RJ
+NB
+vY
+Bl
+Tm
+fF
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+Rh
+"}
+(23,1,1) = {"
+NY
+bQ
+Am
+kX
+mY
+Ie
+hT
+OA
+oe
+hn
+bU
+zx
+zx
+Hv
+vh
+ce
+Mz
+hi
+Bx
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+yf
+"}
+(24,1,1) = {"
+fF
+kR
+dj
+mY
+mY
+mY
+wL
+mY
+oe
+CX
+iQ
+zx
+zx
+Hv
+zm
+eM
+Me
+eu
+Bx
+yf
+yf
+Rh
+YZ
+Oj
+YZ
+yf
+"}
+(25,1,1) = {"
+NY
+OT
+So
+mi
+AI
+mi
+CZ
+mi
+uQ
+pr
+pr
+BM
+BM
+id
+lW
+Su
+QK
+au
+Bx
+yf
+yf
+yf
+YZ
+YZ
+YZ
+yf
+"}
+(26,1,1) = {"
+iA
+sS
+ha
+NY
+uA
+Bx
+Bx
+Bx
+Bx
+Bx
+Hv
+Hv
+Hv
+Bx
+Bx
+dR
+rl
+Bx
+kx
+yf
+yf
+yf
+yf
+Rh
+yf
+yf
+"}
+(27,1,1) = {"
+ZT
+ZT
+ZT
+Pz
+nT
+hj
+St
+ZN
+dK
+Bx
+hQ
+Vt
+AA
+Qs
+dw
+mb
+tK
+Hq
+kx
+tw
+tw
+tw
+Rh
+Rh
+Rh
+Rh
+"}
+(28,1,1) = {"
+ZT
+ZT
+ZT
+Pz
+Pz
+YM
+Jm
+BU
+ui
+DP
+JG
+xO
+xO
+xO
+xO
+xO
+Vu
+jk
+kx
+yf
+tw
+yf
+yf
+Rh
+yf
+yf
+"}
+(29,1,1) = {"
+ZT
+ZT
+ZT
+Pz
+Pz
+Bx
+Bx
+fF
+Bx
+Bx
+NY
+NY
+NY
+Bx
+kx
+kx
+kx
+kx
+kx
+yf
+tw
+yf
+yf
+Rh
+yf
+zS
+"}
diff --git a/_maps/templates/reactor_1.dmm b/_maps/templates/reactor_1.dmm
new file mode 100644
index 0000000000..64c2ddc024
--- /dev/null
+++ b/_maps/templates/reactor_1.dmm
@@ -0,0 +1,149 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/obj/effect/turf_decal/stripes/red/corner{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"b" = (
+/obj/effect/turf_decal/stripes/red/line{
+ dir = 4
+ },
+/obj/effect/turf_decal/arrows/white{
+ dir = 8;
+ pixel_x = 5;
+ pixel_y = 10
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"c" = (
+/obj/effect/turf_decal/stripes/red/line{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"d" = (
+/obj/structure/cable/yellow,
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo,
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"e" = (
+/obj/effect/turf_decal/stripes/red/corner{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"f" = (
+/obj/machinery/atmospherics/pipe/simple/cyan{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"g" = (
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/purple,
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"h" = (
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"s" = (
+/obj/effect/turf_decal/stripes/red/corner,
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"t" = (
+/obj/effect/turf_decal/stripes/red/line{
+ dir = 8
+ },
+/obj/effect/turf_decal/arrows/white{
+ dir = 8;
+ pixel_x = 5;
+ pixel_y = 10
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"B" = (
+/obj/effect/turf_decal/stripes/red/corner{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"G" = (
+/obj/machinery/atmospherics/pipe/simple/cyan{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/engineering/main/reactor_core)
+"M" = (
+/obj/effect/turf_decal/stripes/red/line,
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"O" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/engineering/main/reactor_core)
+"P" = (
+/obj/effect/turf_decal/stripes/red/line{
+ dir = 8
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"V" = (
+/obj/effect/turf_decal/stripes/red/line{
+ dir = 1
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+"Y" = (
+/obj/structure/cable/yellow{
+ icon_state = "0-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/purple,
+/turf/open/floor/plating,
+/area/engineering/main/reactor_core)
+"Z" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers{
+ dir = 4
+ },
+/turf/open/floor/engine,
+/area/engineering/main/reactor_core)
+
+(1,1,1) = {"
+s
+c
+O
+b
+a
+"}
+(2,1,1) = {"
+M
+h
+Z
+h
+V
+"}
+(3,1,1) = {"
+Y
+g
+d
+h
+V
+"}
+(4,1,1) = {"
+M
+h
+f
+h
+V
+"}
+(5,1,1) = {"
+B
+P
+G
+t
+e
+"}
diff --git a/auxmos.dll b/auxmos.dll
index 7263a494a1..4e0687fa2b 100644
Binary files a/auxmos.dll and b/auxmos.dll differ
diff --git a/auxmos.pdb b/auxmos.pdb
index 940e2b60a6..da06980179 100644
Binary files a/auxmos.pdb and b/auxmos.pdb differ
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 5581d71080..b38eb98b14 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -16,6 +16,7 @@
#define DISABLE_ARRIVALRATTLE (1<<13)
#define COMBOHUD_LIGHTING (1<<14)
#define SOUND_BARK (1<<15)
+#define NO_ANTAG (1<<16)
#define DEADMIN_ALWAYS (1<<0)
#define DEADMIN_ANTAGONIST (1<<1)
diff --git a/code/__DEFINES/rendering/atom_hud.dm b/code/__DEFINES/rendering/atom_hud.dm
index 067c3f801c..25763c0d63 100644
--- a/code/__DEFINES/rendering/atom_hud.dm
+++ b/code/__DEFINES/rendering/atom_hud.dm
@@ -24,8 +24,9 @@
#define NANITE_HUD "20"
#define DIAG_NANITE_FULL_HUD "21"
#define RAD_HUD "22" //radation alerts for medical huds
+#define DIAG_LAUNCHPAD_HUD "23" //Displays launchpads' targeting reticle
//for antag huds. these are used at the /mob level
-#define ANTAG_HUD "23"
+#define ANTAG_HUD "24"
//by default everything in the hud_list of an atom is an image
//a value in hud_list with one of these will change that behavior
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 3204a39b89..e08a8de417 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -44,15 +44,12 @@
#define ROLE_GHOSTCAFE "ghostcafe"
#define ROLE_MINOR_ANTAG "minorantag"
#define ROLE_RESPAWN "respawnsystem"
-/// Not an actual antag. Lets players force all antags off.
-#define ROLE_NO_ANTAGONISM "NO_ANTAGS"
//Define for disabling individual antagonists for dynamic
-#define HAS_ANTAG_PREF(C,ROLE) (!(ROLE_NO_ANTAGONISM in C.prefs.be_special) && (ROLE in C.prefs.be_special))
+#define HAS_ANTAG_PREF(C,ROLE) (!(NO_ANTAG & C.prefs.toggles) && (ROLE in C.prefs.be_special))
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
//(in game days played) to play that role
GLOBAL_LIST_INIT(special_roles, list(
- ROLE_NO_ANTAGONISM,
ROLE_TRAITOR = /datum/game_mode/traitor,
ROLE_BROTHER = /datum/game_mode/traitor/bros,
ROLE_OPERATIVE = /datum/game_mode/nuclear,
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index 33a3aa65c4..040853556d 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -16,6 +16,10 @@
#define CHANNEL_DIGEST 1009
#define CHANNEL_PREYLOOP 1008
+
+//Reactor Channel
+#define CHANNEL_REACTOR_ALERT 1007 // Is that radiation I hear? (ported from hyper)
+
///Default range of a sound.
#define SOUND_RANGE 17
///default extra range for sounds considered to be quieter
@@ -34,7 +38,7 @@
//THIS SHOULD ALWAYS BE THE LOWEST ONE!
//KEEP IT UPDATED
-#define CHANNEL_HIGHEST_AVAILABLE 1008 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
+#define CHANNEL_HIGHEST_AVAILABLE 1007 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
#define MAX_INSTRUMENT_CHANNELS (128 * 6)
diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm
new file mode 100644
index 0000000000..fdb8b04280
--- /dev/null
+++ b/code/__HELPERS/matrices.dm
@@ -0,0 +1,43 @@
+/atom/proc/shake_animation(var/intensity = 8) //Makes the object visibly shake
+ var/initial_transform = new/matrix(transform)
+ var/init_px = pixel_x
+ var/shake_dir = pick(-1, 1)
+ var/rotation = 2+soft_cap(intensity, 1, 1, 0.94)
+ var/offset = 1+soft_cap(intensity*0.3, 1, 1, 0.8)
+ var/time = 2+soft_cap(intensity*0.3, 2, 1, 0.92)
+ animate(src, transform=turn(transform, rotation*shake_dir), pixel_x=init_px + offset*shake_dir, time=1)
+ animate(transform=initial_transform, pixel_x=init_px, time=time, easing=ELASTIC_EASING)
+
+/*
+ This proc makes the input taper off above cap. But there's no absolute cutoff.
+ Chunks of the input value above cap, are reduced more and more with each successive one and added to the output
+ A higher input value always makes a higher output value. but the rate of growth slows
+*/
+/proc/soft_cap(var/input, var/cap = 0, var/groupsize = 1, var/groupmult = 0.9)
+
+ //The cap is a ringfenced amount. If we're below that, just return the input
+ if (input <= cap)
+ return input
+
+ var/output = 0
+ var/buffer = 0
+ var/power = 1//We increment this after each group, then apply it to the groupmult as a power
+
+ //Ok its above, so the cap is a safe amount, we move that to the output
+ input -= cap
+ output += cap
+
+ //Now we start moving groups from input to buffer
+
+
+ while (input > 0)
+ buffer = min(input, groupsize) //We take the groupsize, or all the input has left if its less
+ input -= buffer
+
+ buffer *= groupmult**power //This reduces the group by the groupmult to the power of which index we're on.
+ //This ensures that each successive group is reduced more than the previous one
+
+ output += buffer
+ power++ //Transfer to output, increment power, repeat until the input pile is all used
+
+ return output
diff --git a/code/__HELPERS/radiation.dm b/code/__HELPERS/radiation.dm
index d527d0ac51..c80f3f012c 100644
--- a/code/__HELPERS/radiation.dm
+++ b/code/__HELPERS/radiation.dm
@@ -27,6 +27,10 @@
/proc/radiation_pulse(atom/source, intensity, range_modifier, log=FALSE, can_contaminate=TRUE)
if(!SSradiation.can_fire)
return
+ var/turf/open/pool/PL = get_turf(source)
+ if(istype(PL))
+ if(PL.filled == TRUE)
+ intensity *= 0.15
var/area/A = get_area(source)
var/atom/nested_loc = source.loc
var/spawn_waves = TRUE
@@ -52,6 +56,5 @@
thing.rad_act(intensity)
if(log)
- var/turf/_source_T = get_turf(source)
- log_game("Radiation pulse with intensity: [intensity] and range modifier: [range_modifier] in [loc_name(_source_T)][spawn_waves ? "" : " (contained by [nested_loc.name])"]")
+ log_game("Radiation pulse with intensity: [intensity] and range modifier: [range_modifier] in [loc_name(PL)][spawn_waves ? "" : " (contained by [nested_loc.name])"]")
return TRUE
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index ff5829ff73..5c8bf0b8fe 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -321,7 +321,7 @@ GLOBAL_LIST_INIT(wisdoms, world.file2list("strings/wisdoms.txt"))
//LANGUAGE CHARACTER CUSTOMIZATION
GLOBAL_LIST_INIT(speech_verbs, list("default","says","gibbers", "states", "chitters", "chimpers", "declares", "bellows", "buzzes" ,"beeps", "chirps", "clicks", "hisses" ,"poofs" , "puffs", "rattles", "mewls" ,"barks", "blorbles", "squeaks", "squawks", "flutters", "warbles", "caws", "gekkers", "clucks"))
-GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc, "xeno tongue" = /obj/item/organ/tongue/alien))
+GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc, "xeno tongue" = /obj/item/organ/tongue/alien/hybrid))
/proc/get_roundstart_languages()
var/list/languages = subtypesof(/datum/language)
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index f8dfc703f0..2831e25b10 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -47,6 +47,7 @@
#define ui_back "CENTER-2:14,SOUTH:5"
#define ui_storage1 "CENTER+1:18,SOUTH:5"
#define ui_storage2 "CENTER+2:20,SOUTH:5"
+#define ui_combo "CENTER+4:24,SOUTH+1:7" // combo meter for martial arts
//Lower right, persistent menu
#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index fbd62f0565..1eb13d20f6 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -33,6 +33,8 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/alien_plasma_display
var/atom/movable/screen/alien_queen_finder
+ var/atom/movable/screen/combo/combo_display
+
var/atom/movable/screen/devil/soul_counter/devilsouldisplay
var/atom/movable/screen/synth/coolant_counter/coolant_display
@@ -130,6 +132,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
blobpwrdisplay = null
alien_plasma_display = null
alien_queen_finder = null
+ combo_display = null
QDEL_LIST_ASSOC_VAL(plane_masters)
QDEL_LIST(screenoverlays)
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 5ed20f69e6..9547ee6f86 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -461,6 +461,9 @@
zone_select.update_icon()
static_inventory += zone_select
+ combo_display = new /atom/movable/screen/combo()
+ infodisplay += combo_display
+
for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory))
if(inv.slot_id)
inv.hud = src
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 6ca0416dfd..33c0bb0f04 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -720,3 +720,28 @@
/atom/movable/screen/component_button/Click(params)
if(parent)
parent.component_click(src, params)
+
+/atom/movable/screen/combo
+ icon_state = ""
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ screen_loc = ui_combo
+ layer = ABOVE_HUD_LAYER
+ var/timerid
+
+/atom/movable/screen/combo/proc/clear_streak()
+ cut_overlays()
+ icon_state = ""
+
+/atom/movable/screen/combo/update_icon_state(streak = "")
+ clear_streak()
+ if (timerid)
+ deltimer(timerid)
+ if (!streak)
+ return
+ timerid = addtimer(CALLBACK(src, .proc/clear_streak), 20, TIMER_UNIQUE | TIMER_STOPPABLE)
+ icon_state = "combo"
+ for (var/i = 1; i <= length(streak); ++i)
+ var/intent_text = copytext(streak, i, i + 1)
+ var/image/intent_icon = image(icon,src,"combo_[intent_text]")
+ intent_icon.pixel_x = 16 * (i - 1) - 8 * length(streak)
+ add_overlay(intent_icon)
diff --git a/code/_rendering/atom_huds/data_huds.dm b/code/_rendering/atom_huds/data_huds.dm
index d25dcb5380..291923ee4e 100644
--- a/code/_rendering/atom_huds/data_huds.dm
+++ b/code/_rendering/atom_huds/data_huds.dm
@@ -52,10 +52,10 @@
/datum/atom_hud/data/diagnostic
/datum/atom_hud/data/diagnostic/basic
- hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD)
+ hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD, DIAG_LAUNCHPAD_HUD)
/datum/atom_hud/data/diagnostic/advanced
- hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD, DIAG_PATH_HUD)
+ hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD, DIAG_PATH_HUD,DIAG_LAUNCHPAD_HUD)
/datum/atom_hud/data/bot_path
hud_icons = list(DIAG_PATH_HUD)
diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm
index dd9848e465..8a6ded4865 100644
--- a/code/controllers/subsystem/pai.dm
+++ b/code/controllers/subsystem/pai.dm
@@ -1,12 +1,18 @@
SUBSYSTEM_DEF(pai)
name = "pAI"
- flags = SS_NO_INIT|SS_NO_FIRE
+ flags = SS_NO_FIRE
var/list/candidates = list()
var/ghost_spam = FALSE
var/spam_delay = 100
var/list/pai_card_list = list()
+ var/list/restricted_areas = list()
+
+/datum/controller/subsystem/pai/Initialize(var/time_of_day)
+ restricted_areas += typesof(/area/command/heads_quarters, /area/ai_monitored) // heads quarters and AI monitored places (like the armory)
+ initialized = TRUE
+ return ..()
/datum/controller/subsystem/pai/Topic(href, href_list)
if(href_list["download"])
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
index 79810284cd..6edb0ab5ea 100644
--- a/code/datums/looping_sounds/machinery_sounds.dm
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -119,3 +119,8 @@
// volume = 70
// falloff_distance = 5
// falloff_exponent = 20
+
+/datum/looping_sound/rbmk_reactor
+ mid_length = 16
+ mid_sounds = list('sound/effects/rbmk/reactor_hum.ogg' = 10)
+ volume = 10
diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm
index 19cfc7691d..ce94cb3008 100644
--- a/code/datums/martial/_martial.dm
+++ b/code/datums/martial/_martial.dm
@@ -15,6 +15,8 @@
/// Set this variable to something not null, this'll be the preferred unarmed parry in most cases if [can_martial_parry] is TRUE. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING!
var/datum/block_parry_data/block_parry_data
var/pugilist = FALSE
+ var/datum/weakref/holder //owner of the martial art
+ var/display_combos = FALSE //shows combo meter if true
/datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
return FALSE
@@ -25,7 +27,7 @@
/datum/martial_art/proc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
return FALSE
-/datum/martial_art/proc/can_use(mob/living/carbon/human/H)
+/datum/martial_art/proc/can_use(mob/living/carbon/human/owner_human)
return TRUE
/datum/martial_art/proc/add_to_streak(element,mob/living/carbon/human/D)
@@ -34,11 +36,16 @@
streak = streak+element
if(length(streak) > max_streak_length)
streak = copytext(streak, 1 + length(streak[1]))
+ if(display_combos)
+ var/mob/living/holder_living = holder.resolve()
+ holder_living?.hud_used?.combo_display.update_icon_state(streak)
return
/datum/martial_art/proc/reset_streak(mob/living/carbon/human/new_target)
current_target = new_target
streak = ""
+ var/mob/living/holder_living = holder.resolve()
+ holder_living?.hud_used?.combo_display.update_icon_state(streak)
/datum/martial_art/proc/damage_roll(mob/living/carbon/human/A, mob/living/carbon/human/D)
//Here we roll for our damage to be added into the damage var in the various attack procs. This is changed depending on whether we are in combat mode, lying down, or if our target is in combat mode.
@@ -47,46 +54,48 @@
damage *= 0.7
return damage
-/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE)
- if(!istype(H) || !H.mind)
+/datum/martial_art/proc/teach(mob/living/carbon/human/owner_human, make_temporary = FALSE)
+ if(!istype(owner_human) || !owner_human.mind)
return FALSE
- if(H.mind.martial_art)
+ if(owner_human.mind.martial_art)
if(make_temporary)
- if(!H.mind.martial_art.allow_temp_override)
+ if(!owner_human.mind.martial_art.allow_temp_override)
return FALSE
- store(H.mind.martial_art,H)
+ store(owner_human.mind.martial_art,owner_human)
else
- H.mind.martial_art.on_remove(H)
+ owner_human.mind.martial_art.on_remove(owner_human)
else if(make_temporary)
- base = H.mind.default_martial_art
+ base = owner_human.mind.default_martial_art
if(help_verb)
- add_verb(H, help_verb)
- H.mind.martial_art = src
+ add_verb(owner_human, help_verb)
+ owner_human.mind.martial_art = src
+ holder = WEAKREF(owner_human)
if(pugilist)
- ADD_TRAIT(H, TRAIT_PUGILIST, MARTIAL_ARTIST_TRAIT)
+ ADD_TRAIT(owner_human, TRAIT_PUGILIST, MARTIAL_ARTIST_TRAIT)
return TRUE
-/datum/martial_art/proc/store(datum/martial_art/M,mob/living/carbon/human/H)
- M.on_remove(H)
+/datum/martial_art/proc/store(datum/martial_art/M,mob/living/carbon/human/owner_human)
+ M.on_remove(owner_human)
if(M.base) //Checks if M is temporary, if so it will not be stored.
base = M.base
else //Otherwise, M is stored.
base = M
-/datum/martial_art/proc/remove(mob/living/carbon/human/H)
- if(!istype(H) || !H.mind || H.mind.martial_art != src)
+/datum/martial_art/proc/remove(mob/living/carbon/human/owner_human)
+ if(!istype(owner_human) || !owner_human.mind || owner_human.mind.martial_art != src)
return
- on_remove(H)
+ on_remove(owner_human)
if(base)
- base.teach(H)
+ base.teach(owner_human)
else
- var/datum/martial_art/X = H.mind.default_martial_art
- X.teach(H)
- REMOVE_TRAIT(H, TRAIT_PUGILIST, MARTIAL_ARTIST_TRAIT)
+ var/datum/martial_art/X = owner_human.mind.default_martial_art
+ X.teach(owner_human)
+ REMOVE_TRAIT(owner_human, TRAIT_PUGILIST, MARTIAL_ARTIST_TRAIT)
+ holder = null
-/datum/martial_art/proc/on_remove(mob/living/carbon/human/H)
+/datum/martial_art/proc/on_remove(mob/living/carbon/human/owner_human)
if(help_verb)
- remove_verb(H, help_verb)
+ remove_verb(owner_human, help_verb)
return
///Gets called when a projectile hits the owner. Returning anything other than BULLET_ACT_HIT will stop the projectile from hitting the mob.
diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm
index 1d6682e3a6..afc1a26659 100644
--- a/code/datums/martial/cqc.dm
+++ b/code/datums/martial/cqc.dm
@@ -11,6 +11,7 @@
block_chance = 75
pugilist = TRUE
var/old_grab_state = null
+ display_combos = TRUE
/datum/martial_art/cqc/reset_streak(mob/living/carbon/human/new_target)
. = ..()
diff --git a/code/datums/martial/plasma_fist.dm b/code/datums/martial/plasma_fist.dm
index 16ffb381aa..59de1f6ec1 100644
--- a/code/datums/martial/plasma_fist.dm
+++ b/code/datums/martial/plasma_fist.dm
@@ -7,6 +7,7 @@
id = MARTIALART_PLASMAFIST
help_verb = /mob/living/carbon/human/proc/plasma_fist_help
pugilist = TRUE
+ display_combos = TRUE
/datum/martial_art/plasma_fist/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
diff --git a/code/datums/martial/rising_bass.dm b/code/datums/martial/rising_bass.dm
index 0d77644c86..87c0d42a29 100644
--- a/code/datums/martial/rising_bass.dm
+++ b/code/datums/martial/rising_bass.dm
@@ -13,6 +13,7 @@
var/datum/action/risingbassmove/sidekick = new/datum/action/risingbassmove/sidekick()
var/datum/action/risingbassmove/deftswitch = new/datum/action/risingbassmove/deftswitch()
var/repulsecool = 0
+ display_combos = TRUE
/datum/martial_art/the_rising_bass/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(findtext(streak,SIDE_KICK_COMBO))
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index bac1614228..d0cdee4937 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -9,6 +9,7 @@
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
block_parry_data = /datum/block_parry_data/sleeping_carp
pugilist = TRUE
+ display_combos = TRUE
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(findtext(streak,STRONG_PUNCH_COMBO))
diff --git a/code/datums/ruins/station.dm b/code/datums/ruins/station.dm
index 71cdf495e9..1910dc738c 100644
--- a/code/datums/ruins/station.dm
+++ b/code/datums/ruins/station.dm
@@ -55,6 +55,11 @@
suffix = "Box/Engine/budget.dmm"
name = "Box P.A.C.M.A.N"
+/datum/map_template/ruin/station/box/engine/rbmk
+ id = "engine_rbmk"
+ suffix = "Box/Engine/engine_rbmk.dmm"
+ name = "Box RBMK"
+
// Lavaland
// Mining Base
/datum/map_template/ruin/station/lavaland/mining_base
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 6bb6b444b0..e7d9898dd2 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -864,6 +864,10 @@
return SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
/atom/proc/rad_act(strength)
+ var/turf/open/pool/PL = get_turf(src)
+ if(istype(PL))
+ if(PL.filled == TRUE)
+ strength *= 0.15
SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength)
/atom/proc/narsie_act()
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
index 30004a3b69..b742f6f36c 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
@@ -210,10 +210,6 @@
candidates.Remove(candidate_player)
continue
- if(ROLE_NO_ANTAGONISM in candidate_player.client.prefs.be_special)
- candidates.Remove(candidate_player)
- continue
-
if(antag_flag_override)
if(!(HAS_ANTAG_PREF(candidate_player.client, antag_flag_override)))
candidates.Remove(candidate_player)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index a719ec6198..4dbe7a4387 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -14,8 +14,6 @@
candidates.Remove(P)
else if((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
candidates.Remove(P)
- else if(ROLE_NO_ANTAGONISM in P.client.prefs.be_special)
- candidates.Remove(P)
else if(antag_flag_override)
if(!(HAS_ANTAG_PREF(P.client, antag_flag_override)))
candidates.Remove(P)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 601848c22c..b6de6c300a 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -46,9 +46,6 @@
if(!mode.check_age(M.client, minimum_required_age))
trimmed_list.Remove(M)
continue
- if(ROLE_NO_ANTAGONISM in M.client.prefs.be_special)
- trimmed_list.Remove(M)
- continue
if(antag_flag_override)
if(!(HAS_ANTAG_PREF(M.client, antag_flag_override)))
trimmed_list.Remove(M)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index df1b0a701a..12dfc92cc4 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -348,8 +348,9 @@
var/list/curr_tickets = list() //how many tickets someone has for *this* antag roll, so with the free tickets
var/list/datum/mind/insufficient = list() //who got cucked out of an antag roll due to not having *any* tickets
for(var/datum/mind/M in candidates)
+ var/weight = clamp(candidates[M], 0, 1)
var/mind_ckey = ckey(M.key)
- var/can_spend = min(prev_tickets[mind_ckey], additional_tickets) //they can only spend up to config/max_tickets_per_roll
+ var/can_spend = min(prev_tickets[mind_ckey], additional_tickets * weight) //they can only spend up to config/max_tickets_per_roll
var/amount = can_spend + free_tickets //but they get config/default_antag_tickets for free
if(amount <= 0) //if they don't have any
insufficient += M //too bad!
@@ -425,10 +426,11 @@
for(var/mob/dead/new_player/player in players)
if(player.client && player.ready == PLAYER_READY_TO_PLAY)
- if((role in player.client.prefs.be_special) && !(ROLE_NO_ANTAGONISM in player.client.prefs.be_special))
+ if(HAS_ANTAG_PREF(player.client, role))
if(!jobban_isbanned(player, ROLE_SYNDICATE) && !QDELETED(player) && !jobban_isbanned(player, role) && !QDELETED(player)) //Nodrak/Carn: Antag Job-bans
if(age_check(player.client)) //Must be older than the minimum age
candidates += player.mind // Get a list of all the people who want to be the antagonist for this round
+ candidates[player.mind] = player.client.prefs.be_special[role]
if(restricted_jobs)
for(var/datum/mind/player in candidates)
@@ -449,35 +451,11 @@
if(player.assigned_role == job)
drafted -= player
- drafted = shuffle(drafted) // Will hopefully increase randomness, Donkie
-
- while(candidates.len < recommended_enemies) // Pick randomlly just the number of people we need and add them to our list of candidates
- if(drafted.len > 0)
- applicant = pick(drafted)
- if(applicant)
- candidates += applicant
- drafted.Remove(applicant)
-
- else // Not enough scrubs, ABORT ABORT ABORT
- break
-
- if(restricted_jobs)
- for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
- for(var/job in restricted_jobs)
- if(player.assigned_role == job)
- drafted -= player
-
- drafted = shuffle(drafted) // Will hopefully increase randomness, Donkie
-
- while(candidates.len < recommended_enemies) // Pick randomlly just the number of people we need and add them to our list of candidates
- if(drafted.len > 0)
- applicant = pick(drafted)
- if(applicant)
- candidates += applicant
- drafted.Remove(applicant)
-
- else // Not enough scrubs, ABORT ABORT ABORT
- break
+ while(candidates.len < recommended_enemies && length(drafted)) // Pick randomlly just the number of people we need and add them to our list of candidates
+ applicant = pick_n_take(drafted)
+ if(applicant)
+ candidates += applicant
+ candidates[applicant] = 0
return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies
// recommended_enemies if the number of people with that role set to yes is less than recomended_enemies,
diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm
index 65c91fad76..4ec80b5bb4 100644
--- a/code/game/machinery/computer/launchpad_control.dm
+++ b/code/game/machinery/computer/launchpad_control.dm
@@ -100,6 +100,7 @@
var/new_x = text2num(params["x"])
var/new_y = text2num(params["y"])
current_pad.set_offset(new_x, new_y)
+ current_pad.update_indicator()
. = TRUE
if("move_pos")
var/plus_x = text2num(params["x"])
@@ -108,6 +109,7 @@
x = current_pad.x_offset + plus_x,
y = current_pad.y_offset + plus_y
)
+ current_pad.update_indicator()
. = TRUE
if("rename")
. = TRUE
@@ -119,12 +121,15 @@
if(usr && alert(usr, "Are you sure?", "Unlink Launchpad", "I'm Sure", "Abort") != "Abort")
launchpads -= current_pad
selected_id = null
+ current_pad.update_indicator()
. = TRUE
if("launch")
teleport(usr, current_pad, TRUE)
+ current_pad.update_indicator()
. = TRUE
if("pull")
teleport(usr, current_pad, FALSE)
+ current_pad.update_indicator()
. = TRUE
. = TRUE
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index fa689cc95f..14a90ff3bf 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -6,6 +6,7 @@
use_power = TRUE
idle_power_usage = 200
active_power_usage = 2500
+ hud_possible = list(DIAG_LAUNCHPAD_HUD)
circuit = /obj/item/circuitboard/machine/launchpad
var/icon_teleport = "lpad-beam"
var/stationary = TRUE //to prevent briefcase pad deconstruction and such
@@ -24,6 +25,25 @@
E += M.rating*15
range = E
+/obj/machinery/launchpad/Initialize()
+ . = ..()
+ prepare_huds()
+ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
+ diag_hud.add_to_hud(src)
+
+ var/image/holder = hud_list[DIAG_LAUNCHPAD_HUD]
+ var/mutable_appearance/MA = new /mutable_appearance()
+ MA.icon = 'icons/effects/effects.dmi'
+ MA.icon_state = "launchpad_target"
+ MA.layer = ABOVE_OPEN_TURF_LAYER
+ MA.plane = 0
+ holder.appearance = MA
+ update_indicator()
+
+/obj/machinery/launchpad/Destroy()
+ qdel(hud_list[DIAG_LAUNCHPAD_HUD])
+ return ..()
+
/obj/machinery/launchpad/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
@@ -32,6 +52,7 @@
/obj/machinery/launchpad/attackby(obj/item/I, mob/user, params)
if(stationary)
if(default_deconstruction_screwdriver(user, "lpad-idle-o", "lpad-idle", I))
+ update_indicator()
return
if(panel_open)
@@ -63,6 +84,17 @@
return FALSE
return TRUE
+/obj/machinery/launchpad/proc/update_indicator()
+ var/image/holder = hud_list[DIAG_LAUNCHPAD_HUD]
+ var/turf/target_turf
+ if(isAvailable())
+ target_turf = locate(x + x_offset, y + y_offset, z)
+ if(target_turf)
+ holder.icon_state = indicator_icon
+ holder.loc = target_turf
+ else
+ holder.icon_state = null
+
/obj/machinery/launchpad/proc/set_offset(x, y)
if(teleporting)
return
@@ -89,11 +121,13 @@
flick(icon_teleport, src)
+
//Change the indicator's icon to show that we're teleporting
if(sending)
indicator_icon = "launchpad_launch"
else
indicator_icon = "launchpad_pull"
+ update_indicator()
playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, TRUE)
teleporting = TRUE
@@ -103,6 +137,7 @@
//Set the indicator icon back to normal
indicator_icon = "launchpad_target"
+ update_indicator()
if(QDELETED(src) || !isAvailable())
return
@@ -213,6 +248,7 @@
if(do_after(usr, 30, target = usr))
usr.put_in_hands(briefcase)
moveToNullspace() //hides it from suitcase contents
+ update_indicator()
closed = TRUE
/obj/machinery/launchpad/briefcase/attackby(obj/item/I, mob/user, params)
@@ -249,6 +285,7 @@
user.visible_message("[user] starts setting down [src]...", "You start setting up [pad]...")
if(do_after(user, 30, target = user))
pad.forceMove(get_turf(src))
+ pad.update_indicator()
pad.closed = FALSE
user.transferItemToLoc(src, pad, TRUE)
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_HIDE_ALL)
@@ -322,6 +359,7 @@
var/new_x = text2num(params["x"])
var/new_y = text2num(params["y"])
pad.set_offset(new_x, new_y)
+ pad.update_indicator()
. = TRUE
if("move_pos")
var/plus_x = text2num(params["x"])
@@ -330,6 +368,7 @@
x = pad.x_offset + plus_x,
y = pad.y_offset + plus_y
)
+ pad.update_indicator()
. = TRUE
if("rename")
. = TRUE
@@ -340,12 +379,15 @@
if("remove")
. = TRUE
if(usr && alert(usr, "Are you sure?", "Unlink Launchpad", "I'm Sure", "Abort") != "Abort")
+ pad.update_indicator()
pad = null
if("launch")
sending = TRUE
teleport(usr, pad)
+ pad.update_indicator()
. = TRUE
if("pull")
sending = FALSE
teleport(usr, pad)
+ pad.update_indicator()
. = TRUE
diff --git a/code/game/objects/items/devices/swapper.dm b/code/game/objects/items/devices/swapper.dm
new file mode 100644
index 0000000000..08646efd98
--- /dev/null
+++ b/code/game/objects/items/devices/swapper.dm
@@ -0,0 +1,112 @@
+//ported from TG, credit to XDTM
+/obj/item/swapper
+ name = "quantum spin inverter"
+ desc = "An experimental device that is able to swap the locations of two entities by switching their particles' spin values. Must be linked to another device to function."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "swapper"
+ item_state = "electronic"
+ w_class = WEIGHT_CLASS_SMALL
+ item_flags = NOBLUDGEON
+ lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
+
+ var/cooldown = 300
+ var/next_use = 0
+ var/obj/item/swapper/linked_swapper
+
+/obj/item/swapper/Destroy()
+ if(linked_swapper)
+ linked_swapper.linked_swapper = null //*inception music*
+ linked_swapper.update_appearance()
+ linked_swapper = null
+ return ..()
+
+/obj/item/swapper/update_icon_state()
+ icon_state = "swapper[linked_swapper ? "-linked" : null]"
+ return ..()
+
+/obj/item/swapper/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/swapper))
+ var/obj/item/swapper/other_swapper = I
+ if(other_swapper.linked_swapper)
+ to_chat(user, span_warning("[other_swapper] is already linked. Break the current link to establish a new one."))
+ return
+ if(linked_swapper)
+ to_chat(user, span_warning("[src] is already linked. Break the current link to establish a new one."))
+ return
+ to_chat(user, span_notice("You establish a quantum link between the two devices."))
+ linked_swapper = other_swapper
+ other_swapper.linked_swapper = src
+ update_appearance()
+ linked_swapper.update_appearance()
+ else
+ return ..()
+
+/obj/item/swapper/attack_self(mob/living/user)
+ if(world.time < next_use)
+ to_chat(user, span_warning("[src] is still recharging."))
+ return
+ if(QDELETED(linked_swapper))
+ to_chat(user, span_warning("[src] is not linked with another swapper."))
+ return
+ playsound(src, 'sound/weapons/flash.ogg', 25, TRUE)
+ to_chat(user, span_notice("You activate [src]."))
+ playsound(linked_swapper, 'sound/weapons/flash.ogg', 25, TRUE)
+ if(ismob(linked_swapper.loc))
+ var/mob/holder = linked_swapper.loc
+ to_chat(holder, span_notice("[linked_swapper] starts buzzing."))
+ next_use = world.time + cooldown //only the one used goes on cooldown
+ addtimer(CALLBACK(src, .proc/swap, user), 25)
+
+/obj/item/swapper/examine(mob/user)
+ . = ..()
+ if(world.time < next_use)
+ . += span_warning("Time left to recharge: [DisplayTimeText(next_use - world.time)].")
+ if(linked_swapper)
+ . += span_notice("Linked. Alt-Click to break the quantum link.")
+ else
+ . += span_notice("Not Linked. Use on another quantum spin inverter to establish a quantum link.")
+
+/obj/item/swapper/AltClick(mob/living/user)
+ if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERY,, FALSE, !iscyborg(user))) //someone mispelled dexterity
+ return
+ to_chat(user, span_notice("You break the current quantum link."))
+ if(!QDELETED(linked_swapper))
+ linked_swapper.linked_swapper = null
+ linked_swapper.update_appearance()
+ linked_swapper = null
+ update_appearance()
+
+//Gets the topmost teleportable container
+/obj/item/swapper/proc/get_teleportable_container()
+ var/atom/movable/teleportable = src
+ while(ismovable(teleportable.loc))
+ var/atom/movable/AM = teleportable.loc
+ if(AM.anchored)
+ break
+ if(isliving(AM))
+ var/mob/living/L = AM
+ if(L.buckled)
+ if(L.buckled.anchored)
+ break
+ else
+ var/obj/buckled_obj = L.buckled
+ buckled_obj.unbuckle_mob(L)
+ teleportable = AM
+ return teleportable
+
+/obj/item/swapper/proc/swap(mob/user)
+ if(QDELETED(linked_swapper) || world.time < linked_swapper.cooldown)
+ return
+
+ var/atom/movable/A = get_teleportable_container()
+ var/atom/movable/B = linked_swapper.get_teleportable_container()
+ var/target_A = A.drop_location()
+ var/target_B = B.drop_location()
+
+ //TODO: add a sound effect or visual effect
+ if(do_teleport(A, target_B, channel = TELEPORT_CHANNEL_QUANTUM))
+ do_teleport(B, target_A, channel = TELEPORT_CHANNEL_QUANTUM)
+ if(ismob(B))
+ var/mob/M = B
+ to_chat(M, span_warning("[linked_swapper] activates, and you find yourself somewhere else."))
diff --git a/code/game/objects/items/manuals.dm b/code/game/objects/items/manuals.dm
index ed8ec80bf0..3bd13b3905 100644
--- a/code/game/objects/items/manuals.dm
+++ b/code/game/objects/items/manuals.dm
@@ -494,6 +494,38 @@
title = "Toxins or: How I Learned to Stop Worrying and Love the Maxcap"
page_link = "Guide_to_toxins"
+/obj/item/book/manual/wiki/rbmk
+ name = "\improper Haynes nuclear reactor owner's manual"
+ icon_state = "bookEngineering2"
+ author = "CogWerk Engineering Reactor Design Department"
+ title = "Haynes nuclear reactor owner's manual"
+ page_link = "Guide_to_the_Nuclear_Reactor"
+
+/obj/item/book/manual/wiki/rbmk/initialize_wikibook()
+ var/wikiurl = "https://nsv.beestation13.com/wiki"
+ if(wikiurl)
+ dat = {"
+
+
+
+
+
+
+ You start skimming through the manual...
+
+
+
+ "}
+
/obj/item/book/manual/wiki/toxins/suicide_act(mob/user)
var/mob/living/carbon/human/H = user
user.visible_message("[user] starts dancing to the Rhumba Beat! It looks like [user.p_theyre()] trying to commit suicide!")
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index a147263488..0abe2e32be 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -664,7 +664,7 @@ This is here to make the tiles around the station mininuke change when it's arme
if(disk_comfort_level >= 2) //Sleep tight, disky.
if(!(process_tick % 30))
visible_message("[src] sleeps soundly. Sleep tight, disky.")
- if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001))
+ if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001 / max(disk_comfort_level,1)))
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop) && loneop.occurrences < loneop.max_occurrences)
loneop.weight += 1
diff --git a/code/modules/arousal/arousal.dm b/code/modules/arousal/arousal.dm
index 602fb2de6d..4e0f37d529 100644
--- a/code/modules/arousal/arousal.dm
+++ b/code/modules/arousal/arousal.dm
@@ -117,7 +117,8 @@
if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
return
to_chat(src,"You used your [G.name] to fill [container].")
- message_admins("[src] used their [G.name] to fill [container].")
+ message_admins("[ADMIN_LOOKUPFLW(src)] used their [G.name] to fill [container].")
+ log_consent("[key_name(src)] used their [G.name] to fill [container].")
do_climax(fluid_source, container, G, FALSE)
/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
@@ -159,8 +160,8 @@
if(consenting == "Yes")
return target
else
- message_admins("[src] tried to climax with [target], but [target] did not consent.")
- log_consent("[src] tried to climax with [target], but [target] did not consent.")
+ message_admins("[ADMIN_LOOKUPFLW(src)] tried to climax with [target], but [target] did not consent.")
+ log_consent("[key_name(src)] tried to climax with [target], but [target] did not consent.")
/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
var/list/containers_list = list()
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index 70c5065231..56820d2836 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -280,11 +280,7 @@ we use a hook instead
parse_gas_string(model.initial_gas_mix)
return 1
-/datum/gas_mixture/proc/__auxtools_parse_gas_string(gas_string)
-
/datum/gas_mixture/parse_gas_string(gas_string)
- __auxtools_parse_gas_string(gas_string)
- /*
gas_string = SSair.preprocess_gas_string(gas_string)
var/list/gas = params2list(gas_string)
@@ -298,7 +294,6 @@ we use a hook instead
for(var/id in gas)
set_moles(id, text2num(gas[id]))
archive()
- */
return 1
/*
/datum/gas_mixture/react(datum/holder)
diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm
index 39395298b9..23b73cea35 100644
--- a/code/modules/atmospherics/gasmixtures/reactions.dm
+++ b/code/modules/atmospherics/gasmixtures/reactions.dm
@@ -69,8 +69,6 @@
// no test cause it's entirely based on location
-GLOBAL_VAR_INIT(condensation_priority, 0)
-
/datum/gas_reaction/condensation
priority = 0
name = "Condensation"
@@ -89,8 +87,6 @@ GLOBAL_VAR_INIT(condensation_priority, 0)
name = "[R.name] condensation"
id = "[R.type] condensation"
condensing_reagent = GLOB.chemical_reagents_list[R.type]
- GLOB.condensation_priority += 0.000000000001
- priority = GLOB.condensation_priority
exclude = FALSE
/datum/gas_reaction/condensation/react(datum/gas_mixture/air, datum/holder)
diff --git a/code/modules/cargo/exports/engineering.dm b/code/modules/cargo/exports/engineering.dm
new file mode 100644
index 0000000000..4445437389
--- /dev/null
+++ b/code/modules/cargo/exports/engineering.dm
@@ -0,0 +1,4 @@
+/datum/export/plutonium_rod
+ cost = 20000
+ unit_name = "Plutonium Rod"
+ export_types = list(/obj/item/fuel_rod/plutonium)
diff --git a/code/modules/cargo/packs/emergency.dm b/code/modules/cargo/packs/emergency.dm
index 9b2b42da08..45264e7b8c 100644
--- a/code/modules/cargo/packs/emergency.dm
+++ b/code/modules/cargo/packs/emergency.dm
@@ -158,6 +158,14 @@
/obj/item/clothing/head/hardhat/red)
crate_name = "firefighting crate"
+/datum/supply_pack/emergency/Flexiseal
+ name = "Flexi Seal Crate"
+ desc = "Flexi Seal, the perfect stuff for fixing a nuclear reactor safely!"
+ cost = 1000
+ contains = list(/obj/item/sealant)
+ crate_name = "Flexi Seal Crate"
+ crate_type = /obj/structure/closet/crate/radiation
+
/datum/supply_pack/emergency/atmostank
name = "Firefighting Tank Backpack"
desc = "Mow down fires with this high-capacity fire fighting tank backpack. Requires Atmospherics access to open."
diff --git a/code/modules/cargo/packs/engine.dm b/code/modules/cargo/packs/engine.dm
index 499881a110..b7a2d883ce 100644
--- a/code/modules/cargo/packs/engine.dm
+++ b/code/modules/cargo/packs/engine.dm
@@ -166,3 +166,40 @@
cost = 7000
contains = list(/obj/machinery/the_singularitygen/tesla)
crate_name = "tesla generator crate"
+/datum/supply_pack/engine/fuel_rod
+ name = "Uranium Fuel Rod crate"
+ desc = "Two additional fuel rods for use in a reactor, requires CE access to open. Caution: Radioactive"
+ cost = 3000
+ access = ACCESS_CE
+ contains = list(/obj/item/fuel_rod,
+ /obj/item/fuel_rod)
+ crate_name = "Uranium-235 Fuel Rod crate"
+ crate_type = /obj/structure/closet/crate/secure/engineering
+ dangerous = TRUE
+
+/datum/supply_pack/engine/bananium_fuel_rod
+ name = "Bananium Fuel Rod crate"
+ desc = "Two fuel rods designed to utilize and multiply bananium in a reactor, requires CE access to open. Caution: Radioactive"
+ cost = 4000
+ access = ACCESS_CE // Nag your local CE
+ contains = list(/obj/item/fuel_rod/material/bananium,
+ /obj/item/fuel_rod/material/bananium)
+ crate_name = "Bluespace Crystal Fuel Rod crate"
+ crate_type = /obj/structure/closet/crate/secure/engineering
+ dangerous = TRUE
+ contraband = TRUE
+
+/datum/supply_pack/engine/reactor
+ name = "RMBK Nuclear Reactor Kit" // (not) a toy
+ desc = "Contains a reactor beacon and 3 reactor consoles. Uranium rods not included."
+ cost = 12000
+ access = ACCESS_CE
+ contains = list(/obj/item/survivalcapsule/reactor,
+ /obj/machinery/computer/reactor/control_rods/cargo,
+ /obj/machinery/computer/reactor/stats/cargo,
+ /obj/machinery/computer/reactor/fuel_rods/cargo,
+ /obj/item/paper/fluff/rbmkcargo,
+ /obj/item/book/manual/wiki/rbmk)
+ crate_name = "Build Your Own Reactor Kit"
+ crate_type = /obj/structure/closet/crate/secure/engineering
+ dangerous = TRUE
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 1df5d125f7..622f789870 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -1005,11 +1005,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "You are banned from antagonist roles."
src.be_special = list()
+ dat += "DISABLE ALL ANTAGONISM [(toggles & NO_ANTAG) ? "YES" : "NO"]
"
for (var/i in GLOB.special_roles)
- if(i == ROLE_NO_ANTAGONISM)
- dat += "DISABLE ALL ANTAGONISM [(i in be_special) ? "YES" : "NO"]
"
- continue
if(jobban_isbanned(user, i))
dat += "Be [capitalize(i)]: BANNED
"
else
@@ -1022,7 +1020,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(days_remaining)
dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
"
else
- dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"]
"
+ var/enabled_text = ""
+ if(i in be_special)
+ if(be_special[i] >= 1)
+ enabled_text = "Enabled"
+ else
+ enabled_text = "Low"
+ else
+ enabled_text = "Disabled"
+ dat += "Be [capitalize(i)]: [enabled_text]
"
dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Enabled" : "Disabled"]
"
dat += "
"
@@ -2936,12 +2942,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
deadmin ^= DEADMIN_POSITION_SILICON
//
+ if("disable_antag")
+ toggles ^= NO_ANTAG
+
if("be_special")
var/be_special_type = href_list["be_special_type"]
if(be_special_type in be_special)
- be_special -= be_special_type
+ if(be_special[be_special_type] >= 1)
+ be_special -= be_special_type
+ else
+ be_special[be_special_type] = 1
else
be_special += be_special_type
+ be_special[be_special_type] = 0
if("name")
be_random_name = !be_random_name
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 249dbba45f..714025e6e2 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
-#define SAVEFILE_VERSION_MAX 55
+#define SAVEFILE_VERSION_MAX 56
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -42,14 +42,20 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//if your savefile is 3 months out of date, then 'tough shit'.
/datum/preferences/proc/update_preferences(current_version, savefile/S)
- if(current_version < 55) //Bitflag toggles don't set their defaults when they're added, always defaulting to off instead.
- toggles |= SOUND_BARK
- if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
- force_reset_keybindings_direct(TRUE)
- addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
if(current_version < 30)
outline_enabled = TRUE
outline_color = COLOR_THEME_MIDNIGHT
+ if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
+ force_reset_keybindings_direct(TRUE)
+ addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
+ if(current_version < 55) //Bitflag toggles don't set their defaults when they're added, always defaulting to off instead.
+ toggles |= SOUND_BARK
+ if(current_version < 56)
+ if("NO_ANTAGS" in be_special)
+ toggles |= NO_ANTAG
+ be_special -= "NO_ANTAGS"
+ for(var/be_special_type in be_special)
+ be_special[be_special_type] = 1
/datum/preferences/proc/update_character(current_version, savefile/S)
if(current_version < 19)
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index 456bdd558e..f7014df648 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -32,7 +32,7 @@
/datum/round_event/ghost_role/alien_infestation/announce(fake)
if(successSpawn || fake)
- priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", "aliens")
+ priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", "aliens", has_important_message = TRUE)
/datum/round_event/ghost_role/alien_infestation/spawn_role()
diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm
index 926ff87c1d..ae0d5442ad 100644
--- a/code/modules/events/anomaly.dm
+++ b/code/modules/events/anomaly.dm
@@ -45,7 +45,7 @@
CRASH("Anomaly : No valid turfs found for [impact_area] - [impact_area.type]")
/datum/round_event/anomaly/announce(fake)
- priority_announce("Localized energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert")
+ priority_announce("Localized energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert", has_important_message = TRUE)
/datum/round_event/anomaly/start()
var/list/turf/valid = list()
diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm
index 70d6b00fa4..fd64b20244 100644
--- a/code/modules/events/anomaly_bluespace.dm
+++ b/code/modules/events/anomaly_bluespace.dm
@@ -11,4 +11,4 @@
anomaly_path = /obj/effect/anomaly/bluespace
/datum/round_event/anomaly/anomaly_bluespace/announce(fake)
- priority_announce("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ priority_announce("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", has_important_message = TRUE)
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index 4dd8dd87c5..6368e70b94 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -11,5 +11,5 @@
anomaly_path = /obj/effect/anomaly/flux
/datum/round_event/anomaly/anomaly_flux/announce(fake)
- priority_announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ priority_announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", has_important_message = TRUE)
diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm
index 01e70c290c..c1b4ade5c0 100644
--- a/code/modules/events/anomaly_grav.dm
+++ b/code/modules/events/anomaly_grav.dm
@@ -12,4 +12,4 @@
anomaly_path = /obj/effect/anomaly/grav
/datum/round_event/anomaly/anomaly_grav/announce(fake)
- priority_announce("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ priority_announce("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", has_important_message = TRUE)
diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm
index 1ac010344e..83c1fa64cc 100644
--- a/code/modules/events/anomaly_pyro.dm
+++ b/code/modules/events/anomaly_pyro.dm
@@ -12,4 +12,4 @@
anomaly_path = /obj/effect/anomaly/pyro
/datum/round_event/anomaly/anomaly_pyro/announce(fake)
- priority_announce("Pyroclastic anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ priority_announce("Pyroclastic anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", has_important_message = TRUE)
diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm
index 6a17a47d2b..7228030616 100644
--- a/code/modules/events/anomaly_vortex.dm
+++ b/code/modules/events/anomaly_vortex.dm
@@ -12,4 +12,4 @@
anomaly_path = /obj/effect/anomaly/bhole
/datum/round_event/anomaly/anomaly_vortex/announce(fake)
- priority_announce("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert")
+ priority_announce("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert", has_important_message = TRUE)
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index 937fb6a062..ad72f90b4c 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -15,7 +15,7 @@
/datum/round_event/ghost_role/blob/announce(fake)
if(prob(75))
- priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", "outbreak5")
+ priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", "outbreak5", has_important_message = TRUE)
else
print_command_report("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "level 5 biohazard")
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index b9480a646c..1b275af4fb 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -35,7 +35,7 @@
source = initial(example.name)
else if(originMachine)
source = originMachine.name
- priority_announce("Rampant brand intelligence has been detected aboard [station_name()]. Please stand by. The origin is believed to be \a [source].", "Machine Learning Alert")
+ priority_announce("Rampant brand intelligence has been detected aboard [station_name()]. Please stand by. The origin is believed to be \a [source].", "Machine Learning Alert", has_important_message = TRUE)
/datum/round_event/brand_intelligence/start()
for(var/obj/machinery/vending/V in GLOB.machines)
diff --git a/code/modules/events/cat_surgeon.dm b/code/modules/events/cat_surgeon.dm
index cf6e34fd65..025d2d2325 100644
--- a/code/modules/events/cat_surgeon.dm
+++ b/code/modules/events/cat_surgeon.dm
@@ -6,7 +6,7 @@
/datum/round_event/cat_surgeon/announce(fake)
priority_announce("One of our... ahem... 'special' cases has escaped. As it happens their last known location before their tracker went dead is your station so keep an eye out for them. On an unrelated note, has anyone seen our cats?",
- sender_override = "Nanotrasen Psych Ward")
+ sender_override = "Nanotrasen Psych Ward", has_important_message = TRUE)
/datum/round_event/cat_surgeon/start()
var/list/spawn_locs = list()
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 39c04003a9..941478d52a 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -29,7 +29,7 @@
fakeable = FALSE
/datum/round_event/sandstorm/announce(fake)
- priority_announce("The station is passing through a heavy debris cloud. Watch out for breaches.", "Collision Alert")
+ priority_announce("The station is passing through a heavy debris cloud. Watch out for breaches.", "Collision Alert", has_important_message = TRUE)
/datum/round_event/sandstorm/tick()
spawn_meteors(rand(6,10), GLOB.meteorsC)
diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm
index 2712e69362..482abe67f2 100644
--- a/code/modules/events/immovable_rod.dm
+++ b/code/modules/events/immovable_rod.dm
@@ -27,7 +27,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
announceWhen = 5
/datum/round_event/immovable_rod/announce(fake)
- priority_announce("What the fuck was that?!", "General Alert")
+ priority_announce("What the fuck was that?!", "General Alert", has_important_message = TRUE)
/datum/round_event/immovable_rod/start()
var/datum/round_event_control/immovable_rod/C = control
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index c75aff0c4a..f25b476657 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -26,7 +26,7 @@
/datum/round_event/ion_storm/announce(fake)
if(announceEvent == ION_ANNOUNCE || (announceEvent == ION_RANDOM && prob(ionAnnounceChance)) || fake)
- priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm")
+ priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm", has_important_message = prob(80))
/datum/round_event/ion_storm/start()
diff --git a/code/modules/events/major_dust.dm b/code/modules/events/major_dust.dm
index d7d8f1aec8..3f7cbc77f5 100644
--- a/code/modules/events/major_dust.dm
+++ b/code/modules/events/major_dust.dm
@@ -17,6 +17,6 @@
"A neighbouring station is throwing rocks at you. (Perhaps they've \
grown tired of your messages.)")
if(prob(50))
- priority_announce(pick(reason), "Collision Alert")
+ priority_announce(pick(reason), "Collision Alert", has_important_message = prob(75))
else
print_command_report("[pick(reason)]", "Collision Alert")
diff --git a/code/modules/events/meateor_wave.dm b/code/modules/events/meateor_wave.dm
index 44419e9b78..c4711055d9 100644
--- a/code/modules/events/meateor_wave.dm
+++ b/code/modules/events/meateor_wave.dm
@@ -8,4 +8,4 @@
wave_name = "meaty"
/datum/round_event/meteor_wave/meaty/announce(fake)
- priority_announce("Meaty ores have been detected on collision course with the station.", "Oh crap, get the mop.", "meteors")
+ priority_announce("Meaty ores have been detected on collision course with the station.", "Oh crap, get the mop.", "meteors", has_important_message = TRUE)
diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm
index e69af1df13..a3e0eb70ff 100644
--- a/code/modules/events/meteor_wave.dm
+++ b/code/modules/events/meteor_wave.dm
@@ -61,7 +61,7 @@
kill()
/datum/round_event/meteor_wave/announce(fake)
- priority_announce(generateMeteorString(startWhen,TRUE,direction), "Meteor Alert", "meteors")
+ priority_announce(generateMeteorString(startWhen,TRUE,direction), "Meteor Alert", "meteors", has_important_message = TRUE)
/proc/generateMeteorString(startWhen,syndiealert,direction)
var/directionstring
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index ed67c84a43..bf6026300f 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -37,7 +37,7 @@
// if(PIRATES_DUTCHMAN)
// ship_name = "Flying Dutchman"
- priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", SSstation.announcer.get_rand_report_sound())
+ priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", SSstation.announcer.get_rand_report_sound(), has_important_message = TRUE)
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D)
payoff = max(payoff_min, FLOOR(D.account_balance * 0.80, 1000))
@@ -63,16 +63,16 @@
/proc/pirates_answered(datum/comm_message/threat_msg, payoff, ship_name, initial_send_time, response_max_time, ship_template)
if(world.time > initial_send_time + response_max_time)
- priority_announce("Too late to beg for mercy!",sender_override = ship_name)
+ priority_announce("Too late to beg for mercy!",sender_override = ship_name, has_important_message = TRUE)
return
if(threat_msg && threat_msg.answered == 1)
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D)
if(D.adjust_money(-payoff))
- priority_announce("Thanks for the credits, landlubbers.",sender_override = ship_name)
+ priority_announce("Thanks for the credits, landlubbers.",sender_override = ship_name, has_important_message = TRUE)
return
else
- priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name)
+ priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name, has_important_message = TRUE)
spawn_pirates(threat_msg, ship_template, TRUE)
/proc/spawn_pirates(datum/comm_message/threat_msg, ship_template, skip_answer_check)
diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm
index 36e16bb2c9..52a207fd14 100644
--- a/code/modules/events/radiation_storm.dm
+++ b/code/modules/events/radiation_storm.dm
@@ -12,7 +12,7 @@
announceWhen = 1
/datum/round_event/radiation_storm/announce(fake)
- priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", "radiation")
+ priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", "radiation", has_important_message = TRUE)
//sound not longer matches the text, but an audible warning is probably good
/datum/round_event/radiation_storm/start()
diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm
index 35687bb7ed..1cfe777e52 100644
--- a/code/modules/events/space_dragon.dm
+++ b/code/modules/events/space_dragon.dm
@@ -12,8 +12,8 @@
announceWhen = 10
/datum/round_event/ghost_role/space_dragon/announce(fake)
- priority_announce("A large organic energy flux has been recorded near of [station_name()], please stand-by.", "Lifesign Alert")
-
+ priority_announce("A large organic energy flux has been recorded near of [station_name()], please stand-by.", "Lifesign Alert", has_important_message = TRUE)
+
/datum/round_event/ghost_role/space_dragon/spawn_role()
var/list/spawn_locs = list()
for(var/obj/effect/landmark/carpspawn/carp_spawn in GLOB.landmarks_list)
@@ -24,11 +24,11 @@
if(!spawn_locs.len)
message_admins("No valid spawn locations found, aborting...")
return MAP_ERROR
-
+
var/list/candidates = get_candidates(ROLE_SPACE_DRAGON, null, ROLE_SPACE_DRAGON)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
-
+
var/mob/dead/selected = pick_n_take(candidates)
var/datum/mind/player_mind = new /datum/mind(selected.key)
diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm
index a4db18a3b5..2464432d72 100644
--- a/code/modules/events/stray_cargo.dm
+++ b/code/modules/events/stray_cargo.dm
@@ -13,7 +13,7 @@
var/static/list/stray_spawnable_supply_packs = list() ///List of default spawnable supply packs, filtered from the cargo list
/datum/round_event/stray_cargo/announce(fake)
- priority_announce("Stray cargo pod detected on long-range scanners. Expected location of impact: [impact_area.name].", "Collision Alert")
+ priority_announce("Stray cargo pod detected on long-range scanners. Expected location of impact: [impact_area.name].", "Collision Alert", has_important_message = TRUE)
/**
* Tries to find a valid area, throws an error if none are found
diff --git a/code/modules/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm
index dd78e379ca..d873e6ca8f 100644
--- a/code/modules/events/supermatter_surge.dm
+++ b/code/modules/events/supermatter_surge.dm
@@ -20,6 +20,7 @@
/datum/round_event/supermatter_surge/announce()
var/severity = ""
+ var/important = FALSE
switch(power)
if(-INFINITY to 100000)
var/low_threat_perc = 100-round(100*((power-200)/(100000-200)))
@@ -30,10 +31,12 @@
severity = "medium; the supermatter should return to normal operation, but regardless, check if the emitters may need to be turned off temporarily."
else
severity = "high; the emitters likely need to be turned off, and if the supermatter's cooling loop is not fortified, pre-cooled gas may need to be added."
+ important = TRUE
if(100000 to INFINITY)
severity = "extreme; emergency action is likely to be required even if coolant loop is fine. Turn off the emitters and make sure the loop is properly cooling gases."
+ important = TRUE
if(power > 20000 || prob(round(power/200)))
- priority_announce("Supermatter surge detected. Estimated severity is [severity]", "Anomaly Alert")
+ priority_announce("Supermatter surge detected. Estimated severity is [severity]", "Anomaly Alert", has_important_message = important)
/datum/round_event/supermatter_surge/start()
var/obj/machinery/power/supermatter_crystal/supermatter = GLOB.main_supermatter_engine
diff --git a/code/modules/events/supernova.dm b/code/modules/events/supernova.dm
index 1b45cbf831..7862e85709 100644
--- a/code/modules/events/supernova.dm
+++ b/code/modules/events/supernova.dm
@@ -33,7 +33,7 @@
/datum/round_event/supernova/announce()
var/message = "[station_name()]: Our tachyon-doppler array has detected a supernova in your vicinity. Peak flux from the supernova estimated to be [round(power,0.1)] times current solar flux; if the supernova is close to your sun in the sky, your solars may receive this as a power boost.[power > 1 ? " Short burts of radiation may be possible, so please prepare accordingly." : "We expect no radiation bursts from this one."] We hope you enjoy the light."
if(prob(power * 25))
- priority_announce(message, sender_override = "Nanotrasen Meteorology Division")
+ priority_announce(message, sender_override = "Nanotrasen Meteorology Division", has_important_message = TRUE)
announced = TRUE
else
print_command_report(message)
diff --git a/code/modules/events/travelling_trader.dm b/code/modules/events/travelling_trader.dm
index 03aabc940b..be697c9b6d 100644
--- a/code/modules/events/travelling_trader.dm
+++ b/code/modules/events/travelling_trader.dm
@@ -28,7 +28,7 @@
trader.visible_message("[trader] suddenly appears in a puff of smoke!")
/datum/round_event/travelling_trader/announce(fake)
- priority_announce("A mysterious figure has been detected on sensors at [get_area(spawn_location)]", "Mysterious Figure")
+ priority_announce("A mysterious figure has been detected on sensors at [get_area(spawn_location)]", "Mysterious Figure", has_important_message = !fake)
/datum/round_event/travelling_trader/end()
if(trader) // the /datum/round_event/travelling_trader has given up on waiting!
diff --git a/code/modules/events/untie_shoes.dm b/code/modules/events/untie_shoes.dm
index 708a72f40b..5a0da3ebc2 100644
--- a/code/modules/events/untie_shoes.dm
+++ b/code/modules/events/untie_shoes.dm
@@ -1,8 +1,8 @@
/datum/round_event_control/untied_shoes
name = "Untied Shoes"
typepath = /datum/round_event/untied_shoes
- weight = 100
- max_occurrences = 20
+ weight = 50
+ max_occurrences = 10
alert_observers = FALSE
/datum/round_event/untied_shoes
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index 57f2648573..2c03531bcc 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -59,7 +59,7 @@
//needs to be chemid unit checked at some point
/datum/round_event/vent_clog/announce()
- priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert")
+ priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert", has_important_message = TRUE)
/datum/round_event/vent_clog/setup()
endWhen = rand(120, 180)
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index e723ddd79b..3207e3b635 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -32,7 +32,7 @@
wormholes += new /obj/effect/portal/wormhole(T, 0, null, FALSE)
/datum/round_event/wormholes/announce(fake)
- priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", "spanomalies")
+ priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", "spanomalies", has_important_message = TRUE)
/datum/round_event/wormholes/tick()
if(activeFor % shift_frequency == 0)
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index c5cb978f40..6bf2764ba8 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -27,7 +27,7 @@
CRASH("dynamic preview is unsupported")
. = H.AIize(latejoin,preference_source)
-/datum/job/ai/after_spawn(mob/H, mob/M, latejoin)
+/datum/job/ai/after_spawn(mob/H, client/C, latejoin)
. = ..()
if(latejoin)
var/obj/structure/AIcore/latejoin_inactive/lateJoinCore
@@ -41,8 +41,8 @@
H.forceMove(lateJoinCore.loc)
qdel(lateJoinCore)
var/mob/living/silicon/ai/AI = H
- AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked.
- AI.set_core_display_icon(null, M.client)
+ AI.apply_pref_name("ai", C) //If this runtimes oh well jobcode is fucked.
+ AI.set_core_display_icon(null, C)
//we may have been created after our borg
if(SSticker.current_state == GAME_STATE_SETTING_UP)
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index 8290adbbd7..c9f608fc00 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -32,7 +32,7 @@
backpack_contents = list(/obj/item/storage/box/beanbag=1,/obj/item/book/granter/action/drink_fling=1)
shoes = /obj/item/clothing/shoes/laceup
-/datum/job/bartender/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
+/datum/job/bartender/after_spawn(mob/living/H, client/C, latejoin = FALSE)
. = ..()
var/datum/action/innate/drink_fling/D = new
D.Grant(H)
diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm
index 7789e3c614..ef4f20765c 100644
--- a/code/modules/jobs/job_types/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain.dm
@@ -21,7 +21,7 @@
threat = 0.5
-/datum/job/chaplain/after_spawn(mob/living/H, mob/M)
+/datum/job/chaplain/after_spawn(mob/living/H, client/C)
. = ..()
if(H.mind)
H.mind.isholy = TRUE
@@ -41,12 +41,12 @@
return
var/new_religion = DEFAULT_RELIGION
- if(M.client && M.client.prefs.custom_names["religion"])
- new_religion = M.client.prefs.custom_names["religion"]
+ if(C && C.prefs.custom_names["religion"])
+ new_religion = C.prefs.custom_names["religion"]
var/new_deity = DEFAULT_DEITY
- if(M.client && M.client.prefs.custom_names["deity"])
- new_deity = M.client.prefs.custom_names["deity"]
+ if(C && C.prefs.custom_names["deity"])
+ new_deity = C.prefs.custom_names["deity"]
B.deity_name = new_deity
diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm
index e00b3a1e29..171e0cef8a 100644
--- a/code/modules/jobs/job_types/mime.dm
+++ b/code/modules/jobs/job_types/mime.dm
@@ -21,9 +21,9 @@
threat = 0
-/datum/job/mime/after_spawn(mob/living/carbon/human/H, mob/M)
+/datum/job/mime/after_spawn(mob/living/carbon/human/H, client/C)
. = ..()
- H.apply_pref_name("mime", M.client)
+ H.apply_pref_name("mime", C)
/datum/outfit/job/mime
name = "Mime"
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 12d2471568..06eafe649b 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -17,12 +17,12 @@
/datum/job/prisoner/get_latejoin_spawn_point()
return get_roundstart_spawn_point()
-/datum/job/prisoner/after_spawn(mob/living/carbon/human/H, mob/M)
+/datum/job/prisoner/after_spawn(mob/living/carbon/human/H, client/C)
. = ..()
var/list/policies = CONFIG_GET(keyed_list/policy)
var/policy = policies[POLICYCONFIG_JOB_PRISONER]
if(policy)
- var/mob/found = (M?.client && M) || (H?.client && H)
+ var/mob/found = (H?.client && H)
to_chat(found, "
!!READ THIS!!
The following is server-specific policy configuration and overrides anything said above if conflicting.")
to_chat(found, "
")
to_chat(found, "[policy]")
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 29d3bd7868..a1f3e13fcc 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -98,6 +98,16 @@
name = "garden & kitchen bluespace shelter capsule"
desc = "Everything someone needs to make a home cooked meal while surviving the depths of hell... or space."
template_id = "shelter_zeta"
+
+// RBMK reactor beacon so people can create the engine
+
+/obj/item/survivalcapsule/reactor // the not-so-survival capsule
+ name = "RMBK Reactor Beacon"
+ desc = "A special bluespace beacon designed to implement a reactor into the hull of the ship or station that it is activated on."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "beacon"
+ template_id = "reactor"
+
//Pod objects
//Window
diff --git a/code/modules/mining/lavaland/ruins/gym.dm b/code/modules/mining/lavaland/ruins/gym.dm
index c26631be74..68e0706d84 100644
--- a/code/modules/mining/lavaland/ruins/gym.dm
+++ b/code/modules/mining/lavaland/ruins/gym.dm
@@ -1,7 +1,7 @@
/obj/structure/punching_bag
name = "punching bag"
desc = "A punching bag. Can you get to speed level 4???"
- icon = 'goon/icons/obj/fitness.dmi'
+ icon = 'icons/obj/fitness.dmi'
icon_state = "punchingbag"
anchored = TRUE
layer = WALL_OBJ_LAYER
@@ -12,7 +12,7 @@
. = ..()
if(.)
return
- flick("[icon_state]2", src)
+ flick("[icon_state]-punch", src)
playsound(loc, pick(hit_sounds), 25, 1, -1)
if(isliving(user))
var/mob/living/L = user
@@ -20,15 +20,24 @@
L.apply_status_effect(STATUS_EFFECT_EXERCISED)
/obj/structure/weightmachine
- name = "Weight Machine"
desc = "Just looking at this thing makes you feel tired."
+ icon = 'icons/obj/fitness.dmi'
density = TRUE
anchored = TRUE
- var/icon_state_inuse
/obj/structure/weightmachine/proc/AnimateMachine(mob/living/user)
return
+/obj/structure/weightmachine/update_icon_state()
+ . = ..()
+ icon_state = (obj_flags & IN_USE) ? "[base_icon_state]-u" : base_icon_state
+
+/obj/structure/weightmachine/update_overlays()
+ . = ..()
+
+ if(obj_flags & IN_USE)
+ . += mutable_appearance(icon, "[base_icon_state]-o", layer = ABOVE_MOB_LAYER, alpha = src.alpha)
+
/obj/structure/weightmachine/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
@@ -38,7 +47,7 @@
return
else
obj_flags |= IN_USE
- icon_state = icon_state_inuse
+ update_appearance()
user.setDir(SOUTH)
user.Stun(80)
user.forceMove(src.loc)
@@ -48,17 +57,17 @@
playsound(user, 'sound/machines/click.ogg', 60, 1)
obj_flags &= ~IN_USE
+ update_appearance()
user.pixel_y = 0
var/finishmessage = pick("You feel stronger!","You feel like you can take on the world!","You feel robust!","You feel indestructible!")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "exercise", /datum/mood_event/exercise)
- icon_state = initial(icon_state)
to_chat(user, finishmessage)
user.apply_status_effect(STATUS_EFFECT_EXERCISED)
/obj/structure/weightmachine/stacklifter
- icon = 'goon/icons/obj/fitness.dmi'
- icon_state = "fitnesslifter"
- icon_state_inuse = "fitnesslifter2"
+ name = "chest press machine"
+ icon_state = "stacklifter"
+ base_icon_state = "stacklifter"
/obj/structure/weightmachine/stacklifter/AnimateMachine(mob/living/user)
var/lifts = 0
@@ -69,27 +78,24 @@
animate(user, pixel_y = -2, time = 3)
sleep(3)
animate(user, pixel_y = -4, time = 3)
- sleep(3)
- playsound(user, 'goon/sound/effects/spring.ogg', 60, 1)
+ sleep(2)
+ playsound(user, 'sound/machines/creak.ogg', 60, 1)
/obj/structure/weightmachine/weightlifter
- icon = 'goon/icons/obj/fitness.dmi'
- icon_state = "fitnessweight"
- icon_state_inuse = "fitnessweight-c"
+ name = "inline bench press"
+ icon_state = "benchpress"
+ base_icon_state = "benchpress"
/obj/structure/weightmachine/weightlifter/AnimateMachine(mob/living/user)
- var/mutable_appearance/swole_overlay = mutable_appearance(icon, "fitnessweight-w", WALL_OBJ_LAYER)
- add_overlay(swole_overlay)
var/reps = 0
user.pixel_y = 5
while (reps++ < 6)
if (user.loc != src.loc)
break
for (var/innerReps = max(reps, 1), innerReps > 0, innerReps--)
- sleep(3)
+ sleep(4)
animate(user, pixel_y = (user.pixel_y == 3) ? 5 : 3, time = 3)
- playsound(user, 'goon/sound/effects/spring.ogg', 60, 1)
+ playsound(user, 'sound/machines/creak.ogg', 60, 1)
sleep(3)
animate(user, pixel_y = 2, time = 3)
sleep(3)
- cut_overlay(swole_overlay)
diff --git a/code/modules/mining/shelters.dm b/code/modules/mining/shelters.dm
index 3da0c0a2c7..a349622028 100644
--- a/code/modules/mining/shelters.dm
+++ b/code/modules/mining/shelters.dm
@@ -107,3 +107,11 @@
. = ..()
whitelisted_turfs = typecacheof(/turf/closed/mineral)
banned_objects = typecacheof(/obj/structure/stone_tile)
+
+// yes, I COULD make it a seperate object from the surv capsule but the code needed is indeticle soo...
+
+/datum/map_template/shelter/reactor
+ name = "RBMK Reactor"
+ shelter_id = "reactor"
+ description = "A reactor core, coolant and moderator loop not included."
+ mappath = "_maps/templates/reactor_1.dmm"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm
index 07adec0aa4..8a632247bb 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm
@@ -216,6 +216,26 @@
icon_state = "harpywings"
upgrade_to = SPECIES_WINGS_ANGEL
+/datum/sprite_accessory/deco_wings/harpywingsalt
+ name = "Harpy (Alt)"
+ icon_state = "harpywingsalt"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
+/datum/sprite_accessory/deco_wings/harpywingsaltcollar
+ name = "Harpy (Alt Collar)"
+ icon_state = "harpywingsaltcollar"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
+/datum/sprite_accessory/deco_wings/harpywingsbat
+ name = "Harpy (Bat)"
+ icon_state = "harpywingsbat"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
+/datum/sprite_accessory/deco_wings/harpywingsbatcollar
+ name = "Harpy (Bat Collar)"
+ icon_state = "harpywingsbatcollar"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
/datum/sprite_accessory/deco_wings/roboticwing
name = "Robotic"
icon_state = "drago"
@@ -420,6 +440,26 @@
icon_state = "harpywings"
upgrade_to = SPECIES_WINGS_ANGEL
+/datum/sprite_accessory/insect_wings/harpywingsalt
+ name = "Harpy (Alt)"
+ icon_state = "harpywingsalt"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
+/datum/sprite_accessory/insect_wings/harpywingsaltcollar
+ name = "Harpy (Alt Collar)"
+ icon_state = "harpywingsaltcollar"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
+/datum/sprite_accessory/insect_wings/harpywingsbat
+ name = "Harpy (Bat)"
+ icon_state = "harpywingsbat"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
+/datum/sprite_accessory/insect_wings/harpywingsbatcollar
+ name = "Harpy (Bat Collar)"
+ icon_state = "harpywingsbatcollar"
+ upgrade_to = SPECIES_WINGS_ANGEL
+
/datum/sprite_accessory/insect_wings/roboticwing
name = "Robotic"
icon_state = "drago"
diff --git a/code/modules/mob/living/carbon/human/species_types/xeno.dm b/code/modules/mob/living/carbon/human/species_types/xeno.dm
index ffb8ac5a80..cd3ba8d5cc 100644
--- a/code/modules/mob/living/carbon/human/species_types/xeno.dm
+++ b/code/modules/mob/living/carbon/human/species_types/xeno.dm
@@ -14,7 +14,7 @@
skinned_type = /obj/item/stack/sheet/animalhide/xeno
exotic_bloodtype = "X*"
damage_overlay_type = "xeno"
- disliked_food = JUNKFOOD
+ disliked_food = null
liked_food = GROSS | MEAT
species_category = SPECIES_CATEGORY_ALIEN
wings_icons = SPECIES_WINGS_DRAGON //most depictions of xenomorphs with wings are closest to this.
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index ffe88705e4..d18014f4d6 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -322,6 +322,30 @@
else
to_chat(user, "Encryption Key ports not configured.")
+/obj/item/paicard/attack_ghost(mob/dead/observer/user)
+ if(pai)
+ to_chat(user, "This pAI is already in use!")
+ return
+
+ var/area/A = get_area(get_turf(src))
+ if(A.type in SSpai.restricted_areas) // set in subsystem/pai.dm on initialize of the subsystem
+ to_chat(user, "You can't download yourself into a restricted area!")
+ return
+
+ var/pai_name = reject_bad_name(stripped_input(usr, "Enter a name for your pAI", "pAI Name", user.name, MAX_NAME_LEN), TRUE)
+ if(!pai_name)
+ to_chat(user, "Entered name is not valid.")
+ return
+
+ var/mob/living/silicon/pai/new_pai = new(src)
+ new_pai.name = pai_name
+ new_pai.real_name = new_pai.name
+ new_pai.key = user.key
+
+ setPersonality(new_pai)
+
+ SSticker.mode.update_cult_icons_removed(pai.mind)
+
/obj/item/paicard/emag_act(mob/user) // Emag to wipe the master DNA and supplemental directive
. = ..()
if(!pai)
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 0cb2a65375..f7ec9c625e 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -37,6 +37,7 @@
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/supermatter_monitor())
+ hard_drive.store_file(new/datum/computer_file/program/nuclear_monitor())
// ===== RESEARCH CONSOLE =====
/obj/machinery/modular_computer/console/preset/research
diff --git a/code/modules/power/reactor/fuel_rods.dm b/code/modules/power/reactor/fuel_rods.dm
new file mode 100644
index 0000000000..6f71ec0b1e
--- /dev/null
+++ b/code/modules/power/reactor/fuel_rods.dm
@@ -0,0 +1,251 @@
+/obj/item/fuel_rod
+ name = "Uranium-235 Fuel Rod"
+ desc = "A titanium sheathed rod containing a measure of enriched uranium-dioxide powder inside, and a breeding blanket of uranium-238 around it, used to kick off a fission reaction and breed plutonium fuel respectivly."
+ icon = 'icons/obj/control_rod.dmi'
+ icon_state = "irradiated"
+ w_class = WEIGHT_CLASS_BULKY
+ var/depletion = 0 //Each fuel rod will deplete in around 30 minutes.
+ var/fuel_power = 0.10
+ var/rad_strength = 500
+ var/half_life = 2000 // how many depletion ticks are needed to half the fuel_power (1 tick = 1 second)
+ var/time_created = 0
+ var/og_fuel_power = 0.20 //the original fuel power value
+ var/process = FALSE
+ // The depletion where depletion_final() will be called (and does something)
+ var/depletion_threshold = 100
+ // How fast this rod will deplete
+ var/depletion_speed_modifier = 1
+ var/depleted_final = FALSE // depletion_final should run only once
+ var/depletion_conversion_type = "plutonium"
+
+/obj/item/fuel_rod/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
+
+/obj/item/fuel_rod/Initialize()
+ . = ..()
+ time_created = world.time
+ AddComponent(/datum/component/radioactive, rad_strength, src) // This should be temporary for it won't make rads go lower than 350
+ if(process)
+ START_PROCESSING(SSobj, src)
+
+/obj/item/fuel_rod/Destroy()
+ if(process)
+ STOP_PROCESSING(SSobj, src)
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/N = loc
+ if(istype(N))
+ N.fuel_rods -= src
+ . = ..()
+
+// This proc will try to convert your fuel rod if you don't override this proc
+// So, ideally, you should write an override of this for every fuel rod you want to create
+/obj/item/fuel_rod/proc/depletion_final(result_rod)
+ if(!result_rod)
+ return
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/N = loc
+ // Rod conversion is moot when you can't find the reactor
+ if(istype(N))
+ var/obj/item/fuel_rod/R
+
+ // You can add your own depletion scheme and not override this proc if you are going to convert a fuel rod into another type
+ switch(result_rod)
+ if("plutonium")
+ R = new /obj/item/fuel_rod/plutonium(loc)
+ R.depletion = depletion
+ if(prob(1))
+ R.name = "Plush-239 Fuel Rod"
+ R.desc = "NanoTrasen would like to remind you that it is not liable for any permanent radioactive damage done to its employees."
+ R.icon = 'icons/obj/plushes.dmi'
+ R.icon_state = "romanian"
+ R.fuel_power = 0.25 //Funny easter egg, slightly more powerful too.
+ if("depleted")
+ if(fuel_power < 10)
+ fuel_power = 0
+ playsound(loc, 'sound/effects/supermatter.ogg', 100, TRUE)
+ R = new /obj/item/fuel_rod/depleted(loc)
+ R.depletion = depletion
+
+ // Finalization of conversion
+ if(istype(R))
+ N.fuel_rods += R
+ qdel(src)
+ else
+ depleted_final = FALSE // Maybe try again later?
+
+/obj/item/fuel_rod/proc/deplete(amount=0.035)
+ depletion += amount * depletion_speed_modifier
+ if(depletion >= depletion_threshold && !depleted_final)
+ depleted_final = TRUE
+ depletion_final(depletion_conversion_type)
+
+/obj/item/fuel_rod/plutonium
+ fuel_power = 0.20
+ name = "Plutonium-239 Fuel Rod"
+ desc = "A highly energetic titanium sheathed rod containing a sizeable measure of weapons grade plutonium, it's highly efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
+ icon_state = "inferior"
+ rad_strength = 1500
+ process = TRUE // for half life code
+ depletion_threshold = 300
+ depletion_conversion_type = "depleted"
+
+/obj/item/fuel_rod/process()
+ fuel_power = og_fuel_power * 0.5**((world.time - time_created) / half_life SECONDS) // halves the fuel power every half life (33 minutes)
+
+/obj/item/fuel_rod/depleted
+ fuel_power = 0.05
+ name = "Depleted Fuel Rod"
+ desc = "A highly radioactive fuel rod which has expended most of it's useful energy."
+ icon_state = "normal"
+ rad_strength = 6000 // smelly
+ depletion_conversion_type = null // It means that it won't turn into anything
+ process = TRUE
+
+// Master type for material optional (or requiring, wyci) and/or producing rods
+/obj/item/fuel_rod/material
+ // Whether the rod has been harvested. Should be set in expend().
+ var/expended = FALSE
+ // The material that will be inserted and then multiplied (or not). Should be some sort of /obj/item/stack
+ var/material_type
+ // The name of material that'll be used for texts
+ var/material_name
+ var/material_name_singular
+ var/initial_amount = 0
+ // The maximum amount of material the rod can hold
+ var/max_initial_amount = 10
+ var/grown_amount = 0
+ // The multiplier for growth. 1 for the same 2 for double etc etc
+ var/multiplier = 2
+ // After this depletion, you won't be able to add new materials
+ var/material_input_deadline = 25
+ // Material fuel rods generally don't get converted into another fuel object
+ depletion_conversion_type = null
+
+// Called when the rod is fully harvested
+/obj/item/fuel_rod/material/proc/expend()
+ expended = TRUE
+
+// Basic checks for material rods
+/obj/item/fuel_rod/material/proc/check_material_input(mob/user)
+ if(depletion >= material_input_deadline)
+ to_chat(user, "The sample slots have sealed themselves shut, it's too late to add [material_name] now!") // no cheesing in crystals at 100%
+ return FALSE
+ if(expended)
+ to_chat(user, "\The [src]'s material slots have already been used.")
+ return FALSE
+ return TRUE
+
+// The actual growth
+/obj/item/fuel_rod/material/depletion_final(result_rod)
+ if(result_rod)
+ ..() // So if you put anything into depletion_conversion_type then your fuel rod will be converted (or not) and *won't grow*
+ else
+ grown_amount = initial_amount * multiplier
+
+/obj/item/fuel_rod/material/attackby(obj/item/W, mob/user, params)
+ var/obj/item/stack/M = W
+ if(istype(M, material_type))
+ if(!check_material_input(user))
+ return
+ if(initial_amount < max_initial_amount)
+ var/adding = min((max_initial_amount - initial_amount), M.amount)
+ M.amount -= adding
+ initial_amount += adding
+ if (adding == 1)
+ to_chat(user, "You insert [adding] [material_name_singular] into \the [src].")
+ else
+ to_chat(user, "You insert [adding] [material_name] into \the [src].")
+ M.zero_amount()
+ else
+ to_chat(user, "\The [src]'s material slots are full!")
+ return
+ else
+ return ..()
+
+/obj/item/fuel_rod/material/attack_self(mob/user)
+ if(expended)
+ to_chat(user, "You have already removed [material_name] from \the [src].")
+ return
+
+ if(depleted_final)
+ new material_type(user.loc, grown_amount)
+ if (grown_amount == 1)
+ to_chat(user, "You harvest [grown_amount] [material_name_singular] from \the [src].") // Unlikely
+ else
+ to_chat(user, "You harvest [grown_amount] [material_name] from \the [src].")
+ playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, 1)
+ grown_amount = 0
+ expend()
+ else if(depletion)
+ to_chat(user, "\The [src] has not fissiled enough to fully grow the sample. The progress bar shows it is [min(depletion/depletion_threshold*100,100)]% complete.")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
+ else if(initial_amount)
+ new material_type(user.loc, initial_amount)
+ if (initial_amount == 1)
+ to_chat(user, "You remove [initial_amount] [material_name_singular] from \the [src].")
+ else
+ to_chat(user, "You remove [initial_amount] [material_name] from \the [src].")
+ playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, 1)
+ initial_amount = 0
+
+/obj/item/fuel_rod/material/examine(mob/user)
+ . = ..()
+ if(expended)
+ . += "The material slots have been slagged by the extreme heat, you can't grow [material_name] in this rod again..."
+ return
+ else if(depleted_final)
+ . += "This fuel rod's [material_name] are now fully grown, and it currently bears [grown_amount] harvestable [material_name_singular]\s."
+ return
+ if(depletion)
+ . += "The sample is [min(depletion/depletion_threshold*100,100)]% fissiled."
+ . += "[initial_amount]/[max_initial_amount] of the slots for [material_name] are full."
+
+/obj/item/fuel_rod/material/telecrystal
+ name = "Telecrystal Fuel Rod"
+ desc = "A disguised titanium sheathed rod containing several small slots infused with uranium dioxide. Permits the insertion of telecrystals to grow more. Fissiles much faster than its standard counterpart"
+ icon_state = "telecrystal"
+ fuel_power = 0.30 // twice as powerful as a normal rod, you're going to need some engineering autism if you plan to mass produce TC
+ depletion_speed_modifier = 3 // headstart, otherwise it takes two hours
+ rad_strength = 1500
+ max_initial_amount = 8
+ multiplier = 3
+ material_type = /obj/item/stack/telecrystal
+ material_name = "telecrystals"
+ material_name_singular = "telecrystal"
+
+/obj/item/fuel_rod/material/telecrystal/depletion_final(result_rod)
+ ..()
+ if(result_rod)
+ return
+ fuel_power = 0.60 // thrice as powerful as plutonium, you'll want to get this one out quick!
+ name = "Exhausted Telecrystal Fuel Rod"
+ desc = "A highly energetic, disguised titanium sheathed rod containing a number of slots filled with greatly expanded telecrystals which can be removed by hand. It's extremely efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
+ icon_state = "telecrystal_used"
+ AddComponent(/datum/component/radioactive, 3000, src)
+
+/obj/item/fuel_rod/material/bananium
+ name = "Bananium Fuel Rod"
+ desc = "A hilarious heavy-duty fuel rod which fissiles a bit slower than its cowardly counterparts. However, its cutting-edge cosmic clown technology allows rooms for extraordinarily exhilarating extraterrestrial element called bananium to menacingly multiply."
+ icon_state = "bananium"
+ fuel_power = 0.15
+ depletion_speed_modifier = 3
+ rad_strength = 350
+ max_initial_amount = 10
+ multiplier = 3
+ material_type = /obj/item/stack/sheet/mineral/bananium
+ material_name = "sheets of bananium"
+ material_name_singular = "sheet of bananium"
+
+/obj/item/fuel_rod/material/bananium/deplete(amount=0.035)
+ ..()
+ if(initial_amount == max_initial_amount && prob(10))
+ playsound(src, pick('sound/items/bikehorn.ogg', 'sound/misc/bikehorn_creepy.ogg'), 50) // HONK
+
+/obj/item/fuel_rod/material/bananium/depletion_final(result_rod)
+ ..()
+ if(result_rod)
+ return
+ fuel_power = 0.3 // Be warned
+ name = "Fully Grown Bananium Fuel Rod"
+ desc = "A hilarious heavy-duty fuel rod which fissiles a bit slower than it cowardly counterparts. Its greatly grimacing grwoth stage is now over, and bananium outgrowth hums as if it's blatantly honking bike horns."
+ icon_state = "bananium_used"
+ AddComponent(/datum/component/radioactive, 1250, src)
diff --git a/code/modules/power/reactor/rbmk.dm b/code/modules/power/reactor/rbmk.dm
new file mode 100644
index 0000000000..609ec72984
--- /dev/null
+++ b/code/modules/power/reactor/rbmk.dm
@@ -0,0 +1,1012 @@
+//For my sanity :))
+
+#define COOLANT_INPUT_GATE airs[1]
+#define MODERATOR_INPUT_GATE airs[2]
+#define COOLANT_OUTPUT_GATE airs[3]
+
+#define RBMK_TEMPERATURE_OPERATING 640 //Celsius
+#define RBMK_TEMPERATURE_CRITICAL 810 //At this point the entire ship/station is alerted to a meltdown. This may need altering
+#define RBMK_TEMPERATURE_MELTDOWN 900
+
+#define RBMK_NO_COOLANT_TOLERANCE 5 //How many process()ing ticks the reactor can sustain without coolant before slowly taking damage
+
+#define RBMK_PRESSURE_OPERATING 1000 //PSI
+#define RBMK_PRESSURE_CRITICAL 1469.59 //PSI
+
+#define RBMK_MAX_CRITICALITY 3 //No more criticality than N for now.
+
+#define RBMK_POWER_FLAVOURISER 1000 //To turn those KWs into something usable
+
+//Math. Lame.
+#define KPA_TO_PSI(A) (A/6.895)
+#define PSI_TO_KPA(A) (A*6.895)
+#define KELVIN_TO_CELSIUS(A) (A-273.15)
+#define MEGAWATTS /1e+6
+#define CELSIUS_TO_KELVIN(T_K) ((T_K) + T0C)
+
+//Reference: Heaters go up to 500K.
+//Hot plasmaburn: 14164.95 C.
+
+/**
+What is this?
+Moderators list (Not gonna keep this accurate forever):
+Fuel Type:
+Oxygen: Power production multiplier. Allows you to run a low plasma, high oxy mix, and still get a lot of power.
+Plasma: Power production gas. More plasma -> more power, but it enriches your fuel and makes the reactor much, much harder to control.
+Tritium: Extremely efficient power production gas. Will cause chernobyl if used improperly.
+Moderation Type:
+N2: Helps you regain control of the reaction by increasing control rod effectiveness, will massively boost the rad production of the reactor.
+CO2: Super effective shutdown gas for runaway reactions. MASSIVE RADIATION PENALTY!
+Pluoxium: Same as N2, but no cancer-rads!
+Permeability Type:
+BZ: Increases your reactor's ability to transfer its heat to the coolant, thus letting you cool it down faster (but your output will get hotter)
+Water Vapour: More efficient permeability modifier
+Hyper Noblium: Extremely efficient permeability increase. (10x as efficient as bz)
+Depletion type:
+Nitryl: When you need weapons grade plutonium yesterday. Causes your fuel to deplete much, much faster. Not a huge amount of use outside of sabotage.
+Sabotage:
+Meltdown:
+Flood reactor moderator with plasma, they won't be able to mitigate the reaction with control rods.
+Shut off coolant entirely. Raise control rods.
+Swap all fuel out with spent fuel, as it's way stronger.
+Blowout:
+Shut off exit valve for quick overpressure.
+Cause a pipefire in the coolant line (LETHAL).
+Tack heater onto coolant line (can also cause straight meltdown)
+Tips:
+Be careful to not exhaust your plasma supply. I recommend you DON'T max out the moderator input when youre running plasma + o2, or you're at a tangible risk of running out of those gasses from atmos.
+The reactor CHEWS through moderator. It does not do this slowly. Be very careful with that!
+*/
+
+//Remember kids. If the reactor itself is not physically powered by an APC, it cannot shove coolant in!
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor
+ name = "Advanced Gas-Cooled Nuclear Reactor"
+ desc = "A tried and tested design which can output stable power at an acceptably low risk. The moderator can be changed to provide different effects."
+ icon = 'icons/obj/machines/rbmk.dmi'
+ icon_state = "reactor_map"
+ pixel_x = -32
+ pixel_y = -32
+ density = FALSE //It burns you if you're stupid enough to walk over it.
+ anchored = TRUE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
+ light_color = LIGHT_COLOR_CYAN
+ dir = WEST //Less headache inducing :))
+ var/id = null //Change me mappers
+ //Variables essential to operation
+ var/temperature = 0 //Lose control of this -> Meltdown
+ var/vessel_integrity = 400 //How long can the reactor withstand overpressure / meltdown? This gives you a fair chance to react to even a massive pipe fire
+ var/pressure = 0 //Lose control of this -> Blowout
+ var/K = 0 //Rate of reaction.
+ var/desired_k = 0
+ var/control_rod_effectiveness = 0.65 //Starts off with a lot of control over K. If you flood this thing with plasma, you lose your ability to control K as easily.
+ var/power = 0 //0-100%. A function of the maximum heat you can achieve within operating temperature
+ var/power_modifier = 1 //Upgrade me with parts, science! Flat out increase to physical power output when loaded with plasma.
+ var/list/fuel_rods = list()
+ //Secondary variables.
+ var/next_slowprocess = 0
+ var/gas_absorption_effectiveness = 0.5
+ var/gas_absorption_constant = 0.5 //We refer to this one as it's set on init, randomized.
+ var/minimum_coolant_level = 2
+ var/warning = FALSE //Have we begun warning the crew of their impending death?
+ var/next_warning = 0 //To avoid spam.
+ var/last_power_produced = 0 //For logging purposes
+ var/next_flicker = 0 //Light flicker timer
+ var/base_power_modifier = RBMK_POWER_FLAVOURISER
+ var/slagged = FALSE //Is this reactor even usable any more?
+ //Console statistics.
+ var/last_coolant_temperature = 0
+ var/last_output_temperature = 0
+ var/last_heat_delta = 0 //For administrative cheating only. Knowing the delta lets you know EXACTLY what to set K at.
+ var/no_coolant_ticks = 0 //How many times in succession did we not have enough coolant? Decays twice as fast as it accumulates.
+ var/last_user = null
+ var/current_desired_k = null
+ var/datum/looping_sound/rbmk_reactor/soundloop
+ var/datum/powernet/powernet = null
+
+//Use this in your maps if you want everything to be preset.
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/preset
+ id = "default_reactor_for_lazy_mappers"
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/examine(mob/user)
+ . = ..()
+ if(Adjacent(src, user))
+ if(do_after(user, 1 SECONDS, target=src))
+ var/percent = vessel_integrity / initial(vessel_integrity) * 100
+ var/msg = "The reactor looks operational."
+ switch(percent)
+ if(0 to 10)
+ msg = "[src]'s seals are dangerously warped and you can see cracks all over the reactor vessel! "
+ if(10 to 40)
+ msg = "[src]'s seals are heavily warped and cracked! "
+ if(40 to 60)
+ msg = "[src]'s seals are holding, but barely. You can see some micro-fractures forming in the reactor vessel."
+ if(60 to 80)
+ msg = "[src]'s seals are in-tact, but slightly worn. There are no visible cracks in the reactor vessel."
+ if(80 to 90)
+ msg = "[src]'s seals are in good shape, and there are no visible cracks in the reactor vessel."
+ if(95 to 100)
+ msg = "[src]'s seals look factory new, and the reactor's in excellent shape."
+ . += msg
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/fuel_rod))
+ if(power >= 20)
+ to_chat(user, "You cannot insert fuel into [src] when it has been raised above 20% power.")
+ return FALSE
+ if(fuel_rods.len >= 5)
+ to_chat(user, "[src] is already at maximum fuel load.")
+ return FALSE
+ to_chat(user, "You start to insert [W] into [src]...")
+ radiation_pulse(src, temperature)
+ if(do_after(user, 5 SECONDS, target=src))
+ if(!fuel_rods.len)
+ start_up() //That was the first fuel rod. Let's heat it up.
+ message_admins("Reactor first started up by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(src)]")
+ investigate_log("Reactor first started by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
+ fuel_rods += W
+ W.forceMove(src)
+ radiation_pulse(src, temperature) //Wear protective equipment when even breathing near a reactor!
+ investigate_log("Rod added to reactor by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
+ return TRUE
+ if(istype(W, /obj/item/sealant))
+ if(power >= 20)
+ to_chat(user, "You cannot repair [src] while it is running at above 20% power.")
+ return FALSE
+ if(vessel_integrity >= 350)
+ to_chat(user, "[src]'s seals are already in-tact, repairing them further would require a new set of seals.")
+ return FALSE
+ if(vessel_integrity <= 0.5 * initial(vessel_integrity)) //Heavily damaged.
+ to_chat(user, "[src]'s reactor vessel is cracked and worn, you need to repair the cracks with a welder before you can repair the seals.")
+ return FALSE
+ if(do_after(user, 5 SECONDS, target=src))
+ if(vessel_integrity >= 350) //They might've stacked doafters
+ to_chat(user, "[src]'s seals are already in-tact, repairing them further would require a new set of seals.")
+ return FALSE
+ playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6)
+ user.visible_message("[user] applies sealant to some of [src]'s worn out seals.", "You apply sealant to some of [src]'s worn out seals.")
+ vessel_integrity += 10
+ vessel_integrity = clamp(vessel_integrity, 0, initial(vessel_integrity))
+ return TRUE
+ return ..()
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/welder_act(mob/living/user, obj/item/I)
+ if(power >= 20)
+ to_chat(user, "You can't repair [src] while it is running at above 20% power.")
+ return FALSE
+ if(vessel_integrity > 0.5 * initial(vessel_integrity))
+ to_chat(user, "[src] is free from cracks. Further repairs must be carried out with flexi-seal sealant.")
+ return FALSE
+ if(I.use_tool(src, user, 0, volume=40))
+ if(vessel_integrity > 0.5 * initial(vessel_integrity))
+ to_chat(user, "[src] is free from cracks. Further repairs must be carried out with flexi-seal sealant.")
+ return FALSE
+ vessel_integrity += 20
+ to_chat(user, "You weld together some of [src]'s cracks. This'll do for now.")
+ return TRUE
+
+//Admin procs to mess with the reaction environment.
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/lazy_startup()
+ for(var/I=0;I<5;I++)
+ fuel_rods += new /obj/item/fuel_rod(src)
+ message_admins("Reactor started up by admins in [ADMIN_VERBOSEJMP(src)]")
+ start_up()
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/deplete()
+ for(var/obj/item/fuel_rod/FR in fuel_rods)
+ FR.depletion = 100
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/Initialize()
+ . = ..()
+ connect_to_network()
+ icon_state = "reactor_off"
+ gas_absorption_effectiveness = rand(5, 6)/10 //All reactors are slightly different. This will result in you having to figure out what the balance is for K.
+ gas_absorption_constant = gas_absorption_effectiveness //And set this up for the rest of the round.
+ STOP_PROCESSING(SSmachines, src) //We'll handle this one ourselves.
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/Crossed(atom/movable/AM, oldloc)
+ . = ..()
+ if(isliving(AM) && temperature > 0)
+ var/mob/living/L = AM
+ L.adjust_bodytemperature(clamp(temperature, BODYTEMP_COOLING_MAX, BODYTEMP_HEATING_MAX)) //If you're on fire, you heat up!
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/process()
+ update_parents() //Update the pipenet to register new gas mixes
+ if(next_slowprocess < world.time)
+ slowprocess()
+ next_slowprocess = world.time + 1 SECONDS //Set to wait for another second before processing again, we don't need to process more than once a second
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/has_fuel()
+ return fuel_rods?.len
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/slowprocess()
+ //Let's get our gasses sorted out.
+ var/datum/gas_mixture/coolant_input = COOLANT_INPUT_GATE
+ var/datum/gas_mixture/moderator_input = MODERATOR_INPUT_GATE
+ var/datum/gas_mixture/coolant_output = COOLANT_OUTPUT_GATE
+
+ //Firstly, heat up the reactor based off of K.
+ var/input_moles = coolant_input.total_moles() //Firstly. Do we have enough moles of coolant?
+ if(input_moles >= minimum_coolant_level)
+ last_coolant_temperature = KELVIN_TO_CELSIUS(coolant_input.return_temperature())
+ //Important thing to remember, once you slot in the fuel rods, this thing will not stop making heat, at least, not unless you can live to be thousands of years old which is when the spent fuel finally depletes fully.
+ var/heat_delta = (KELVIN_TO_CELSIUS(coolant_input.return_temperature()) / 100) * gas_absorption_effectiveness //Take in the gas as a cooled input, cool the reactor a bit. The optimum, 100% balanced reaction sits at K=1, coolant input temp of 200K / -73 celsius.
+ last_heat_delta = heat_delta
+ temperature += heat_delta
+ coolant_output.merge(coolant_input) //And now, shove the input into the output.
+ coolant_input.clear() //Clear out anything left in the input gate.
+ color = null
+ no_coolant_ticks = max(0, no_coolant_ticks-2) //Needs half as much time to recover the ticks than to acquire them
+ else
+ if(has_fuel())
+ no_coolant_ticks++
+ if(no_coolant_ticks > RBMK_NO_COOLANT_TOLERANCE)
+ temperature += temperature / 500 //This isn't really harmful early game, but when your reactor is up to full power, this can get out of hand quite quickly.
+ vessel_integrity -= temperature / 200 //Think fast chucklenuts!
+ take_damage(10) //Just for the sound effect, to let you know you've fucked up.
+ color = "[COLOR_RED]"
+ investigate_log("Reactor taking damage from the lack of coolant", INVESTIGATE_SINGULO)
+ //Now, heat up the output and set our pressure.
+ coolant_output.set_temperature(CELSIUS_TO_KELVIN(temperature)) //Heat the coolant output gas that we just had pass through us.
+ last_output_temperature = KELVIN_TO_CELSIUS(coolant_output.return_temperature())
+ pressure = KPA_TO_PSI(coolant_output.return_pressure())
+ power = (temperature / RBMK_TEMPERATURE_CRITICAL) * 100
+ var/radioactivity_spice_multiplier = 1 //Some gasses make the reactor a bit spicy.
+ var/depletion_modifier = 0.035 //How rapidly do your rods decay
+ gas_absorption_effectiveness = gas_absorption_constant
+ //Next up, handle moderators!
+ if(moderator_input.total_moles() >= minimum_coolant_level)
+ var/total_fuel_moles = moderator_input.get_moles(GAS_PLASMA) + (moderator_input.get_moles(GAS_TRITIUM)*10) //Constricted plasma is 50% more efficient as fuel than plasma, but is harder to produce
+ var/power_modifier = max((moderator_input.get_moles(GAS_O2) / moderator_input.total_moles() * 10), 1) //You can never have negative IPM. For now.
+ if(total_fuel_moles >= minimum_coolant_level) //You at least need SOME fuel.
+ var/power_produced = max((total_fuel_moles / moderator_input.total_moles() * 10), 1)
+ last_power_produced = max(0,((power_produced*power_modifier)*moderator_input.total_moles()))
+ last_power_produced *= (power/100) //Aaaand here comes the cap. Hotter reactor => more power.
+ last_power_produced *= base_power_modifier //Finally, we turn it into actual usable numbers.
+ radioactivity_spice_multiplier += moderator_input.get_moles(GAS_TRITIUM) / 5 //Chernobyl 2.
+ var/turf/T = get_turf(src)
+ if(power >= 20)
+ coolant_output.adjust_moles(GAS_NITRYL, total_fuel_moles/50) //Shove out nitryl into the air when it's fuelled. You need to filter this off, or you're gonna have a bad time.
+ var/obj/structure/cable/C = T.get_cable_node()
+ if(!C?.powernet)
+ //message_admins("No cable or cable has no powernet!")
+ return
+ else
+ C.powernet.newavail += last_power_produced //hacky wtf
+ //message_admins("Adding [last_power_produced] to powernet.")
+ add_avail(last_power_produced)
+ var/total_control_moles = moderator_input.get_moles(GAS_N2) + (moderator_input.get_moles(GAS_CO2)*2) + (moderator_input.get_moles(GAS_PLUOXIUM)*3) //N2 helps you control the reaction at the cost of making it absolutely blast you with rads. Pluoxium has the same effect but without the rads!
+ if(total_control_moles >= minimum_coolant_level)
+ var/control_bonus = total_control_moles / 250 //1 mol of n2 -> 0.002 bonus control rod effectiveness, if you want a super controlled reaction, you'll have to sacrifice some power.
+ control_rod_effectiveness = initial(control_rod_effectiveness) + control_bonus
+ radioactivity_spice_multiplier += moderator_input.get_moles(GAS_N2) / 25 //An example setup of 50 moles of n2 (for dealing with spent fuel) leaves us with a radioactivity spice multiplier of 3.
+ radioactivity_spice_multiplier += moderator_input.get_moles(GAS_CO2) / 12.5
+ var/total_permeability_moles = moderator_input.get_moles(GAS_BZ) + (moderator_input.get_moles(GAS_H2O)*2) + (moderator_input.get_moles(GAS_HYPERNOB)*10)
+ if(total_permeability_moles >= minimum_coolant_level)
+ var/permeability_bonus = total_permeability_moles / 500
+ gas_absorption_effectiveness = gas_absorption_constant + permeability_bonus
+ var/total_degradation_moles = moderator_input.get_moles(GAS_NITRYL) //Because it's quite hard to get.
+ if(total_degradation_moles >= minimum_coolant_level*0.5) //I'll be nice.
+ depletion_modifier += total_degradation_moles / 15 //Oops! All depletion. This causes your fuel rods to get SPICY.
+ playsound(src, pick('sound/machines/sm/accent/normal/1.ogg','sound/machines/sm/accent/normal/2.ogg','sound/machines/sm/accent/normal/3.ogg','sound/machines/sm/accent/normal/4.ogg','sound/machines/sm/accent/normal/5.ogg'), 100, TRUE)
+ //From this point onwards, we clear out the remaining gasses.
+ moderator_input.clear() //Woosh. And the soul is gone.
+ K += total_fuel_moles / 1000
+ var/fuel_power = 0 //So that you can't magically generate K with your control rods.
+ if(!has_fuel()) //Reactor must be fuelled and ready to go before we can heat it up boys.
+ K = 0
+ else
+ for(var/obj/item/fuel_rod/FR in fuel_rods)
+ K += FR.fuel_power
+ fuel_power += FR.fuel_power
+ FR.deplete(depletion_modifier)
+ //Firstly, find the difference between the two numbers.
+ var/difference = abs(K - desired_k)
+ //Then, hit as much of that goal with our cooling per tick as we possibly can.
+ difference = clamp(difference, 0, control_rod_effectiveness) //And we can't instantly zap the K to what we want, so let's zap as much of it as we can manage....
+ if(difference > fuel_power && desired_k > K)
+ message_admins("Not enough fuel to get [difference]. We have fuel [fuel_power]")
+ investigate_log("Reactor has not enough fuel to get [difference]. We have fuel [fuel_power]", INVESTIGATE_SINGULO)
+ difference = fuel_power //Again, to stop you being able to run off of 1 fuel rod.
+ if(K != desired_k)
+ if(desired_k > K)
+ K += difference
+ else if(desired_k < K)
+ K -= difference
+ if(K == desired_k && last_user && current_desired_k != desired_k)
+ current_desired_k = desired_k
+ message_admins("Reactor desired criticality set to [desired_k] by [ADMIN_LOOKUPFLW(last_user)] in [ADMIN_VERBOSEJMP(src)]")
+ investigate_log("reactor desired criticality set to [desired_k] by [key_name(last_user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
+
+ K = clamp(K, 0, RBMK_MAX_CRITICALITY)
+ if(has_fuel())
+ temperature += K
+ else
+ temperature -= 10 //Nothing to heat us up, so.
+ handle_alerts() //Let's check if they're about to die, and let them know.
+ update_icon()
+ radiation_pulse(src, temperature*radioactivity_spice_multiplier)
+ if(power >= 93 && world.time >= next_flicker) //You're overloading the reactor. Give a more subtle warning that power is getting out of control.
+ next_flicker = world.time + 2 MINUTES
+ for(var/obj/machinery/light/L in GLOB.machines)
+ if(prob(25) && L.z == z) //If youre running the reactor cold though, no need to flicker the lights.
+ L.flicker()
+ investigate_log("Reactor overloading at [power]% power", INVESTIGATE_SINGULO)
+ for(var/atom/movable/I in get_turf(src))
+ if(isliving(I))
+ var/mob/living/L = I
+ if(temperature > 0)
+ L.adjust_bodytemperature(clamp(temperature, BODYTEMP_COOLING_MAX, BODYTEMP_HEATING_MAX)) //If you're on fire, you heat up!
+ if(istype(I, /obj/item/reagent_containers/food) && !istype(I, /obj/item/reagent_containers/food/drinks))
+ playsound(src, pick('sound/machines/fryer/deep_fryer_1.ogg', 'sound/machines/fryer/deep_fryer_2.ogg'), 100, TRUE)
+ var/obj/item/reagent_containers/food/grilled_item = I
+ if(prob(80))
+ return //To give the illusion that it's actually cooking omegalul.
+ switch(power)
+ if(20 to 39)
+ grilled_item.name = "grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "[initial(I.desc)] It's been grilled over a nuclear reactor."
+ if(!(grilled_item.foodtype & FRIED))
+ grilled_item.foodtype |= FRIED
+ if(40 to 70)
+ grilled_item.name = "heavily grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "[initial(I.desc)] It's been heavily grilled through the magic of nuclear fission."
+ if(!(grilled_item.foodtype & FRIED))
+ grilled_item.foodtype |= FRIED
+ if(70 to 95)
+ grilled_item.name = "Three-Mile Nuclear-Grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "A [initial(grilled_item.name)]. It's been put on top of a nuclear reactor running at extreme power by some badass engineer."
+ if(!(grilled_item.foodtype & FRIED))
+ grilled_item.foodtype |= FRIED
+ if(95 to INFINITY)
+ grilled_item.name = "Ultimate Meltdown Grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "A [initial(grilled_item.name)]. A grill this perfect is a rare technique only known by a few engineers who know how to perform a 'controlled' meltdown whilst also having the time to throw food on a reactor. I'll bet it tastes amazing."
+ if(!(grilled_item.foodtype & FRIED))
+ grilled_item.foodtype |= FRIED
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/relay(var/sound, var/message=null, loop = FALSE, channel = null) //Sends a sound + text message to the crew of a ship
+ for(var/mob/M in GLOB.player_list)
+ if(M.z == z)
+ if(!isinspace(M))
+ if(sound)
+ if(channel) //Doing this forbids overlapping of sounds
+ SEND_SOUND(M, sound(sound, repeat = loop, wait = 0, volume = 70, channel = channel))
+ else
+ SEND_SOUND(M, sound(sound, repeat = loop, wait = 0, volume = 70))
+ if(message)
+ to_chat(M, message)
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/stop_relay(channel) //Stops all playing sounds for crewmen on N channel.
+ for(var/mob/M in GLOB.player_list)
+ if(M.z == z)
+ M.stop_sound_channel(channel)
+
+//Method to handle sound effects, reactor warnings, all that jazz.
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/handle_alerts()
+ var/alert = FALSE //If we have an alert condition, we'd best let people know.
+ if(K <= 0 && temperature <= 0)
+ shut_down()
+ //First alert condition: Overheat
+ if(temperature >= RBMK_TEMPERATURE_CRITICAL)
+ alert = TRUE
+ investigate_log("Reactor reaching critical temperature at [temperature] C with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ message_admins("Reactor reaching critical temperature at [ADMIN_VERBOSEJMP(src)]")
+ if(temperature >= RBMK_TEMPERATURE_MELTDOWN)
+ var/temp_damage = min(temperature/100, initial(vessel_integrity)/40) //40 seconds to meltdown from full integrity, worst-case. Bit less than blowout since it's harder to spike heat that much.
+ vessel_integrity -= temp_damage
+ if(vessel_integrity <= temp_damage)
+ investigate_log("Reactor melted down at [temperature] C with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ meltdown() //Oops! All meltdown
+ return
+ else
+ alert = FALSE
+ if(temperature < -200) //That's as cold as I'm letting you get it, engineering.
+ color = COLOR_CYAN
+ temperature = -200
+ else
+ color = null
+ //Second alert condition: Overpressurized (the more lethal one)
+ if(pressure >= RBMK_PRESSURE_CRITICAL)
+ alert = TRUE
+ investigate_log("Reactor reaching critical pressure at [pressure] PSI with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ message_admins("Reactor reaching critical pressure at [ADMIN_VERBOSEJMP(src)]")
+ shake_animation(0.5)
+ playsound(loc, 'sound/machines/clockcult/steam_whoosh.ogg', 100, TRUE)
+ var/turf/T = get_turf(src)
+ T.atmos_spawn_air("water_vapor=[pressure/100];TEMP=[CELSIUS_TO_KELVIN(temperature)]")
+ var/pressure_damage = min(pressure/100, initial(vessel_integrity)/45) //You get 45 seconds (if you had full integrity), worst-case. But hey, at least it can't be instantly nuked with a pipe-fire.. though it's still very difficult to save.
+ vessel_integrity -= pressure_damage
+ if(vessel_integrity <= pressure_damage) //It wouldn't
+ investigate_log("Reactor blowout at [pressure] PSI with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ blowout()
+ return
+ if(warning)
+ if(!alert) //Congrats! You stopped the meltdown / blowout.
+ stop_relay(CHANNEL_REACTOR_ALERT)
+ warning = FALSE
+ set_light(0)
+ light_color = LIGHT_COLOR_CYAN
+ set_light(10)
+ else
+ if(!alert)
+ return
+ if(world.time < next_warning)
+ return
+ next_warning = world.time + 30 SECONDS //To avoid engis pissing people off when reaaaally trying to stop the meltdown or whatever.
+ warning = TRUE //Start warning the crew of the imminent danger.
+ relay('sound/effects/rbmk/alarm.ogg', null, loop=TRUE, channel = CHANNEL_REACTOR_ALERT)
+ set_light(0)
+ light_color = LIGHT_COLOR_RED
+ set_light(10)
+
+//Failure condition 1: Meltdown. Achieved by having heat go over tolerances. This is less devastating because it's easier to achieve.
+//Results: Engineering becomes unusable and your engine irreparable
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/meltdown()
+ set waitfor = FALSE
+ SSair.atmos_machinery -= src //Annd we're now just a useless brick.
+ slagged = TRUE
+ color = null
+ update_icon()
+ STOP_PROCESSING(SSmachines, src)
+ icon_state = "reactor_slagged"
+ AddComponent(/datum/component/radioactive, 15000 , src, 0)
+ var/obj/effect/landmark/nuclear_waste_spawner/NSW = new /obj/effect/landmark/nuclear_waste_spawner/strong(get_turf(src))
+ relay('sound/effects/rbmk/meltdown.ogg', "You hear a horrible metallic hissing.")
+ stop_relay(CHANNEL_REACTOR_ALERT)
+ NSW.fire() //This will take out engineering for a decent amount of time as they have to clean up the sludge.
+ for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
+ if((apc.z == z) && prob(70))
+ apc.overload_lighting()
+ var/datum/gas_mixture/coolant_input = COOLANT_INPUT_GATE
+ var/datum/gas_mixture/moderator_input = MODERATOR_INPUT_GATE
+ var/datum/gas_mixture/coolant_output = COOLANT_OUTPUT_GATE
+ var/turf/T = get_turf(src)
+ coolant_input.set_temperature(CELSIUS_TO_KELVIN(temperature)*2)
+ moderator_input.set_temperature(CELSIUS_TO_KELVIN(temperature)*2)
+ coolant_output.set_temperature(CELSIUS_TO_KELVIN(temperature)*2)
+ T.assume_air(coolant_input)
+ T.assume_air(moderator_input)
+ T.assume_air(coolant_output)
+ power = 0 //we set it to zero so the reactor can be serviced and repaired.
+ explosion(get_turf(src), 0, 5, 10, 20, TRUE, TRUE)
+ empulse(get_turf(src), 25, 15)
+ QDEL_NULL(soundloop)
+
+//Failure condition 2: Blowout. Achieved by reactor going over-pressured. This is a round-ender because it requires more fuckery to achieve.
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/blowout()
+ explosion(get_turf(src), GLOB.MAX_EX_DEVESTATION_RANGE, GLOB.MAX_EX_HEAVY_RANGE, GLOB.MAX_EX_LIGHT_RANGE, GLOB.MAX_EX_FLASH_RANGE)
+ meltdown() //Double kill.
+ power = 0 //we set it to zero so the reactor can be serviced and repaired.
+ SSweather.run_weather("nuclear fallout")
+ for(var/X in GLOB.landmarks_list)
+ if(istype(X, /obj/effect/landmark/nuclear_waste_spawner))
+ var/obj/effect/landmark/nuclear_waste_spawner/WS = X
+ if(is_station_level(WS.z)) //Begin the SLUDGING
+ WS.fire()
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/update_icon()
+ icon_state = "reactor_off"
+ switch(temperature)
+ if(0 to 200)
+ icon_state = "reactor_on"
+ if(200 to RBMK_TEMPERATURE_OPERATING)
+ icon_state = "reactor_hot"
+ if(RBMK_TEMPERATURE_OPERATING to 750)
+ icon_state = "reactor_veryhot"
+ if(750 to RBMK_TEMPERATURE_CRITICAL) //Point of no return.
+ icon_state = "reactor_overheat"
+ if(RBMK_TEMPERATURE_CRITICAL to INFINITY)
+ icon_state = "reactor_meltdown"
+ if(!has_fuel())
+ icon_state = "reactor_off"
+ if(slagged)
+ icon_state = "reactor_slagged"
+
+
+//Startup, shutdown
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/start_up()
+ START_PROCESSING(SSmachines, src)
+ desired_k = 1
+ set_light(10)
+ var/startup_sound = pick('sound/effects/ship/reactor/startup.ogg', 'sound/effects/ship/reactor/startup2.ogg')
+ playsound(loc, startup_sound, 100)
+ soundloop = new(src, TRUE)
+ if(!powernet)
+ message_admins("No powernet for the Nuclear Reactor! Trying to add.")
+ connect_to_network()
+ if(!powernet)
+ message_admins("Powernet add fail. This reactor will never produce power.")
+
+//Shuts off the fuel rods, ambience, etc. Keep in mind that your temperature may still go up!
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/shut_down()
+ STOP_PROCESSING(SSmachines, src)
+ set_light(0)
+ K = 0
+ desired_k = 0
+ temperature = 0
+ update_icon()
+ QDEL_NULL(soundloop)
+
+/obj/item/fuel_rod/Initialize()
+ .=..()
+ AddComponent(/datum/component/radioactive, 350 , src)
+
+//Controlling the reactor.
+
+/obj/machinery/computer/reactor
+ name = "Reactor control console"
+ desc = "Scream"
+ light_color = "#55BA55"
+ light_power = 1
+ light_range = 3
+ icon = 'icons/obj/rbmk_computer.dmi'
+ icon_state = "oldcomp"
+ icon_screen = "library"
+ icon_keyboard = null
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/reactor = null
+ var/id = "default_reactor_for_lazy_mappers"
+
+/obj/machinery/computer/reactor/Initialize(mapload, obj/item/circuitboard/C)
+ . = ..()
+ addtimer(CALLBACK(src, .proc/link_to_reactor), 10 SECONDS)
+
+/obj/machinery/computer/reactor/wrench_act(mob/living/user, obj/item/I)
+ to_chat(user, "You start [anchored ? "un" : ""]securing [name]...")
+ if(I.use_tool(src, user, 40, volume=75))
+ to_chat(user, "You [anchored ? "un" : ""]secure [name].")
+ setAnchored(!anchored)
+ return TRUE
+ return FALSE
+
+/obj/machinery/computer/reactor/proc/link_to_reactor()
+ for(var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/asdf in GLOB.machines)
+ if(asdf.id && asdf.id == id)
+ reactor = asdf
+ return TRUE
+ return FALSE
+
+#define FREQ_RBMK_CONTROL 1439.69
+
+/obj/machinery/computer/reactor/control_rods
+ name = "Control rod management computer"
+ desc = "A computer which can remotely raise / lower the control rods of a reactor."
+ icon_screen = "rbmk_rods"
+
+/obj/machinery/computer/reactor/control_rods/attack_hand(mob/living/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/machinery/computer/reactor/control_rods/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "RbmkControlRods")
+ ui.open()
+ ui.set_autoupdate(TRUE)
+
+/obj/machinery/computer/reactor/control_rods/ui_act(action, params)
+ if(..())
+ return
+ if(!reactor)
+ return
+ if(action == "input")
+ var/input = text2num(params["target"])
+ reactor.last_user = usr
+ reactor.desired_k = clamp(input, 0, 3)
+
+/obj/machinery/computer/reactor/control_rods/ui_data(mob/user)
+ var/list/data = list()
+ data["control_rods"] = 0
+ data["k"] = 0
+ data["desiredK"] = 0
+ if(reactor)
+ data["k"] = reactor.K
+ data["desiredK"] = reactor.desired_k
+ data["control_rods"] = 100 - (reactor.desired_k / 3 * 100) //Rod insertion is extrapolated as a function of the percentage of K
+ return data
+
+/obj/machinery/computer/reactor/stats
+ name = "Reactor Statistics Console"
+ desc = "A console for monitoring the statistics of a nuclear reactor."
+ icon_screen = "rbmk_stats"
+ var/next_stat_interval = 0
+ var/list/psiData = list()
+ var/list/powerData = list()
+ var/list/tempInputData = list()
+ var/list/tempOutputdata = list()
+
+/obj/machinery/computer/reactor/stats/attack_hand(mob/living/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/machinery/computer/reactor/stats/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "RbmkStats")
+ ui.open()
+ ui.set_autoupdate(TRUE)
+
+/obj/machinery/computer/reactor/stats/process()
+ if(world.time >= next_stat_interval)
+ next_stat_interval = world.time + 1 SECONDS //You only get a slow tick.
+ psiData += (reactor) ? reactor.pressure : 0
+ if(psiData.len > 100) //Only lets you track over a certain timeframe.
+ psiData.Cut(1, 2)
+ powerData += (reactor) ? reactor.power*10 : 0 //We scale up the figure for a consistent:tm: scale
+ if(powerData.len > 100) //Only lets you track over a certain timeframe.
+ powerData.Cut(1, 2)
+ tempInputData += (reactor) ? reactor.last_coolant_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempInputData.len > 100) //Only lets you track over a certain timeframe.
+ tempInputData.Cut(1, 2)
+ tempOutputdata += (reactor) ? reactor.last_output_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempOutputdata.len > 100) //Only lets you track over a certain timeframe.
+ tempOutputdata.Cut(1, 2)
+
+/obj/machinery/computer/reactor/stats/ui_data(mob/user)
+ var/list/data = list()
+ data["powerData"] = powerData
+ data["psiData"] = psiData
+ data["tempInputData"] = tempInputData
+ data["tempOutputdata"] = tempOutputdata
+ data["coolantInput"] = reactor ? reactor.last_coolant_temperature : 0
+ data["coolantOutput"] = reactor ? reactor.last_output_temperature : 0
+ data["power"] = reactor ? reactor.power : 0
+ data ["psi"] = reactor ? reactor.pressure : 0
+ return data
+
+/obj/machinery/computer/reactor/fuel_rods
+ name = "Reactor Fuel Management Console"
+ desc = "A console which can remotely raise fuel rods out of nuclear reactors."
+ icon_screen = "rbmk_fuel"
+
+/obj/machinery/computer/reactor/fuel_rods/attack_hand(mob/living/user)
+ . = ..()
+ if(!reactor)
+ return FALSE
+ if(reactor.power > 20)
+ to_chat(user, "You cannot remove fuel from [reactor] when it is above 20% power.")
+ return FALSE
+ if(!reactor.fuel_rods.len)
+ to_chat(user, "[reactor] does not have any fuel rods loaded.")
+ return FALSE
+ var/atom/movable/fuel_rod = input(usr, "Select a fuel rod to remove", "[src]", null) as null|anything in reactor.fuel_rods
+ if(!fuel_rod)
+ return
+ playsound(src, pick('sound/effects/rbmk/switch.ogg','sound/effects/rbmk/switch2.ogg','sound/effects/rbmk/switch3.ogg'), 100, FALSE)
+ playsound(reactor, 'sound/effects/ship/freespace2/crane_1.wav', 100, FALSE)
+ fuel_rod.forceMove(get_turf(reactor))
+ reactor.fuel_rods -= fuel_rod
+
+//Preset pumps for mappers. You can also set the id tags yourself.
+/obj/machinery/atmospherics/components/binary/pump/rbmk_input
+ id = "rbmk_input"
+ frequency = FREQ_RBMK_CONTROL
+
+/obj/machinery/atmospherics/components/binary/pump/rbmk_output
+ id = "rbmk_output"
+ frequency = FREQ_RBMK_CONTROL
+
+/obj/machinery/atmospherics/components/binary/pump/rbmk_moderator
+ id = "rbmk_moderator"
+ frequency = FREQ_RBMK_CONTROL
+
+/obj/machinery/computer/reactor/pump
+ name = "Reactor inlet valve computer"
+ desc = "A computer which controls valve settings on an advanced gas cooled reactor. Alt click it to remotely set pump pressure."
+ icon_screen = "rbmk_input"
+ id = "rbmk_input"
+ var/datum/radio_frequency/radio_connection
+ var/on = FALSE
+
+/obj/machinery/computer/reactor/pump/AltClick(mob/user)
+ . = ..()
+ var/newPressure = input(user, "Set new output pressure (kPa)", "Remote pump control", null) as num
+ if(!newPressure)
+ return
+ newPressure = clamp(newPressure, 0, MAX_OUTPUT_PRESSURE) //Number sanitization is not handled in the pumps themselves, only during their ui_act which this doesn't use.
+ signal(on, newPressure)
+
+/obj/machinery/computer/reactor/attack_robot(mob/user)
+ . = ..()
+ attack_hand(user)
+
+/obj/machinery/computer/reactor/attack_ai(mob/user)
+ . = ..()
+ attack_hand(user)
+
+/obj/machinery/computer/reactor/pump/attack_hand(mob/living/user)
+ . = ..()
+ if(!is_operational())
+ return FALSE
+ playsound(loc, pick('sound/effects/rbmk/switch.ogg','sound/effects/rbmk/switch2.ogg','sound/effects/rbmk/switch3.ogg'), 100, FALSE)
+ visible_message("[src]'s switch flips [on ? "off" : "on"].")
+ on = !on
+ signal(on)
+
+/obj/machinery/computer/reactor/pump/Initialize(mapload, obj/item/circuitboard/C)
+ . = ..()
+ radio_connection = SSradio.add_object(src, FREQ_RBMK_CONTROL,filter=RADIO_ATMOSIA)
+
+/obj/machinery/computer/reactor/pump/proc/signal(power, set_output_pressure=null)
+ var/datum/signal/signal
+ if(!set_output_pressure) //Yes this is stupid, but technically if you pass through "set_output_pressure" onto the signal, it'll always try and set its output pressure and yeahhh...
+ signal = new(list(
+ "tag" = id,
+ "frequency" = FREQ_RBMK_CONTROL,
+ "timestamp" = world.time,
+ "power" = power,
+ "sigtype" = "command"
+ ))
+ else
+ signal = new(list(
+ "tag" = id,
+ "frequency" = FREQ_RBMK_CONTROL,
+ "timestamp" = world.time,
+ "power" = power,
+ "set_output_pressure" = set_output_pressure,
+ "sigtype" = "command"
+ ))
+ radio_connection.post_signal(src, signal, filter=RADIO_ATMOSIA)
+
+//Preset subtypes for mappers
+/obj/machinery/computer/reactor/pump/rbmk_input
+ name = "Reactor inlet valve computer"
+ icon_screen = "rbmk_input"
+ id = "rbmk_input"
+
+/obj/machinery/computer/reactor/pump/rbmk_output
+ name = "Reactor output valve computer"
+ icon_screen = "rbmk_output"
+ id = "rbmk_output"
+
+/obj/machinery/computer/reactor/pump/rbmk_moderator
+ name = "Reactor moderator valve computer"
+ icon_screen = "rbmk_moderator"
+ id = "rbmk_moderator"
+
+//Monitoring program.
+/datum/computer_file/program/nuclear_monitor
+ filename = "rbmkmonitor"
+ filedesc = "Nuclear Reactor Monitoring"
+ ui_header = "smmon_0.gif"
+ program_icon_state = "smmon_0"
+ extended_desc = "This program connects to specially calibrated sensors to provide information on the status of nuclear reactors."
+ requires_ntnet = TRUE
+ transfer_access = ACCESS_CONSTRUCTION
+ //network_destination = "rbmk monitoring system" //Apparently we don't use these anymore
+ size = 2
+ tgui_id = "NtosRbmkStats"
+ //ui_x = 350
+ //ui_y = 550
+ var/active = TRUE //Easy process throttle
+ var/next_stat_interval = 0
+ var/list/psiData = list()
+ var/list/powerData = list()
+ var/list/tempInputData = list()
+ var/list/tempOutputdata = list()
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/reactor //Our reactor.
+
+/datum/computer_file/program/nuclear_monitor/process_tick()
+ ..()
+ if(!reactor || !active)
+ return FALSE
+ var/stage = 0
+ //This is dirty but i'm lazy wahoo!
+ if(reactor.power > 0)
+ stage = 1
+ if(reactor.power >= 40)
+ stage = 2
+ if(reactor.temperature >= RBMK_TEMPERATURE_OPERATING)
+ stage = 3
+ if(reactor.temperature >= RBMK_TEMPERATURE_CRITICAL)
+ stage = 4
+ if(reactor.temperature >= RBMK_TEMPERATURE_MELTDOWN)
+ stage = 5
+ if(reactor.vessel_integrity <= 100) //Bye bye! GET OUT!
+ stage = 6
+ ui_header = "smmon_[stage].gif"
+ program_icon_state = "smmon_[stage]"
+ if(istype(computer))
+ computer.update_icon()
+ if(world.time >= next_stat_interval)
+ next_stat_interval = world.time + 1 SECONDS //You only get a slow tick.
+ psiData += (reactor) ? reactor.pressure : 0
+ if(psiData.len > 100) //Only lets you track over a certain timeframe.
+ psiData.Cut(1, 2)
+ powerData += (reactor) ? reactor.power*10 : 0 //We scale up the figure for a consistent:tm: scale
+ if(powerData.len > 100) //Only lets you track over a certain timeframe.
+ powerData.Cut(1, 2)
+ tempInputData += (reactor) ? reactor.last_coolant_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempInputData.len > 100) //Only lets you track over a certain timeframe.
+ tempInputData.Cut(1, 2)
+ tempOutputdata += (reactor) ? reactor.last_output_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempOutputdata.len > 100) //Only lets you track over a certain timeframe.
+ tempOutputdata.Cut(1, 2)
+
+/datum/computer_file/program/nuclear_monitor/run_program(mob/living/user)
+ . = ..(user)
+ //No reactor? Go find one then.
+ if(!reactor)
+ for(var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/R in GLOB.machines)
+ if(R.z == usr.z)
+ reactor = R
+ break
+ active = TRUE
+
+/datum/computer_file/program/nuclear_monitor/kill_program(forced = FALSE)
+ active = FALSE
+ ..()
+
+/datum/computer_file/program/nuclear_monitor/ui_data()
+ var/list/data = get_header_data()
+ data["powerData"] = powerData
+ data["psiData"] = psiData
+ data["tempInputData"] = tempInputData
+ data["tempOutputdata"] = tempOutputdata
+ data["coolantInput"] = reactor ? reactor.last_coolant_temperature : 0
+ data["coolantOutput"] = reactor ? reactor.last_output_temperature : 0
+ data["power"] = reactor ? reactor.power : 0
+ data ["psi"] = reactor ? reactor.pressure : 0
+ return data
+
+/datum/computer_file/program/nuclear_monitor/ui_act(action, params)
+ if(..())
+ return TRUE
+
+ switch(action)
+ if("swap_reactor")
+ var/list/choices = list()
+ for(var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/R in GLOB.machines)
+ if(R.z != usr.z)
+ continue
+ choices += R
+ reactor = input(usr, "What reactor do you wish to monitor?", "[src]", null) as null|anything in choices
+ powerData = list()
+ psiData = list()
+ tempInputData = list()
+ tempOutputdata = list()
+ return TRUE
+
+/obj/effect/decal/nuclear_waste
+ name = "Plutonium sludge"
+ desc = "A writhing pool of heavily irradiated, spent reactor fuel. You probably shouldn't step through this..."
+ icon = 'icons/obj/machines/reactor_parts.dmi'
+ icon_state = "nuclearwaste"
+ alpha = 150
+ light_color = LIGHT_COLOR_CYAN
+ color = "#ff9eff"
+
+/obj/effect/decal/nuclear_waste/Initialize()
+ . = ..()
+ for(var/obj/A in get_turf(src))
+ if(istype(A, /obj/structure))
+ qdel(src) //It is more processing efficient to do this here rather than when searching for available turfs.
+ set_light(3)
+ AddComponent(/datum/component/radioactive, 1000, src, 0)
+
+/obj/effect/decal/nuclear_waste/epicenter //The one that actually does the irradiating. This is to avoid every bit of sludge PROCESSING
+ name = "Dense nuclear sludge"
+
+/obj/effect/landmark/nuclear_waste_spawner //Clean way of spawning nuclear gunk after a reactor core meltdown.
+ name = "Nuclear waste spawner"
+ var/range = 5 //5 tile radius to spawn goop
+
+/obj/effect/landmark/nuclear_waste_spawner/strong
+ range = 10
+
+/obj/effect/landmark/nuclear_waste_spawner/proc/fire()
+ playsound(loc, 'sound/effects/gib_step.ogg', 100)
+ new /obj/effect/decal/nuclear_waste/epicenter(get_turf(src))
+ for(var/turf/open/floor in orange(range, get_turf(src)))
+ if(prob(35)) //Scatter the sludge, don't smear it everywhere
+ new /obj/effect/decal/nuclear_waste (floor)
+ qdel(src)
+
+/obj/effect/decal/nuclear_waste/epicenter/Initialize()
+ . = ..()
+
+/obj/effect/decal/nuclear_waste/Crossed(atom/movable/AM)
+ . = ..()
+ if(isliving(AM))
+ var/mob/living/L = AM
+ playsound(loc, 'sound/effects/gib_step.ogg', HAS_TRAIT(L, TRAIT_LIGHT_STEP) ? 20 : 50, 1)
+ radiation_pulse(src, 500, 5) //MORE RADS
+
+/obj/effect/decal/nuclear_waste/attackby(obj/item/tool, mob/user)
+ if(tool.tool_behaviour == TOOL_SHOVEL)
+ radiation_pulse(src, 1000, 5) //MORE RADS
+ to_chat(user, "You start to clear [src]...")
+ if(tool.use_tool(src, user, 50, volume=100))
+ to_chat(user, "You clear [src]. ")
+ qdel(src)
+ return
+ . = ..()
+
+/datum/weather/nuclear_fallout
+ name = "nuclear fallout"
+ desc = "Irradiated dust falls down everywhere."
+ telegraph_duration = 50
+ telegraph_message = "The air suddenly becomes dusty.."
+ weather_message = "You feel a wave of hot ash fall down on you."
+ weather_overlay = "light_ash"
+ telegraph_overlay = "light_snow"
+ weather_duration_lower = 600
+ weather_duration_upper = 1500
+ weather_color = "green"
+ telegraph_sound = null
+ weather_sound = 'sound/effects/ship/reactor/falloutwind.ogg'
+ end_duration = 100
+ area_type = /area
+ protected_areas = list(/area/maintenance, /area/ai_monitored/turret_protected/ai_upload, /area/ai_monitored/turret_protected/ai_upload_foyer,
+ /area/ai_monitored/turret_protected/ai, /area/commons/storage/emergency/starboard, /area/commons/storage/emergency/port, /area/shuttle)
+ target_trait = ZTRAIT_STATION
+ end_message = "The ash stops falling."
+ immunity_type = "rad"
+
+/datum/weather/nuclear_fallout/weather_act(mob/living/L)
+ L.rad_act(100)
+
+/datum/weather/nuclear_fallout/telegraph()
+ ..()
+ status_alarm(TRUE)
+
+/datum/weather/nuclear_fallout/proc/status_alarm(active) //Makes the status displays show the radiation warning for those who missed the announcement.
+ var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
+ if(!frequency)
+ return
+
+ var/datum/signal/signal = new
+ if (active)
+ signal.data["command"] = "alert"
+ signal.data["picture_state"] = "radiation"
+ else
+ signal.data["command"] = "shuttle"
+
+ var/atom/movable/virtualspeaker/virt = new(null)
+ frequency.post_signal(virt, signal)
+
+/datum/weather/nuclear_fallout/end()
+ if(..())
+ return
+ status_alarm(FALSE)
+
+/obj/item/sealant
+ name = "Flexi seal"
+ desc = "A neat spray can that can repair torn inflatable segments, and more!"
+ icon = 'icons/obj/inflatable.dmi'
+ icon_state = "sealant"
+ w_class = WEIGHT_CLASS_TINY
+
+/area/engineering/main/reactor_core
+ name = "Nuclear Reactor Core"
+
+/area/engineering/main/reactor_control
+ name = "Reactor Control Room"
+
+
+//Procs shamelessly taken from machinery/power
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/connect_to_network()
+ var/turf/T = src.loc
+ if(!T || !istype(T))
+ return FALSE
+
+ var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked
+ if(!C || !C.powernet)
+ return FALSE
+
+ C.powernet.add_machine(src)
+ return TRUE
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/disconnect_from_network()
+ if(!powernet)
+ return FALSE
+ powernet.remove_machine(src)
+ return TRUE
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/add_avail(amount)
+ if(powernet)
+ powernet.newavail += amount
+ return TRUE
+ else
+ return FALSE
diff --git a/code/modules/power/reactor/reactor_cargo.dm b/code/modules/power/reactor/reactor_cargo.dm
new file mode 100644
index 0000000000..5548e88f8d
--- /dev/null
+++ b/code/modules/power/reactor/reactor_cargo.dm
@@ -0,0 +1,37 @@
+// modular shitcode but it works:tm:
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/multitool_act(mob/living/user, obj/item/multitool/I)
+ if(istype(I))
+ to_chat(user, "You add \the [src]'s ID into the multitool's buffer.")
+ I.buffer = src.id
+ return TRUE
+/obj/machinery/computer/reactor/multitool_act(mob/living/user, obj/item/multitool/I)
+ if(istype(I))
+ to_chat(user, "You add the reactor's ID to \the [src]>")
+ src.id = I.buffer
+ link_to_reactor()
+ return TRUE
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo // easier on the brain
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo/New()
+ . = ..()
+ id = rand(1, 9999999) // cmon, what are the chances? The chances are... Very low friend... But maybe we can make this a bit better.
+
+// Cargo variants can be wrenched down and don't start linked to the default RMBK reactor
+
+/obj/machinery/computer/reactor/control_rods/cargo
+ anchored = FALSE
+ id = null
+
+/obj/machinery/computer/reactor/stats/cargo
+ anchored = FALSE
+ id = null
+
+/obj/machinery/computer/reactor/fuel_rods/cargo
+ anchored = FALSE
+ id = null
+
+/obj/item/paper/fluff/rbmkcargo
+ name = "Nuclear Reactor Instructions"
+ info = "Make sure a 5x5 area is completely clear of pipes, cables and machinery when using the beacon. Those will be provided automatically with the beacon's bluespace decompression. Use a multitool on the reactor then on the computers provided to link them together. Also make sure the reactor has a proper pipeline filled with cooling gas before inserting fuel rods. Good luck!"
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 9d887a8e1f..567ac00487 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -34,6 +34,7 @@ field_generator power level display
max_integrity = 500
//100% immune to lasers and energy projectiles since it absorbs their energy.
armor = list(MELEE = 25, BULLET = 10, LASER = 100, ENERGY = 100, BOMB = 0, BIO = 0, RAD = 0, FIRE = 50, ACID = 70)
+ var/obj/item/radio/radio
var/const/num_power_levels = 6 // Total number of power level icon has
var/power_level = 0
var/active = FG_OFFLINE
@@ -58,6 +59,9 @@ field_generator power level display
. = ..()
fields = list()
connected_gens = list()
+ radio = new(src)
+ radio.listening = 0
+ radio.recalculateChannels()
/obj/machinery/field/generator/ComponentInitialize()
. = ..()
@@ -166,6 +170,7 @@ field_generator power level display
/obj/machinery/field/generator/Destroy()
cleanup()
+ QDEL_NULL(radio)
return ..()
@@ -333,6 +338,7 @@ field_generator power level display
if((world.time - O.last_warning) > 50) //to stop message-spam
temp = 0
var/turf/T = get_turf(src)
+ radio.talk_into(src, "A containment field has failed in [get_area_name(src, TRUE)] while a singularity exists.", null, language = get_selected_language())
message_admins("A singulo exists and a containment field has failed at [ADMIN_VERBOSEJMP(T)].")
investigate_log("has failed whilst a singulo exists at [AREACOORD(T)].", INVESTIGATE_SINGULO)
O.last_warning = world.time
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 8490daa681..29fd68d034 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -415,17 +415,13 @@
/obj/singularity/proc/mezzer()
for(var/mob/living/carbon/M in oviewers(8, src))
- if(isbrain(M)) //Ignore brains
- continue
-
- if(M.stat == CONSCIOUS)
- if (ishuman(M))
- var/mob/living/carbon/human/H = M
- if(istype(H.glasses, /obj/item/clothing/glasses/meson))
- var/obj/item/clothing/glasses/meson/MS = H.glasses
- if(MS.vision_flags == SEE_TURFS)
- to_chat(H, "You look directly into the [src.name], good thing you had your protective eyewear on!")
- return
+ if(M.stat == CONSCIOUS && ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(istype(H.glasses, /obj/item/clothing/glasses/meson))
+ var/obj/item/clothing/glasses/meson/MS = H.glasses
+ if(MS.vision_flags == SEE_TURFS)
+ to_chat(H, "You look directly into the [src.name], good thing you had your protective eyewear on!")
+ return
M.apply_effect(60, EFFECT_STUN)
M.visible_message("[M] stares blankly at the [src.name]!", \
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 04dc8a0d26..948586046e 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -1686,14 +1686,14 @@ All effects don't start immediately, but rather get worse over time; the rate is
var/heal_points = 10
if(L.health <= 0)
heal_points = 20 //heal more if we're in softcrit
- for(var/i in 1 to min(volume, heal_points)) //only heals 1 point of damage per unit on add, for balance reasons
- L.adjustBruteLoss(-1)
- L.adjustFireLoss(-1)
- L.adjustToxLoss(-1)
- L.adjustOxyLoss(-1)
- L.adjustStaminaLoss(-1)
+ heal_points = min(volume, heal_points)
+ L.adjustBruteLoss(-heal_points)
+ L.adjustFireLoss(-heal_points)
+ L.adjustToxLoss(-heal_points)
+ L.adjustOxyLoss(-heal_points)
+ L.adjustStaminaLoss(-heal_points)
L.visible_message("[L] shivers with renewed vigor!", "One taste of [lowertext(name)] fills you with energy!")
- if(!L.stat && heal_points == 20) //brought us out of softcrit
+ if(!L.stat && L.health > 0) //brought us out of softcrit
L.visible_message("[L] lurches to [L.p_their()] feet!", "Up and at 'em, kid.")
/datum/reagent/consumable/ethanol/bastion_bourbon/on_mob_life(mob/living/L)
diff --git a/code/modules/research/designs/bluespace_designs.dm b/code/modules/research/designs/bluespace_designs.dm
index cf6aff5918..b16ffeb9de 100644
--- a/code/modules/research/designs/bluespace_designs.dm
+++ b/code/modules/research/designs/bluespace_designs.dm
@@ -115,3 +115,13 @@
build_path = /obj/item/pipe/bluespace
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+
+/datum/design/swapper //ported from TG, credit to XDTM
+ name = "Quantum Spin Inverter"
+ desc = "An experimental device that is able to swap the locations of two entities by switching their particles' spin values. Must be linked to another device to function."
+ id = "swapper"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron = 500, /datum/material/diamond = 1500, /datum/material/glass = 1000, /datum/material/bluespace = 2000, /datum/material/silver=1000)
+ build_path = /obj/item/swapper
+ category = list("Bluespace Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm
index cc02e271f9..9c7ca5b999 100644
--- a/code/modules/research/destructive_analyzer.dm
+++ b/code/modules/research/destructive_analyzer.dm
@@ -84,15 +84,7 @@ Note: Must be placed within 3 tiles of the R&D Console
reclaim_materials_from(thing)
for(var/mob/M in thing)
M.death()
- if(istype(thing, /obj/item/stack/sheet))
- var/obj/item/stack/sheet/S = thing
- if(S.amount > 1 && !innermode)
- S.amount--
- loaded_item = S
- else
- qdel(S)
- else
- qdel(thing)
+ qdel(thing)
if (!innermode)
update_icon()
return TRUE
diff --git a/code/modules/research/techweb/nodes/bluespace_nodes.dm b/code/modules/research/techweb/nodes/bluespace_nodes.dm
index 90b69eb28d..802f7854b1 100644
--- a/code/modules/research/techweb/nodes/bluespace_nodes.dm
+++ b/code/modules/research/techweb/nodes/bluespace_nodes.dm
@@ -61,7 +61,7 @@
display_name = "Bluespace Travel"
description = "Application of Bluespace for static teleportation technology."
prereq_ids = list("adv_power", "adv_bluespace")
- design_ids = list("tele_station", "tele_hub", "quantumpad", "quantum_keycard", "launchpad", "launchpad_console", "teleconsole", "roastingstick", "bluespace_pipe")
+ design_ids = list("tele_station", "tele_hub", "quantumpad", "quantum_keycard", "launchpad", "launchpad_console", "teleconsole", "roastingstick", "bluespace_pipe","swapper")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/unregulated_bluespace
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index b281132ce0..4ea1892007 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -15,6 +15,7 @@
var/list/accents = list() //done in order of priority (please always apply abductor accent and stuttering last)
var/static/list/languages_possible_base = typecacheof(list(
/datum/language/common,
+ /datum/language/machine,
/datum/language/draconic,
/datum/language/codespeak,
/datum/language/monkey,
@@ -142,7 +143,7 @@
icon_state = "tonguexeno"
say_mod = "hisses"
taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED
- maxHealth = 500 //They've a little mouth for a tongue, so it's pretty rhobust
+ maxHealth = 500 //They've a little mouth for a tongue, so it's pretty robust
initial_accents = list(/datum/accent/alien)
var/static/list/languages_possible_alien = typecacheof(list(
/datum/language/xenocommon,
@@ -155,6 +156,13 @@
. = ..()
languages_possible = languages_possible_alien
+/obj/item/organ/tongue/alien/hybrid
+ name = "xenohybrid tongue"
+
+/obj/item/organ/tongue/alien/hybrid/Initialize(mapload)
+ . = ..()
+ languages_possible = languages_possible_base
+
/obj/item/organ/tongue/bone
name = "bone \"tongue\""
desc = "Apparently skeletons alter the sounds they produce through oscillation of their teeth, hence their characteristic rattling."
@@ -248,6 +256,7 @@
taste_sensitivity = 101 // Not a tongue, they can't taste shit
var/static/list/languages_possible_ethereal = typecacheof(list(
/datum/language/common,
+ /datum/language/machine,
/datum/language/draconic,
/datum/language/codespeak,
/datum/language/monkey,
@@ -269,6 +278,7 @@
say_mod = "chitters"
var/static/list/languages_possible_arachnid = typecacheof(list(
/datum/language/common,
+ /datum/language/machine,
/datum/language/draconic,
/datum/language/codespeak,
/datum/language/monkey,
diff --git a/code/modules/uplink/uplink_items/uplink_devices.dm b/code/modules/uplink/uplink_items/uplink_devices.dm
index 44dcedac34..3a6aed2009 100644
--- a/code/modules/uplink/uplink_items/uplink_devices.dm
+++ b/code/modules/uplink/uplink_items/uplink_devices.dm
@@ -275,6 +275,18 @@
purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
restricted = TRUE
+//this seems like a terrible idea to me, so commenting it out for now. -Shell
+/*
+/datum/uplink_item/device_tools/tc_rod
+ name = "Telecrystal Fuel Rod"
+ desc = "This special fuel rod has eight material slots that can be inserted with telecrystals, \
+ once the rod has been fully depleted, you will be able to harvest the extra telecrystals. \
+ Please note: This Rod fissiles much faster than it's regular counterpart, it doesn't take \
+ much to overload the reactor with these..."
+ item = /obj/item/twohanded/required/fuel_rod/material/telecrystal
+ cost = 7
+*/
+
/* for now
/datum/uplink_item/device_tools/suspiciousphone
name = "Protocol CRAB-17 Phone"
diff --git a/config/entries/general.txt b/config/entries/general.txt
index 3688b8197b..ad8792c10a 100644
--- a/config/entries/general.txt
+++ b/config/entries/general.txt
@@ -470,6 +470,7 @@ BOX_RANDOM_ENGINE Box TEG,3
BOX_RANDOM_ENGINE Box Empty,0
BOX_RANDOM_ENGINE Box Antimatter,1
BOX_RANDOM_ENGINE Box P.A.C.M.A.N,1
+BOX_RANDOM_ENGINE Box RBMK,3
## Whether the suicide verb is allowed.
diff --git a/dependencies.sh b/dependencies.sh
index e0df827a2a..7943782996 100644
--- a/dependencies.sh
+++ b/dependencies.sh
@@ -21,7 +21,7 @@ export SPACEMAN_DMM_VERSION=suite-1.7
export PYTHON_VERSION=3.7.9
# Auxmos git tag
-export AUXMOS_VERSION=v2.1.0
+export AUXMOS_VERSION=v1.1.1
# Extools git tag
export EXTOOLS_VERSION=v0.0.7
diff --git a/goon/icons/obj/chairs.dmi b/goon/icons/obj/chairs.dmi
deleted file mode 100644
index 462370ac37..0000000000
Binary files a/goon/icons/obj/chairs.dmi and /dev/null differ
diff --git a/goon/icons/obj/fitness.dmi b/goon/icons/obj/fitness.dmi
deleted file mode 100644
index e2675cbda4..0000000000
Binary files a/goon/icons/obj/fitness.dmi and /dev/null differ
diff --git a/goon/sound/effects/spring.ogg b/goon/sound/effects/spring.ogg
deleted file mode 100644
index 472a75e304..0000000000
Binary files a/goon/sound/effects/spring.ogg and /dev/null differ
diff --git a/html/changelogs/AutoChangeLog-pr-15826.yml b/html/changelogs/AutoChangeLog-pr-15826.yml
new file mode 100644
index 0000000000..39769021e0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-15826.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - code_imp: "Auxmos updated to 2.0.0"
diff --git a/html/changelogs/archive/2022-08.yml b/html/changelogs/archive/2022-08.yml
index 6f210ee675..6890cbb6b9 100644
--- a/html/changelogs/archive/2022-08.yml
+++ b/html/changelogs/archive/2022-08.yml
@@ -171,3 +171,14 @@
the station's modular computer chatroom.
- bugfix: Stacked windows in survival pods fixed.
- tweak: rtgs in survival pods now come prewired, I figured out a new trick woo.
+2022-08-30:
+ Putnam3145:
+ - balance: Untied shoes are slightly less common
+ - admin: Better admin logging on potential prefbreak arousal stuff
+ zeroisthebiggay:
+ - bugfix: harpy wings layering
+ - rscadd: and steals the harpy wings from rp so that there is some variety
+2022-08-31:
+ Putnam3145:
+ - bugfix: has_important_message = TRUE added to a bunch of events so you still know
+ where that pyro anomaly is with an intern
diff --git a/html/changelogs/archive/2022-09.yml b/html/changelogs/archive/2022-09.yml
new file mode 100644
index 0000000000..bb6e51dab2
--- /dev/null
+++ b/html/changelogs/archive/2022-09.yml
@@ -0,0 +1,45 @@
+2022-09-01:
+ Putnam3145:
+ - bugfix: Job code is no longer fricked (oh well)
+ - rscadd: Antag roles can be set to "low" rather than just "enabled" or "disabled",
+ which will prevent spending antag rep on that antag but make you a low priority
+ pick
+ - code_imp: Changed no antags from a special role to a toggle (why did i make it
+ a special role)
+ - code_imp: All direct references to ROLE_NO_ANTAGONISTS have been swapped to the
+ HAS_ANTAG_PREF macro or removed
+2022-09-02:
+ Putnam3145:
+ - tweak: comfy disky no longer wants the station to blow up
+ - balance: Field generators now yell if they fail (singuloose nerf)
+ ? zeroisthebiggay and Tastyfish (https://github.com/Tastyfish) and Fikou (https://github.com/Fikou)
+ and san7890 (https://github.com/san7890)
+ : - bugfix: The 2 types of weight machines now have unique in-game names.
+ - imageadd: New sprites for the weight machines.
+ - imageadd: new punching bag sprites!!!!!!!!!!!!!!!!
+ - soundadd: The next time you work out at the gym, please appreciate the new crispness
+ of the sound when that machine starts to squeak as a result of your effort.
+ So refreshing.
+2022-09-03:
+ Putnam3145:
+ - bugfix: Destructive analyzer now does not multiply your stack by 50
+2022-09-14:
+ timothyteakettle:
+ - rscadd: lets ghosts click a pAI device to download into it without a master
+2022-09-18:
+ Putnam3145:
+ - code_imp: Spurious for loop squashed
+ That REALLY Good Soda Flavor, jacklecroy on GitHub, ported by Hatterhat:
+ - rscadd: Using certain martial arts will create a combo meter when you attack.
+ - imageadd: Added combo meter icons.
+ shellspeed1:
+ - rscadd: The Quantum Spin design has been brought over from TG for testing in the
+ Citadel Station facilities.
+ - balance: Launchpads now have a targeting icon.
+ - balance: Massive gamebreaking balance. Xenohybrids can now eat DOUGHNUTS. Security
+ officers everywhere are afraid.
+ - bugfix: makes it some people can speak the encoded langauge.
+ - bugfix: makes the xenotongue a xenohybrid tongue so that people with it can speak
+ other langauges.
+ - rscadd: Adds the RBMK reactor from NSV. Credit to Kmc2000 for originally making
+ it. https://github.com/BeeStation/NSV13/pull/828
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 2614e35211..be99d57a3d 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi
index 4377a0d7c6..39e8eb4b45 100644
Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ
diff --git a/icons/mob/wings.dmi b/icons/mob/wings.dmi
index 49d9e4d4a8..8b74384e29 100644
Binary files a/icons/mob/wings.dmi and b/icons/mob/wings.dmi differ
diff --git a/icons/obj/control_rod.dmi b/icons/obj/control_rod.dmi
new file mode 100644
index 0000000000..bef59ebf2e
Binary files /dev/null and b/icons/obj/control_rod.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 08404ec4dd..5254b53d2b 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/fitness.dmi b/icons/obj/fitness.dmi
new file mode 100644
index 0000000000..c48e2450fb
Binary files /dev/null and b/icons/obj/fitness.dmi differ
diff --git a/icons/obj/inflatable.dmi b/icons/obj/inflatable.dmi
new file mode 100644
index 0000000000..65888be4ad
Binary files /dev/null and b/icons/obj/inflatable.dmi differ
diff --git a/icons/obj/machines/rbmk.dmi b/icons/obj/machines/rbmk.dmi
new file mode 100644
index 0000000000..1c52ddac12
Binary files /dev/null and b/icons/obj/machines/rbmk.dmi differ
diff --git a/icons/obj/machines/rbmk_computer.dmi b/icons/obj/machines/rbmk_computer.dmi
new file mode 100644
index 0000000000..66bbcb40ac
Binary files /dev/null and b/icons/obj/machines/rbmk_computer.dmi differ
diff --git a/icons/obj/machines/reactor_parts.dmi b/icons/obj/machines/reactor_parts.dmi
new file mode 100644
index 0000000000..7fd5cfccb0
Binary files /dev/null and b/icons/obj/machines/reactor_parts.dmi differ
diff --git a/icons/obj/rbmk_computer.dmi b/icons/obj/rbmk_computer.dmi
new file mode 100644
index 0000000000..66bbcb40ac
Binary files /dev/null and b/icons/obj/rbmk_computer.dmi differ
diff --git a/sound/effects/rbmk/alarm.ogg b/sound/effects/rbmk/alarm.ogg
new file mode 100644
index 0000000000..8e698a69f1
Binary files /dev/null and b/sound/effects/rbmk/alarm.ogg differ
diff --git a/sound/effects/rbmk/meltdown.ogg b/sound/effects/rbmk/meltdown.ogg
new file mode 100644
index 0000000000..9c0a1a9917
Binary files /dev/null and b/sound/effects/rbmk/meltdown.ogg differ
diff --git a/sound/effects/rbmk/reactor_hum.ogg b/sound/effects/rbmk/reactor_hum.ogg
new file mode 100644
index 0000000000..adc1e867ed
Binary files /dev/null and b/sound/effects/rbmk/reactor_hum.ogg differ
diff --git a/sound/effects/rbmk/switch.ogg b/sound/effects/rbmk/switch.ogg
new file mode 100644
index 0000000000..668d3b0310
Binary files /dev/null and b/sound/effects/rbmk/switch.ogg differ
diff --git a/sound/effects/rbmk/switch2.ogg b/sound/effects/rbmk/switch2.ogg
new file mode 100644
index 0000000000..b9cd7d1540
Binary files /dev/null and b/sound/effects/rbmk/switch2.ogg differ
diff --git a/sound/effects/rbmk/switch3.ogg b/sound/effects/rbmk/switch3.ogg
new file mode 100644
index 0000000000..b89cbab4cc
Binary files /dev/null and b/sound/effects/rbmk/switch3.ogg differ
diff --git a/sound/effects/rbmk/turbine_end.ogg b/sound/effects/rbmk/turbine_end.ogg
new file mode 100644
index 0000000000..55859aff90
Binary files /dev/null and b/sound/effects/rbmk/turbine_end.ogg differ
diff --git a/sound/effects/rbmk/turbine_mid.ogg b/sound/effects/rbmk/turbine_mid.ogg
new file mode 100644
index 0000000000..2010370425
Binary files /dev/null and b/sound/effects/rbmk/turbine_mid.ogg differ
diff --git a/sound/effects/rbmk/turbine_start.ogg b/sound/effects/rbmk/turbine_start.ogg
new file mode 100644
index 0000000000..4104d0f627
Binary files /dev/null and b/sound/effects/rbmk/turbine_start.ogg differ
diff --git a/sound/effects/ship/freespace2/crane_1.wav b/sound/effects/ship/freespace2/crane_1.wav
new file mode 100644
index 0000000000..394bd658cb
Binary files /dev/null and b/sound/effects/ship/freespace2/crane_1.wav differ
diff --git a/sound/effects/ship/reactor/falloutwind.ogg b/sound/effects/ship/reactor/falloutwind.ogg
new file mode 100644
index 0000000000..4119371517
Binary files /dev/null and b/sound/effects/ship/reactor/falloutwind.ogg differ
diff --git a/sound/effects/ship/reactor/startup.ogg b/sound/effects/ship/reactor/startup.ogg
new file mode 100644
index 0000000000..4df6e736c5
Binary files /dev/null and b/sound/effects/ship/reactor/startup.ogg differ
diff --git a/sound/effects/ship/reactor/startup2.ogg b/sound/effects/ship/reactor/startup2.ogg
new file mode 100644
index 0000000000..b11e80fc82
Binary files /dev/null and b/sound/effects/ship/reactor/startup2.ogg differ
diff --git a/sound/machines/creak.ogg b/sound/machines/creak.ogg
new file mode 100644
index 0000000000..edb9802b08
Binary files /dev/null and b/sound/machines/creak.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 18d1486683..432aa71715 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -196,6 +196,7 @@
#include "code\__HELPERS\level_traits.dm"
#include "code\__HELPERS\lighting.dm"
#include "code\__HELPERS\markov.dm"
+#include "code\__HELPERS\matrices.dm"
#include "code\__HELPERS\mobs.dm"
#include "code\__HELPERS\mouse_control.dm"
#include "code\__HELPERS\names.dm"
@@ -1202,6 +1203,7 @@
#include "code\game\objects\items\devices\reverse_bear_trap.dm"
#include "code\game\objects\items\devices\scanners.dm"
#include "code\game\objects\items\devices\sensor_device.dm"
+#include "code\game\objects\items\devices\swapper.dm"
#include "code\game\objects\items\devices\taperecorder.dm"
#include "code\game\objects\items\devices\traitordevices.dm"
#include "code\game\objects\items\devices\transfer_valve.dm"
@@ -1969,6 +1971,7 @@
#include "code\modules\cargo\bounties\slime.dm"
#include "code\modules\cargo\bounties\special.dm"
#include "code\modules\cargo\bounties\virus.dm"
+#include "code\modules\cargo\exports\engineering.dm"
#include "code\modules\cargo\exports\food_wine.dm"
#include "code\modules\cargo\exports\gear.dm"
#include "code\modules\cargo\exports\large_objects.dm"
@@ -3108,6 +3111,9 @@
#include "code\modules\power\antimatter\containment_jar.dm"
#include "code\modules\power\antimatter\control.dm"
#include "code\modules\power\antimatter\shielding.dm"
+#include "code\modules\power\reactor\fuel_rods.dm"
+#include "code\modules\power\reactor\rbmk.dm"
+#include "code\modules\power\reactor\reactor_cargo.dm"
#include "code\modules\power\singularity\collector.dm"
#include "code\modules\power\singularity\containment_field.dm"
#include "code\modules\power\singularity\emitter.dm"
diff --git a/tgui/packages/tgui/interfaces/NtosRbmkStats.js b/tgui/packages/tgui/interfaces/NtosRbmkStats.js
new file mode 100644
index 0000000000..9d44e0b9e9
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosRbmkStats.js
@@ -0,0 +1,94 @@
+import { map, sortBy } from 'common/collections';
+import { flow } from 'common/fp';
+import { toFixed } from 'common/math';
+import { pureComponentHooks } from 'common/react';
+import { Component, Fragment } from 'inferno';
+import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
+import { NtosWindow } from '../layouts';
+import { useBackend, useLocalState } from '../backend';
+
+export const NtosRbmkStats = (props, context) => {
+ const { act, data } = useBackend(context);
+ const powerData = data.powerData.map((value, i) => [i, value]);
+ const psiData = data.psiData.map((value, i) => [i, value]);
+ const tempInputData = data.tempInputData.map((value, i) => [i, value]);
+ const tempOutputdata = data.tempOutputdata.map((value, i) => [i, value]);
+ return (
+
+
+
+ act('swap_reactor')}
+ content="Change Reactor" />
+ }>
+ Reactor Power (%):
+
+
+ Reactor Pressure (PSI):
+
+ {data.psi} PSI
+
+ Coolant temperature (°C):
+
+ {data.coolantInput} °C
+
+ Outlet temperature (°C):
+
+ {data.coolantOutput} °C
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/RbmkControlRods.js b/tgui/packages/tgui/interfaces/RbmkControlRods.js
new file mode 100644
index 0000000000..13b4a95ca9
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/RbmkControlRods.js
@@ -0,0 +1,56 @@
+import { Fragment } from 'inferno';
+import { useBackend, useLocalState } from '../backend';
+import { Section, ProgressBar, Slider } from '../components';
+import { Window } from '../layouts';
+
+export const RbmkControlRods = (props, context) => {
+ const { act, data } = useBackend(context);
+ const control_rods = data.control_rods;
+ const k = data.k;
+ const desiredK = data.desiredK;
+ return (
+
+
+
+ Control Rod Insertion:
+
+
+ Neutrons per generation (K):
+
+
+ {k}
+
+
+ Target criticality:
+
+ act('input', {
+ target: value,
+ })} />
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/RbmkStats.js b/tgui/packages/tgui/interfaces/RbmkStats.js
new file mode 100644
index 0000000000..c168023db5
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/RbmkStats.js
@@ -0,0 +1,89 @@
+import { map, sortBy } from 'common/collections';
+import { flow } from 'common/fp';
+import { toFixed } from 'common/math';
+import { pureComponentHooks } from 'common/react';
+import { Component, Fragment } from 'inferno';
+import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
+import { Window } from '../layouts';
+import { useBackend, useLocalState } from '../backend';
+
+export const RbmkStats = (props, context) => {
+ const { act, data } = useBackend(context);
+ const powerData = data.powerData.map((value, i) => [i, value]);
+ const psiData = data.psiData.map((value, i) => [i, value]);
+ const tempInputData = data.tempInputData.map((value, i) => [i, value]);
+ const tempOutputdata = data.tempOutputdata.map((value, i) => [i, value]);
+ return (
+
+
+
+ Reactor Power (%):
+
+
+ Reactor Pressure (PSI):
+
+ {data.psi} PSI
+
+ Coolant temperature (°C):
+
+ {data.coolantInput} °C
+
+ Outlet temperature (°C):
+
+ {data.coolantOutput} °C
+
+
+
+
+
+ );
+};