diff --git a/_maps/RandomRuins/StationRuins/Box/Engine/engine_singulo.dmm b/_maps/RandomRuins/StationRuins/Box/Engine/engine_singulo.dmm
index 76cb8bb9db..402cfc73cf 100644
--- a/_maps/RandomRuins/StationRuins/Box/Engine/engine_singulo.dmm
+++ b/_maps/RandomRuins/StationRuins/Box/Engine/engine_singulo.dmm
@@ -735,7 +735,7 @@
/obj/machinery/atmospherics/components/unary/outlet_injector{
dir = 1
},
-/turf/open/space/basic,
+/turf/open/floor/plating/airless,
/area/engine/engineering)
"Si" = (
/obj/structure/cable/yellow{
diff --git a/_maps/RandomRuins/StationRuins/Box/Engine/engine_tesla.dmm b/_maps/RandomRuins/StationRuins/Box/Engine/engine_tesla.dmm
index 7f55d69b17..af2ba1ffd7 100644
--- a/_maps/RandomRuins/StationRuins/Box/Engine/engine_tesla.dmm
+++ b/_maps/RandomRuins/StationRuins/Box/Engine/engine_tesla.dmm
@@ -240,7 +240,7 @@
/obj/machinery/atmospherics/components/unary/outlet_injector{
dir = 1
},
-/turf/open/space/basic,
+/turf/open/floor/plating/airless,
/area/engine/engineering)
"nR" = (
/obj/structure/cable{
@@ -905,14 +905,6 @@
},
/turf/open/space/basic,
/area/space/nearstation)
-"VR" = (
-/obj/structure/lattice/catwalk,
-/obj/machinery/camera/emp_proof{
- c_tag = "Tesla Engine Northeast";
- network = list("ss13","engine")
- },
-/turf/open/space/basic,
-/area/space/nearstation)
"Wf" = (
/obj/effect/turf_decal/stripes/line{
dir = 9
@@ -1728,7 +1720,7 @@ dz
dz
FP
FP
-VR
+FP
dz
dz
Yl
diff --git a/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm b/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm
index fec45c7310..3118ad3f5f 100644
--- a/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm
+++ b/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm
@@ -64,6 +64,9 @@
/turf/open/floor/plating,
/area/mine/living_quarters)
"aN" = (
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
/turf/open/floor/plasteel/white,
/area/mine/laborcamp)
"aW" = (
@@ -82,24 +85,10 @@
"bg" = (
/turf/closed/wall,
/area/mine/eva)
-"bH" = (
-/obj/machinery/atmospherics/components/unary/outlet_injector/atmos/atmos_waste{
- dir = 4;
- piping_layer = 3
- },
-/obj/structure/lattice/catwalk,
-/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
-/area/mine/living_quarters)
-"bI" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/obj/structure/cable{
- icon_state = "2-8"
- },
-/turf/open/floor/plasteel,
+"bn" = (
+/obj/structure/cable,
+/obj/effect/spawner/structure/window/reinforced,
+/turf/open/floor/plating,
/area/mine/living_quarters)
"bX" = (
/obj/machinery/door/window/southleft,
@@ -174,13 +163,6 @@
},
/turf/open/floor/plasteel,
/area/mine/living_quarters)
-"di" = (
-/obj/machinery/light{
- dir = 4
- },
-/obj/effect/turf_decal/tile/brown,
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"dk" = (
/obj/structure/table,
/obj/item/stack/packageWrap,
@@ -192,12 +174,6 @@
},
/turf/open/floor/plasteel,
/area/mine/eva)
-"dC" = (
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"dL" = (
/obj/machinery/atmospherics/components/binary/pump/on,
/obj/structure/cable{
@@ -220,6 +196,15 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
+"dU" = (
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/obj/structure/sign/warning/electricshock{
+ pixel_y = -32
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"dZ" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -301,6 +286,21 @@
},
/turf/open/floor/plasteel,
/area/mine/laborcamp)
+"fx" = (
+/obj/machinery/light/small{
+ dir = 8
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
+"fA" = (
+/obj/machinery/power/terminal{
+ dir = 4
+ },
+/obj/machinery/power/port_gen/pacman{
+ anchored = 1
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"fN" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 5
@@ -308,6 +308,12 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 5
},
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"fO" = (
@@ -349,9 +355,18 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel,
/area/mine/production)
+"gn" = (
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/turf/open/floor/plasteel,
+/area/mine/laborcamp)
"gs" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp)
"gP" = (
@@ -376,6 +391,15 @@
},
/turf/open/floor/plasteel/white,
/area/mine/laborcamp)
+"hb" = (
+/obj/structure/cable{
+ icon_state = "0-2"
+ },
+/obj/machinery/power/smes{
+ charge = 5e+006
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"hf" = (
/obj/item/twohanded/required/kirbyplants/random,
/turf/open/floor/plasteel,
@@ -387,14 +411,6 @@
},
/turf/open/floor/plasteel,
/area/mine/living_quarters)
-"hv" = (
-/obj/machinery/camera{
- c_tag = "Crew Area Hallway West";
- dir = 1;
- network = list("mine")
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"hy" = (
/obj/machinery/camera{
c_tag = "Labor Camp External";
@@ -415,25 +431,21 @@
/turf/open/floor/plasteel,
/area/mine/living_quarters)
"hP" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
- dir = 4
+/obj/structure/lattice/catwalk,
+/obj/machinery/atmospherics/components/unary/outlet_injector/atmos/atmos_waste{
+ dir = 4;
+ piping_layer = 3
},
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 4
- },
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/turf/open/floor/plasteel,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/mine/living_quarters)
"hQ" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
-/obj/structure/cable{
- icon_state = "1-2"
+/obj/machinery/atmospherics/components/unary/outlet_injector/atmos/atmos_waste{
+ dir = 1;
+ piping_layer = 3
},
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
+/obj/structure/lattice/catwalk,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/mine/laborcamp/security)
"ia" = (
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
@@ -582,6 +594,14 @@
},
/turf/open/floor/plasteel,
/area/mine/living_quarters)
+"kP" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable,
+/obj/structure/cable{
+ icon_state = "0-2"
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp)
"kR" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on{
dir = 4
@@ -630,6 +650,9 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 6
},
+/obj/structure/cable{
+ icon_state = "2-4"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp)
"lI" = (
@@ -715,6 +738,9 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 4
},
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"mN" = (
@@ -733,13 +759,11 @@
},
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
-"nv" = (
+"nx" = (
/obj/effect/spawner/structure/window/reinforced,
-/obj/structure/cable{
- icon_state = "0-2"
- },
+/obj/structure/cable,
/turf/open/floor/plating,
-/area/mine/living_quarters)
+/area/mine/laborcamp/security)
"ny" = (
/obj/machinery/computer/prisoner,
/obj/effect/turf_decal/tile/red{
@@ -772,11 +796,17 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 10
},
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
- dir = 1
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
},
/obj/structure/cable{
- icon_state = "1-2"
+ icon_state = "1-8"
+ },
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
},
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
@@ -790,6 +820,14 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
+"nL" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable,
+/obj/structure/cable{
+ icon_state = "0-2"
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"nN" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -805,6 +843,19 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel,
/area/mine/production)
+"nX" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/obj/structure/cable{
+ icon_state = "1-8"
+ },
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"oo" = (
/obj/machinery/conveyor{
id = "mining_internal"
@@ -828,6 +879,9 @@
/obj/machinery/door/airlock/medical/glass{
name = "Infirmary"
},
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel/white,
/area/mine/laborcamp)
"oy" = (
@@ -886,6 +940,22 @@
},
/turf/open/floor/plasteel,
/area/mine/eva)
+"pu" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "0-4"
+ },
+/obj/structure/cable{
+ icon_state = "1-4"
+ },
+/obj/structure/cable{
+ icon_state = "2-4"
+ },
+/obj/effect/spawner/structure/window/reinforced,
+/turf/open/floor/plating,
+/area/mine/living_quarters)
"pK" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
@@ -947,6 +1017,15 @@
},
/turf/open/floor/plating,
/area/mine/living_quarters)
+"qx" = (
+/obj/effect/turf_decal/tile/purple{
+ dir = 4
+ },
+/obj/structure/sign/poster/official/random{
+ pixel_y = 32
+ },
+/turf/open/floor/plasteel,
+/area/mine/living_quarters)
"qH" = (
/obj/structure/extinguisher_cabinet{
pixel_x = 30
@@ -1071,6 +1150,12 @@
"si" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
+/obj/structure/cable{
+ icon_state = "1-8"
+ },
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"sj" = (
@@ -1151,15 +1236,25 @@
},
/turf/open/floor/plasteel,
/area/mine/laborcamp)
+"td" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-4"
+ },
+/obj/structure/cable,
+/turf/open/floor/plating,
+/area/mine/laborcamp)
"tk" = (
/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden/layer3,
-/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden,
/obj/structure/cable{
icon_state = "4-8"
},
/obj/structure/cable{
icon_state = "1-4"
},
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 8
+ },
/turf/open/floor/plasteel,
/area/mine/living_quarters)
"tr" = (
@@ -1183,16 +1278,6 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
-"uh" = (
-/obj/effect/turf_decal/tile/brown{
- dir = 8
- },
-/obj/effect/turf_decal/tile/brown,
-/obj/effect/turf_decal/tile/brown{
- dir = 1
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"uG" = (
/obj/structure/table,
/obj/item/clothing/glasses/meson,
@@ -1258,6 +1343,13 @@
},
/turf/open/floor/plasteel,
/area/mine/laborcamp)
+"vA" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/turf/open/floor/plasteel,
+/area/mine/laborcamp)
"vE" = (
/obj/effect/turf_decal/tile/brown{
dir = 1
@@ -1285,6 +1377,16 @@
},
/turf/open/floor/plasteel,
/area/mine/eva)
+"vM" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/chair,
+/turf/open/floor/plasteel,
+/area/mine/living_quarters)
"wd" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -1356,6 +1458,9 @@
/area/mine/production)
"xd" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp)
"xh" = (
@@ -1399,6 +1504,9 @@
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer3{
dir = 1
},
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"yt" = (
@@ -1416,6 +1524,13 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plasteel,
/area/mine/production)
+"yQ" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"yR" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/turf/open/floor/plasteel,
@@ -1435,12 +1550,26 @@
},
/turf/open/floor/plating,
/area/mine/living_quarters)
+"zk" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-2"
+ },
+/obj/structure/cable,
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"zo" = (
/obj/structure/table,
/turf/open/floor/carpet,
/area/mine/living_quarters)
"zx" = (
/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-2"
+ },
+/obj/structure/cable{
+ icon_state = "0-4"
+ },
/turf/open/floor/plating,
/area/mine/laborcamp)
"zy" = (
@@ -1502,14 +1631,10 @@
/turf/open/floor/plasteel,
/area/mine/living_quarters)
"Ah" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
/obj/structure/cable{
icon_state = "1-2"
},
-/obj/structure/cable{
- icon_state = "2-8"
- },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"Aj" = (
@@ -1602,6 +1727,16 @@
/obj/structure/reagent_dispensers/fueltank,
/turf/open/floor/plating,
/area/mine/living_quarters)
+"BD" = (
+/obj/machinery/door/airlock/maintenance{
+ name = "Mining Station Maintenance";
+ req_access_txt = "48"
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp/security)
"BO" = (
/obj/machinery/door/airlock{
name = "Labor Camp Storage"
@@ -1638,6 +1773,12 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
+"Cl" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/turf/open/floor/plasteel,
+/area/mine/laborcamp)
"Cm" = (
/obj/machinery/vending/snack,
/obj/effect/turf_decal/tile/bar,
@@ -1646,6 +1787,13 @@
},
/turf/open/floor/plasteel,
/area/mine/living_quarters)
+"Cw" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
+/turf/open/floor/plating,
+/area/mine/laborcamp)
"Cy" = (
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
@@ -1748,18 +1896,6 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
-"Em" = (
-/obj/effect/turf_decal/tile/brown{
- dir = 1
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 8
- },
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"Ev" = (
/obj/machinery/light/small{
dir = 4
@@ -1792,7 +1928,9 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/machinery/vending/security,
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"EH" = (
@@ -1805,6 +1943,9 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 9
},
+/obj/structure/cable{
+ icon_state = "1-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp)
"FC" = (
@@ -1825,19 +1966,6 @@
},
/turf/open/floor/plasteel,
/area/mine/laborcamp)
-"Ge" = (
-/obj/effect/spawner/structure/window/reinforced,
-/obj/structure/cable{
- icon_state = "2-4"
- },
-/obj/structure/cable{
- icon_state = "1-4"
- },
-/obj/structure/cable{
- icon_state = "0-4"
- },
-/turf/open/floor/plating,
-/area/mine/living_quarters)
"Gt" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3{
dir = 1
@@ -1845,9 +1973,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/structure/cable{
- icon_state = "4-8"
- },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"GM" = (
@@ -1891,6 +2016,9 @@
dir = 10
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp)
"Hh" = (
@@ -1964,11 +2092,6 @@
},
/turf/open/floor/plasteel,
/area/mine/laborcamp)
-"HR" = (
-/obj/effect/spawner/structure/window/reinforced,
-/obj/structure/cable,
-/turf/open/floor/plating,
-/area/mine/living_quarters)
"HS" = (
/obj/machinery/conveyor{
dir = 10;
@@ -2038,6 +2161,9 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on{
dir = 8
},
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"II" = (
@@ -2053,6 +2179,9 @@
/obj/structure/table,
/obj/effect/turf_decal/tile/red,
/obj/machinery/recharger,
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"IS" = (
@@ -2073,19 +2202,6 @@
},
/turf/open/floor/carpet,
/area/mine/living_quarters)
-"Jc" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
- dir = 4
- },
-/obj/effect/spawner/structure/window/reinforced,
-/obj/structure/cable{
- icon_state = "2-4"
- },
-/obj/structure/cable{
- icon_state = "0-4"
- },
-/turf/open/floor/plating,
-/area/mine/living_quarters)
"Jl" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3,
/obj/effect/turf_decal/tile/purple{
@@ -2267,12 +2383,6 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
-"KR" = (
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"KS" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -2347,10 +2457,11 @@
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"LG" = (
-/obj/effect/turf_decal/tile/brown{
- dir = 1
+/obj/structure/cable{
+ icon_state = "0-2"
},
-/turf/open/floor/plasteel,
+/obj/effect/spawner/structure/window/reinforced,
+/turf/open/floor/plating,
/area/mine/living_quarters)
"LL" = (
/obj/effect/turf_decal/tile/red{
@@ -2392,6 +2503,13 @@
},
/turf/open/floor/plasteel,
/area/mine/production)
+"Mh" = (
+/obj/structure/table,
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/turf/open/floor/plasteel/white,
+/area/mine/laborcamp)
"Mj" = (
/obj/structure/sink{
dir = 4;
@@ -2492,12 +2610,6 @@
},
/turf/open/floor/plating,
/area/mine/living_quarters)
-"ML" = (
-/obj/structure/chair{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"MM" = (
/obj/machinery/light{
dir = 8
@@ -2702,16 +2814,16 @@
/turf/open/floor/plasteel,
/area/mine/living_quarters)
"Qr" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
-/obj/machinery/door/airlock/security/glass{
- name = "Labor Camp Monitoring";
- req_access_txt = "2"
+/obj/structure/cable,
+/obj/structure/cable{
+ icon_state = "1-8"
},
/obj/structure/cable{
- icon_state = "1-2"
+ icon_state = "1-4"
},
-/turf/open/floor/plasteel,
+/obj/effect/spawner/structure/window/reinforced,
+/turf/open/floor/plating,
/area/mine/laborcamp/security)
"Qx" = (
/obj/structure/chair{
@@ -2738,6 +2850,12 @@
},
/turf/open/floor/plasteel,
/area/mine/eva)
+"QL" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/turf/open/floor/plasteel,
+/area/mine/laborcamp/security)
"Rf" = (
/obj/machinery/mineral/unloading_machine{
dir = 1;
@@ -2755,6 +2873,9 @@
req_access_txt = "2";
security_level = 6
},
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp)
"Ru" = (
@@ -2837,6 +2958,12 @@
/area/mine/living_quarters)
"Sp" = (
/obj/effect/spawner/structure/window/reinforced,
+/obj/structure/cable{
+ icon_state = "0-2"
+ },
+/obj/structure/cable{
+ icon_state = "0-8"
+ },
/turf/open/floor/plating,
/area/mine/laborcamp/security)
"Sq" = (
@@ -2904,7 +3031,7 @@
dir = 8
},
/obj/structure/cable{
- icon_state = "4-8"
+ icon_state = "1-8"
},
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
@@ -2951,6 +3078,9 @@
desc = "Powered by the tears and sweat of laborers.";
name = "Prison Ofitser"
},
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"Un" = (
@@ -3099,19 +3229,6 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/turf/open/floor/plasteel/dark,
/area/mine/maintenance)
-"WX" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 5
- },
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer3,
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/structure/cable{
- icon_state = "1-4"
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"WY" = (
/obj/machinery/door/airlock{
id_tag = "miningdorm1";
@@ -3170,12 +3287,6 @@
},
/turf/open/floor/plasteel,
/area/mine/living_quarters)
-"XR" = (
-/obj/structure/sign/poster/official/random{
- pixel_y = 32
- },
-/turf/open/floor/plasteel,
-/area/mine/living_quarters)
"XU" = (
/obj/machinery/power/smes{
charge = 5e+006
@@ -3211,9 +3322,6 @@
dir = 1;
network = list("labor")
},
-/obj/structure/cable{
- icon_state = "4-8"
- },
/turf/open/floor/plasteel,
/area/mine/laborcamp/security)
"Yi" = (
@@ -3291,13 +3399,6 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3,
/turf/open/floor/plasteel,
/area/mine/living_quarters)
-"Zu" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
- dir = 4
- },
-/obj/structure/lattice/catwalk,
-/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
-/area/mine/living_quarters)
"Zw" = (
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
@@ -4329,9 +4430,9 @@ cd
cd
HO
cd
-zI
-zI
-VT
+RF
+RF
+RF
VT
VT
VT
@@ -4386,9 +4487,9 @@ HU
cd
Jr
cd
-my
-wV
-VT
+fA
+fx
+RF
VT
uJ
VT
@@ -4443,13 +4544,13 @@ at
cd
Op
cd
-wV
-wV
-wV
+hb
+dU
+RF
+VT
VT
VT
VT
-Lm
Lm
wz
wz
@@ -4486,8 +4587,8 @@ JY
JY
JY
zx
-zx
-zx
+kP
+td
cd
cd
cd
@@ -4501,15 +4602,15 @@ NH
NH
Ya
Mn
-Mn
+BD
Mn
Dv
Mn
-nv
-Ge
-HR
-VN
-Lm
+wV
+VT
+VT
+VT
+wz
Lm
Lm
Lm
@@ -4542,9 +4643,9 @@ JY
JY
JY
JY
-zx
-wB
+Cw
wB
+Mh
cd
AH
NH
@@ -4562,11 +4663,11 @@ EB
MM
LC
Mn
-xH
-dC
-hf
-VN
-bH
+wV
+wV
+VT
+VT
+Lm
Lm
Lm
Lm
@@ -4603,13 +4704,13 @@ cd
eh
aN
ow
-NH
-NH
-NH
-NH
+Cl
+Cl
+Cl
+gn
vo
ly
-xd
+vA
Hg
gs
gs
@@ -4619,11 +4720,11 @@ si
fN
TC
Mn
-ML
-dC
-oy
-VN
-Zu
+wV
+my
+VT
+VT
+VT
Lm
Lm
Lm
@@ -4664,7 +4765,7 @@ fs
NH
Wt
xd
-xd
+vA
Fx
Yf
uR
@@ -4676,13 +4777,13 @@ Hx
mL
Yg
Mn
-XR
-dC
-Ag
-VN
-Jc
-HR
-VN
+zI
+VT
+VT
+VT
+VT
+wz
+Lm
Lm
Lm
Lm
@@ -4732,14 +4833,14 @@ LL
Hx
xS
Gt
-Sp
-FE
-Em
-cD
-Ag
-sm
-uh
-VN
+Dv
+my
+VT
+VT
+VT
+VT
+wz
+Lm
Lm
Lm
Lm
@@ -4791,12 +4892,12 @@ nJ
Ah
Qr
hQ
-bI
-hQ
-hQ
-WX
-hv
-VN
+VT
+VT
+VT
+Lm
+Lm
+Lm
Lm
Lm
Lm
@@ -4846,13 +4947,13 @@ WL
Hx
IG
UJ
-Sp
-Ag
-Ag
-di
-KR
+yQ
+my
+my
+VT
+Lm
hP
-Ag
+Lm
VN
VN
VN
@@ -4901,15 +5002,15 @@ Ya
Mn
kB
Oh
-Hx
+QL
sR
Al
Al
Al
Al
LG
-hP
-Ag
+pu
+bn
VN
zy
If
@@ -4955,7 +5056,7 @@ my
my
my
zI
-Sp
+Dv
ny
ig
IK
@@ -4964,9 +5065,9 @@ Al
wC
wC
Al
-Ag
-hP
-Ag
+hf
+vM
+xH
VN
Ru
Ag
@@ -5013,16 +5114,16 @@ my
my
zI
Sp
-Sp
-Sp
-Sp
-Sp
+zk
+nL
+nX
+nx
Al
Uz
Ue
Al
-EH
-hP
+qx
+sm
aW
VN
Dr
diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index a630c54b6e..6c1642ca8b 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -21911,7 +21911,7 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
-/obj/machinery/computer/arcade/minesweeper,
+/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain)
"bdj" = (
@@ -55854,6 +55854,10 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/dorms)
+"nZL" = (
+/obj/machinery/computer/arcade/minesweeper,
+/turf/open/floor/wood,
+/area/crew_quarters/heads/captain)
"oax" = (
/obj/structure/table/wood/fancy,
/obj/machinery/light,
@@ -90865,7 +90869,7 @@ aVv
aXg
aYF
aZV
-bbw
+nZL
bcn
tgH
ben
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index fd0f0a1742..1abdd85019 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -62353,7 +62353,7 @@
/turf/open/floor/wood,
/area/crew_quarters/heads/captain/private)
"cbd" = (
-/obj/effect/landmark/event_spawn,
+/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain/private)
"cbe" = (
@@ -127169,6 +127169,13 @@
dir = 9
},
/area/science/circuit)
+"rjr" = (
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 1
+ },
+/obj/effect/landmark/event_spawn,
+/turf/open/floor/wood,
+/area/crew_quarters/heads/captain/private)
"rqh" = (
/obj/structure/lattice,
/turf/open/space/basic,
@@ -169426,7 +169433,7 @@ bUO
bXf
bUQ
cbe
-ccP
+rjr
ceH
cgu
cij
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 3353d1b15b..1be18fdfeb 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -28355,6 +28355,7 @@
/obj/structure/disposalpipe/segment{
dir = 6
},
+/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/carpet,
/area/crew_quarters/heads/captain/private)
"bdL" = (
@@ -81645,6 +81646,19 @@
/obj/machinery/door/firedoor,
/turf/open/floor/plating,
/area/crew_quarters/cryopod)
+"eQf" = (
+/obj/machinery/computer/shuttle/mining/common{
+ dir = 1
+ },
+/obj/structure/window/reinforced,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/hallway/primary/port)
"eZe" = (
/obj/structure/cable/yellow{
icon_state = "1-2"
@@ -98511,7 +98525,7 @@ bgA
biz
bkb
blZ
-bnM
+eQf
bqf
bqf
bqf
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index 22734b0979..b34da30300 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -9830,6 +9830,7 @@
/obj/structure/cable{
icon_state = "0-2"
},
+/obj/item/twohanded/required/kirbyplants/random,
/turf/open/floor/carpet,
/area/crew_quarters/heads/captain)
"axT" = (
@@ -11162,10 +11163,7 @@
/obj/structure/extinguisher_cabinet{
pixel_x = -24
},
-/obj/item/twohanded/required/kirbyplants{
- icon_state = "plant-20";
- pixel_y = 3
- },
+/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/heads/captain)
"aBl" = (
@@ -28097,11 +28095,6 @@
},
/turf/open/floor/plasteel/dark,
/area/science/robotics/lab)
-"boY" = (
-/obj/effect/turf_decal/bot,
-/obj/effect/landmark/start/cyborg,
-/turf/open/floor/plasteel,
-/area/science/robotics/lab)
"boZ" = (
/obj/structure/table,
/obj/machinery/cell_charger,
@@ -98811,8 +98804,8 @@ beI
bkt
blH
bmO
-boY
-boY
+bnQ
+bnQ
bqi
bry
bsZ
@@ -99068,8 +99061,8 @@ bju
bkt
blI
bmO
-boY
-boY
+bnQ
+bnQ
bqi
brz
bta
diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm
index fe48edd266..65be8258a1 100644
--- a/code/__DEFINES/MC.dm
+++ b/code/__DEFINES/MC.dm
@@ -20,7 +20,7 @@
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum}
-#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum
+#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum;Processor.currentrun -= Datum
//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm
index 2b19610040..56d5c63833 100644
--- a/code/__DEFINES/citadel_defines.dm
+++ b/code/__DEFINES/citadel_defines.dm
@@ -34,6 +34,7 @@
#define CAN_MASTURBATE_WITH (1<<5)
#define MASTURBATE_LINKED_ORGAN (1<<6) //used to pass our mission to the linked organ
#define CAN_CLIMAX_WITH (1<<7)
+#define GENITAL_CAN_AROUSE (1<<8)
#define COCK_SIZE_MIN 1
#define COCK_SIZE_MAX 20
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index ad93dd2d54..939f6698cb 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -199,4 +199,6 @@
#define LORENTZ_CUMULATIVE_DISTRIBUTION(x, y, s) ( (1/PI)*TORADIANS(arctan((x-y)/s)) + 1/2 )
#define RULE_OF_THREE(a, b, x) ((a*x)/b)
-// )
\ No newline at end of file
+// )
+
+#define MANHATTAN_DISTANCE(a, b) (abs(a.x - b.x) + abs(a.y - b.y))
\ No newline at end of file
diff --git a/code/__DEFINES/profile.dm b/code/__DEFINES/profile.dm
index 28fc7782ce..8929e0f05e 100644
--- a/code/__DEFINES/profile.dm
+++ b/code/__DEFINES/profile.dm
@@ -1,5 +1,5 @@
-#define PROFILE_START ;PROFILE_STORE = list();PROFILE_SET;
-#define PROFILE_STOP ;PROFILE_STORE = null;
+#define LINE_PROFILE_START ;PROFILE_STORE = list();PROFILE_SET;
+#define LINE_PROFILE_STOP ;PROFILE_STORE = null;
#define PROFILE_SET ;PROFILE_TIME = TICK_USAGE_REAL; PROFILE_LINE = __LINE__; PROFILE_FILE = __FILE__; PROFILE_SLEEPCHECK = world.time;
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 5c54843df2..162c898917 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -47,6 +47,7 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
+#define INIT_ORDER_PROFILER 101
#define INIT_ORDER_FAIL2TOPIC 22
#define INIT_ORDER_TITLE 20
#define INIT_ORDER_GARBAGE 19
diff --git a/code/__DEFINES/tgs.config.dm b/code/__DEFINES/tgs.config.dm
index a40b5d4663..9f4f63a1fc 100644
--- a/code/__DEFINES/tgs.config.dm
+++ b/code/__DEFINES/tgs.config.dm
@@ -1,4 +1,5 @@
#define TGS_EXTERNAL_CONFIGURATION
+#define TGS_V3_API
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
#define TGS_READ_GLOBAL(Name) GLOB.##Name
#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index db4f046ec3..dcccfc9295 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -107,6 +107,22 @@
var/commit //full sha of compiled commit
var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch
+//represents a version of tgstation-server
+/datum/tgs_version
+ var/suite //The suite version, can be >=3
+
+ //this group of variables can be null to represent a wild card
+ var/major //The major version
+ var/minor //The minor version
+ var/patch //The patch version
+
+ var/raw_parameter //The unparsed parameter
+ var/deprefixed_parameter //The version only bit of raw_parameter
+
+//if the tgs_version is a wildcard version
+/datum/tgs_version/proc/Wildcard()
+ return
+
//represents a merge of a GitHub pull request
/datum/tgs_revision_information/test_merge
var/number //pull request number
@@ -155,22 +171,22 @@
//FUNCTIONS
-//Returns the respective string version of the API
+//Returns the respective supported /datum/tgs_version of the API
/world/proc/TgsMaximumAPIVersion()
return
/world/proc/TgsMinimumAPIVersion()
return
-//Gets the current version of the server tools running the server
-/world/proc/TgsVersion()
- return
-
//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
//No function below this succeeds if it returns FALSE
/world/proc/TgsAvailable()
return
+//Gets the current /datum/tgs_version of the server tools running the server
+/world/proc/TgsVersion()
+ return
+
/world/proc/TgsInstanceName()
return
diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm
index c0acf4c5a5..1bde770045 100644
--- a/code/__HELPERS/_cit_helpers.dm
+++ b/code/__HELPERS/_cit_helpers.dm
@@ -57,14 +57,11 @@ GLOBAL_LIST_EMPTY(ipc_antennas_list)
//Genitals and Arousal Lists
GLOBAL_LIST_EMPTY(genitals_list)
-GLOBAL_LIST_EMPTY(cock_shapes_list)//global_lists.dm for the list initializations //Now also _DATASTRUCTURES globals.dm
-GLOBAL_LIST_EMPTY(cock_shapes_icons) //Associated list for names->icon_states for cockshapes.
+GLOBAL_LIST_EMPTY(cock_shapes_list)
GLOBAL_LIST_EMPTY(gentlemans_organ_names)
GLOBAL_LIST_EMPTY(balls_shapes_list)
-GLOBAL_LIST_EMPTY(balls_shapes_icons)
GLOBAL_LIST_EMPTY(breasts_size_list)
GLOBAL_LIST_EMPTY(breasts_shapes_list)
-GLOBAL_LIST_EMPTY(breasts_shapes_icons)
GLOBAL_LIST_EMPTY(vagina_shapes_list)
GLOBAL_LIST_INIT(cum_into_containers_list, list(/obj/item/reagent_containers/food/snacks/pie)) //Yer fuggin snowflake name list jfc
GLOBAL_LIST_INIT(dick_nouns, list("dick","cock","member","shaft"))
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index ff9d1bec4c..7045767f98 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -51,26 +51,15 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/antenna, GLOB.ipc_antennas_list, roundstart = TRUE)
//genitals
init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list)
- for(var/K in GLOB.cock_shapes_list)
- var/datum/sprite_accessory/penis/value = GLOB.cock_shapes_list[K]
- GLOB.cock_shapes_icons[K] = value.icon_state
-
init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list)
GLOB.breasts_size_list = list ("a", "b", "c", "d", "e") //We need the list to choose from initialized, but it's no longer a sprite_accessory thing.
GLOB.gentlemans_organ_names = list("phallus", "willy", "dick", "prick", "member", "tool", "gentleman's organ",
"cock", "wang", "knob", "dong", "joystick", "pecker", "johnson", "weenie", "tadger", "schlong", "thirsty ferret",
"baloney pony", "schlanger", "Mutton dagger", "old blind bob","Hanging Johnny", "fishing rod", "Tally whacker", "polly rocket",
"One eyed trouser trout", "Ding dong", "ankle spanker", "Pork sword", "engine cranker", "Harry hot dog", "Davy Crockett",
"Kidney cracker", "Heat seeking moisture missile", "Giggle stick", "love whistle", "Tube steak", "Uncle Dick", "Purple helmet warrior")
- for(var/K in GLOB.breasts_shapes_list)
- var/datum/sprite_accessory/breasts/value = GLOB.breasts_shapes_list[K]
- GLOB.breasts_shapes_icons[K] = value.icon_state
-
- init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list)
- for(var/K in GLOB.balls_shapes_list)
- var/datum/sprite_accessory/testicles/value = GLOB.balls_shapes_list[K]
- GLOB.balls_shapes_icons[K] = value.icon_state
for(var/gpath in subtypesof(/obj/item/organ/genital))
var/obj/item/organ/genital/G = gpath
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 9cced1fdc7..2cb088c654 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1543,3 +1543,25 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
for(var/each_item in items_list)
for(var/i in 1 to items_list[each_item])
new each_item(where_to)
+
+//sends a message to chat
+//config_setting should be one of the following
+//null - noop
+//empty string - use TgsTargetBroadcast with admin_only = FALSE
+//other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used
+/proc/send2chat(message, config_setting)
+ if(config_setting == null || !world.TgsAvailable())
+ return
+ var/datum/tgs_version/version = world.TgsVersion()
+ if(config_setting == "" || version.suite == 3)
+ world.TgsTargetedChatBroadcast(message, FALSE)
+ return
+
+ var/list/channels_to_use = list()
+ for(var/I in world.TgsChatChannelInfo())
+ var/datum/tgs_chat_channel/channel = I
+ if(channel.tag == config_setting)
+ channels_to_use += channel
+
+ if(channels_to_use.len)
+ world.TgsChatBroadcast()
\ No newline at end of file
diff --git a/code/__HELPERS/vector.dm b/code/__HELPERS/vector.dm
new file mode 100644
index 0000000000..80295bde0e
--- /dev/null
+++ b/code/__HELPERS/vector.dm
@@ -0,0 +1,57 @@
+// Basic geometry things.
+
+/datum/vector/
+ var/x = 0
+ var/y = 0
+
+/datum/vector/New(var/x, var/y)
+ src.x = x
+ src.y = y
+
+/datum/vector/proc/duplicate()
+ return new /datum/vector(x, y)
+
+/datum/vector/proc/euclidian_norm()
+ return sqrt(x*x + y*y)
+
+/datum/vector/proc/squared_norm()
+ return x*x + y*y
+
+/datum/vector/proc/normalize()
+ var/norm = euclidian_norm()
+ x = x/norm
+ y = y/norm
+ return src
+
+/datum/vector/proc/chebyshev_norm()
+ return max(abs(x), abs(y))
+
+/datum/vector/proc/chebyshev_normalize()
+ var/norm = chebyshev_norm()
+ x = x/norm
+ y = y/norm
+ return src
+
+/datum/vector/proc/is_integer()
+ return ISINTEGER(x) && ISINTEGER(y)
+
+/atom/movable/proc/vector_translate(var/datum/vector/V, var/delay)
+ var/turf/T = get_turf(src)
+ var/turf/destination = locate(T.x + V.x, T.y + V.y, z)
+ var/datum/vector/V_norm = V.duplicate()
+ V_norm.chebyshev_normalize()
+ if (!V_norm.is_integer())
+ return
+ var/turf/destination_temp
+ while (destination_temp != destination)
+ destination_temp = locate(T.x + V_norm.x, T.y + V_norm.y, z)
+ forceMove(destination_temp)
+ T = get_turf(src)
+ sleep(delay + world.tick_lag) // Shortest possible time to sleep
+
+/atom/proc/get_translated_turf(var/datum/vector/V)
+ var/turf/T = get_turf(src)
+ return locate(T.x + V.x, T.y + V.y, z)
+
+/proc/atoms2vector(var/atom/A, var/atom/B)
+ return new /datum/vector((B.x - A.x), (B.y - A.y)) // Vector from A -> B
\ No newline at end of file
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index cda19dbaac..3f11c8a2f2 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -1,19 +1,22 @@
-
+/**
+ *This is the proc that handles the order of an item_attack.
+ *The order of procs called is:
+ *tool_act on the target. If it returns TRUE, the chain will be stopped.
+ *pre_attack() on src. If this returns TRUE, the chain will be stopped.
+ *attackby on the target. If it returns TRUE, the chain will be stopped.
+ *and lastly
+ *afterattack. The return value does not matter.
+ */
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
- if(!tool_attack_chain(user, target) && pre_attack(target, user, params))
- // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
- var/resolved = target.attackby(src, user, params)
- if(!resolved && target && !QDELETED(src))
- afterattack(target, user, 1, params) // 1: clicking something Adjacent
-
-
-//Checks if the item can work as a tool, calling the appropriate tool behavior on the target
-/obj/item/proc/tool_attack_chain(mob/user, atom/target)
- if(!tool_behaviour)
- return FALSE
-
- return target.tool_act(user, src, tool_behaviour)
-
+ if(tool_behaviour && target.tool_act(user, src, tool_behaviour))
+ return
+ if(pre_attack(target, user, params))
+ return
+ if(target.attackby(src,user, params))
+ return
+ if(QDELETED(src) || QDELETED(target))
+ return
+ afterattack(target, user, TRUE, params)
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
@@ -23,8 +26,8 @@
/obj/item/proc/pre_attack(atom/A, mob/living/user, params) //do stuff before attackby!
if(SEND_SIGNAL(src, COMSIG_ITEM_PRE_ATTACK, A, user, params) & COMPONENT_NO_ATTACK)
- return FALSE
- return TRUE //return FALSE to avoid calling attackby after this proc does stuff
+ return TRUE
+ return FALSE //return TRUE to avoid calling attackby after this proc does stuff
// No comment
/atom/proc/attackby(obj/item/W, mob/user, params)
@@ -112,7 +115,7 @@
send_item_attack_message(I, user)
if(I.force)
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
- if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
+ if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 2e5b8a1852..cb8c0fe966 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -1,3 +1,5 @@
+/datum/config_entry/flag/auto_profile // Automatically start profiler on server start
+
/datum/config_entry/flag/autoadmin // if autoadmin is enabled
protection = CONFIG_ENTRY_LOCKED
@@ -392,6 +394,13 @@
config_entry_value = 50
/datum/config_entry/flag/irc_announce_new_game
+ deprecated_by = /datum/config_entry/string/chat_announce_new_game
+
+/datum/config_entry/flag/irc_announce_new_game/DeprecationUpdate(value)
+ return "" //default broadcast
+
+/datum/config_entry/string/chat_announce_new_game
+ config_entry_value = null
/datum/config_entry/flag/debug_admin_hrefs
diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/processing/weather.dm
similarity index 77%
rename from code/controllers/subsystem/weather.dm
rename to code/controllers/subsystem/processing/weather.dm
index 5e44f2e4c3..0a1b4fc609 100644
--- a/code/controllers/subsystem/weather.dm
+++ b/code/controllers/subsystem/processing/weather.dm
@@ -4,25 +4,16 @@
#define END_STAGE 4
//Used for all kinds of weather, ex. lavaland ash storms.
-SUBSYSTEM_DEF(weather)
+PROCESSING_SUBSYSTEM_DEF(weather)
name = "Weather"
flags = SS_BACKGROUND
wait = 10
runlevels = RUNLEVEL_GAME
- var/list/processing = list()
var/list/eligible_zlevels = list()
var/list/next_hit_by_zlevel = list() //Used by barometers to know when the next storm is coming
-/datum/controller/subsystem/weather/fire()
- // process active weather
- for(var/V in processing)
- var/datum/weather/W = V
- if(W.aesthetic || W.stage != MAIN_STAGE)
- continue
- for(var/i in GLOB.mob_living_list)
- var/mob/living/L = i
- if(W.can_weather_act(L))
- W.weather_act(L)
+/datum/controller/subsystem/processing/weather/fire()
+ . = ..() //Active weather is handled by . = ..() processing subsystem base fire().
// start random weather on relevant levels
for(var/z in eligible_zlevels)
@@ -34,7 +25,7 @@ SUBSYSTEM_DEF(weather)
addtimer(CALLBACK(src, .proc/make_eligible, z, possible_weather), randTime + initial(W.weather_duration_upper), TIMER_UNIQUE) //Around 5-10 minutes between weathers
next_hit_by_zlevel["[z]"] = world.time + randTime + initial(W.telegraph_duration)
-/datum/controller/subsystem/weather/Initialize(start_timeofday)
+/datum/controller/subsystem/processing/weather/Initialize(start_timeofday)
for(var/V in subtypesof(/datum/weather))
var/datum/weather/W = V
var/probability = initial(W.probability)
@@ -47,7 +38,7 @@ SUBSYSTEM_DEF(weather)
eligible_zlevels["[z]"][W] = probability
return ..()
-/datum/controller/subsystem/weather/proc/run_weather(datum/weather/weather_datum_type, z_levels)
+/datum/controller/subsystem/processing/weather/proc/run_weather(datum/weather/weather_datum_type, z_levels)
if (istext(weather_datum_type))
for (var/V in subtypesof(/datum/weather))
var/datum/weather/W = V
@@ -69,11 +60,11 @@ SUBSYSTEM_DEF(weather)
var/datum/weather/W = new weather_datum_type(z_levels)
W.telegraph()
-/datum/controller/subsystem/weather/proc/make_eligible(z, possible_weather)
+/datum/controller/subsystem/processing/weather/proc/make_eligible(z, possible_weather)
eligible_zlevels[z] = possible_weather
next_hit_by_zlevel["[z]"] = null
-/datum/controller/subsystem/weather/proc/get_weather(z, area/active_area)
+/datum/controller/subsystem/processing/weather/proc/get_weather(z, area/active_area)
var/datum/weather/A
for(var/V in processing)
var/datum/weather/W = V
diff --git a/code/controllers/subsystem/profiler.dm b/code/controllers/subsystem/profiler.dm
new file mode 100644
index 0000000000..ec8b243073
--- /dev/null
+++ b/code/controllers/subsystem/profiler.dm
@@ -0,0 +1,63 @@
+#define PROFILER_FILENAME "profiler.json"
+
+SUBSYSTEM_DEF(profiler)
+ name = "Profiler"
+ init_order = INIT_ORDER_PROFILER
+ runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
+ wait = 3000
+ flags = SS_NO_TICK_CHECK
+ var/fetch_cost = 0
+ var/write_cost = 0
+
+/datum/controller/subsystem/profiler/stat_entry(msg)
+ msg += "F:[round(fetch_cost,1)]ms"
+ msg += "|W:[round(write_cost,1)]ms"
+ ..(msg)
+
+/datum/controller/subsystem/profiler/Initialize()
+ if(CONFIG_GET(flag/auto_profile))
+ StartProfiling()
+ else
+ StopProfiling() //Stop the early start from world/New
+ return ..()
+
+/datum/controller/subsystem/profiler/fire()
+ if(CONFIG_GET(flag/auto_profile))
+ DumpFile()
+
+/datum/controller/subsystem/profiler/Shutdown()
+ if(CONFIG_GET(flag/auto_profile))
+ DumpFile()
+ return ..()
+
+/datum/controller/subsystem/profiler/proc/StartProfiling()
+#if DM_BUILD < 1506 || DM_VERSION < 513
+ stack_trace("Auto profiling unsupported on this byond version")
+ CONFIG_SET(flag/auto_profile, FALSE)
+#else
+ world.Profile(PROFILE_START)
+#endif
+
+/datum/controller/subsystem/profiler/proc/StopProfiling()
+#if DM_BUILD >= 1506 && DM_VERSION >= 513
+ world.Profile(PROFILE_STOP)
+#endif
+
+/datum/controller/subsystem/profiler/proc/DumpFile()
+#if DM_BUILD < 1506 || DM_VERSION < 513
+ stack_trace("Auto profiling unsupported on this byond version")
+ CONFIG_SET(flag/auto_profile, FALSE)
+#else
+ var/timer = TICK_USAGE_REAL
+ var/current_profile_data = world.Profile(PROFILE_REFRESH,format="json")
+ fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ CHECK_TICK
+ if(!length(current_profile_data)) //Would be nice to have explicit proc to check this
+ stack_trace("Warning, profiling stopped manually before dump.")
+ var/json_file = file("[GLOB.log_directory]/[PROFILER_FILENAME]")
+ if(fexists(json_file))
+ fdel(json_file)
+ timer = TICK_USAGE_REAL
+ WRITE_FILE(json_file, current_profile_data)
+ write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+#endif
diff --git a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm
index 9e926b29a1..b77c78c4bb 100644
--- a/code/controllers/subsystem/server_maint.dm
+++ b/code/controllers/subsystem/server_maint.dm
@@ -81,8 +81,8 @@ SUBSYSTEM_DEF(server_maint)
co.ehjax_send(data = "roundrestart")
if(server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[server]")
- var/tgsversion = world.TgsVersion()
+ var/datum/tgs_version/tgsversion = world.TgsVersion()
if(tgsversion)
- SSblackbox.record_feedback("text", "server_tools", 1, tgsversion)
+ SSblackbox.record_feedback("text", "server_tools", 1, tgsversion.raw_parameter)
#undef PING_BUFFER_TIME
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 971a9f837b..fe994facdc 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -158,8 +158,7 @@ SUBSYSTEM_DEF(ticker)
for(var/client/C in GLOB.clients)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, "Welcome to [station_name()]!")
- if(CONFIG_GET(flag/irc_announce_new_game))
- world.TgsTargetedChatBroadcast("New round starting on [SSmapping.config.map_name]!", FALSE)
+ send2chat("New round starting on [SSmapping.config.map_name]!", CONFIG_GET(string/chat_announce_new_game))
current_state = GAME_STATE_PREGAME
//Everyone who wants to be an observer is now spawned
create_observers()
@@ -488,7 +487,7 @@ SUBSYSTEM_DEF(ticker)
if("SCORE")
SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = MAJORITY_JUDGEMENT_VOTING)
// fallback
- SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = MAJORITY_JUDGEMENT_VOTING)
+ SSvote.initiate_vote("map","server",hideresults=TRUE)
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return current_state >= GAME_STATE_PLAYING
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 54c719a6d5..04d9c706ed 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -820,3 +820,11 @@
target.layer = old_layer
target.plane = old_plane
current_button.appearance_cache = target.appearance
+
+/proc/get_action_of_type(mob/M, var/action_type)
+ if(!M.actions || !ispath(action_type, /datum/action))
+ return
+ for(var/datum/action/A in M.actions)
+ if(istype(A, action_type))
+ return A
+ return
\ No newline at end of file
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index d467f66b6a..a8e03946b6 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -25,6 +25,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/unlock_code
var/failsafe_code
var/datum/ui_state/checkstate
+ var/compact_mode = FALSE
var/debug = FALSE
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate)
@@ -122,7 +123,7 @@ GLOBAL_LIST_EMPTY(uplinks)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
+ ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
@@ -139,29 +140,35 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
+ data["compact_mode"] = compact_mode
+ return data
+
+/datum/component/uplink/ui_static_data(mob/user)
+ var/list/data = list()
data["categories"] = list()
for(var/category in uplink_items)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
- if(category == selected_cat)
- for(var/item in uplink_items[category])
- var/datum/uplink_item/I = uplink_items[category][item]
- if(I.limited_stock == 0)
+ for(var/item in uplink_items[category])
+ var/datum/uplink_item/I = uplink_items[category][item]
+ if(I.limited_stock == 0)
+ continue
+ if(I.restricted_roles.len)
+ var/is_inaccessible = TRUE
+ for(var/R in I.restricted_roles)
+ if(R == user.mind.assigned_role || debug)
+ is_inaccessible = FALSE
+ if(is_inaccessible)
continue
- if(I.restricted_roles.len)
- var/is_inaccessible = 1
- for(var/R in I.restricted_roles)
- if(R == user.mind.assigned_role || debug)
- is_inaccessible = 0
if(is_inaccessible)
continue
- cat["items"] += list(list(
- "name" = I.name,
- "cost" = I.cost,
- "desc" = I.desc,
- ))
+ cat["items"] += list(list(
+ "name" = I.name,
+ "cost" = I.cost,
+ "desc" = I.desc,
+ ))
data["categories"] += list(cat)
return data
@@ -189,6 +196,8 @@ GLOBAL_LIST_EMPTY(uplinks)
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
+ if("compact_toggle")
+ compact_mode = !compact_mode
return TRUE
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm
index a63be406c1..04f84ee008 100644
--- a/code/datums/elements/mob_holder.dm
+++ b/code/datums/elements/mob_holder.dm
@@ -1,5 +1,5 @@
/datum/element/mob_holder
- element_flags = ELEMENT_BESPOKE
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
var/worn_state
var/alt_worn
diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm
index 55bc9bafad..fffb90c681 100644
--- a/code/datums/elements/wuv.dm
+++ b/code/datums/elements/wuv.dm
@@ -1,6 +1,6 @@
/datum/element/wuv //D'awwwww
- element_flags = ELEMENT_BESPOKE
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
//the for the me emote proc call when petted.
var/pet_emote
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index 7d040a4982..0b0fed407c 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -27,7 +27,8 @@
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
- msg += "Test merge active of PR #[tm.number] commit [tm.commit]"
+ msg += "Test merge active of PR #[tm.number] commit [tm.pull_request_commit]"
+ SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.pull_request_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
if(commit && commit != originmastercommit)
msg += "HEAD: [commit]"
@@ -75,7 +76,8 @@
else if(!pc)
msg += "No commit information"
if(world.TgsAvailable())
- msg += "Server tools version: [world.TgsVersion()]"
+ var/datum/tgs_version/version = world.TgsVersion()
+ msg += "Server tools version: [version.raw_parameter]"
// Game mode odds
msg += "
Current Informational Settings:"
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index faa7eec392..b2177a860a 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -144,9 +144,8 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
/datum/quirk/nearsighted/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
- H.put_in_hands(glasses)
- H.equip_to_slot(glasses, SLOT_GLASSES)
- H.regenerate_icons() //this is to remove the inhand icon, which persists even if it's not in their hands
+ if(!H.equip_to_slot_if_possible(glasses, SLOT_GLASSES))
+ H.put_in_hands(glasses)
/datum/quirk/nyctophobia
name = "Nyctophobia"
diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm
index 01b9facbb8..6761ecbb4c 100644
--- a/code/datums/weather/weather.dm
+++ b/code/datums/weather/weather.dm
@@ -61,7 +61,7 @@
if(A.z in impacted_z_levels)
impacted_areas |= A
weather_duration = rand(weather_duration_lower, weather_duration_upper)
- START_PROCESSING(SSweather, src)
+ START_PROCESSING(SSweather, src) //The reason this doesn't start and stop at main stage is because processing list is also used to see active running weathers (for example, you wouldn't want two ash storms starting at once.)
update_areas()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
@@ -107,6 +107,14 @@
STOP_PROCESSING(SSweather, src)
update_areas()
+/datum/weather/process()
+ if(aesthetic || (stage != MAIN_STAGE))
+ return
+ for(var/i in GLOB.mob_living_list)
+ var/mob/living/L = i
+ if(can_weather_act(L))
+ weather_act(L)
+
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
var/turf/mob_turf = get_turf(L)
if(mob_turf && !(mob_turf.z in impacted_z_levels))
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 3bfcececda..adc1748ded 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -2,13 +2,11 @@
#define FONT_SIZE "5pt"
#define FONT_COLOR "#09f"
#define FONT_STYLE "Arial Black"
-#define MAX_TIMER 9000
-
-#define PRESET_SHORT 1200
-#define PRESET_MEDIUM 1800
-#define PRESET_LONG 3000
-
+#define MAX_TIMER 15 MINUTES
+#define PRESET_SHORT 2 MINUTES
+#define PRESET_MEDIUM 3 MINUTES
+#define PRESET_LONG 5 MINUTES
///////////////////////////////////////////////////////////////////////////////////////////////
// Brig Door control displays.
@@ -25,7 +23,7 @@
desc = "A remote control for a door."
req_access = list(ACCESS_SECURITY)
density = FALSE
- var/id = null // id of linked machinery/lockers
+ var/id // id of linked machinery/lockers
var/activation_time = 0
var/timer_duration = 0
@@ -43,8 +41,6 @@
Radio = new/obj/item/radio(src)
Radio.listening = 0
-/obj/machinery/door_timer/Initialize()
- . = ..()
if(id != null)
for(var/obj/machinery/door/window/brigdoor/M in urange(20, src))
if (M.id == id)
@@ -71,7 +67,7 @@
return
if(timing)
- if(world.realtime - activation_time >= timer_duration)
+ if(REALTIMEOFDAY - activation_time >= timer_duration)
timer_end() // open doors, reset timer, clear status screen
update_icon()
@@ -80,14 +76,13 @@
..()
update_icon()
-
// open/closedoor checks if door_timer has power, if so it checks if the
// linked door is open/closed (by density) then opens it/closes it.
/obj/machinery/door_timer/proc/timer_start()
if(stat & (NOPOWER|BROKEN))
return 0
- activation_time = world.realtime
+ activation_time = REALTIMEOFDAY
timing = TRUE
for(var/obj/machinery/door/window/brigdoor/door in targets)
@@ -104,7 +99,6 @@
C.update_icon()
return 1
-
/obj/machinery/door_timer/proc/timer_end(forced = FALSE)
if(stat & (NOPOWER|BROKEN))
@@ -136,7 +130,7 @@
/obj/machinery/door_timer/proc/time_left(seconds = FALSE)
- . = max(0,timer_duration - (activation_time ? world.realtime - activation_time : 0))
+ . = max(0,timer_duration - (activation_time ? REALTIMEOFDAY - activation_time : 0))
if(seconds)
. /= 10
@@ -240,7 +234,7 @@
preset_time = PRESET_LONG
. = set_timer(preset_time)
if(timing)
- activation_time = world.realtime
+ activation_time = REALTIMEOFDAY
else
. = FALSE
diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm
index 237aca02a9..8b8a02d190 100644
--- a/code/game/objects/items/RPD.dm
+++ b/code/game/objects/items/RPD.dm
@@ -345,7 +345,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
A = get_turf(A)
var/can_make_pipe = (isturf(A) || is_type_in_typecache(A, make_pipe_whitelist))
- . = FALSE
+ . = TRUE
if((mode & DESTROY_MODE) && istype(A, /obj/item/pipe) || istype(A, /obj/structure/disposalconstruct) || istype(A, /obj/structure/c_transit_tube) || istype(A, /obj/structure/c_transit_tube_pod) || istype(A, /obj/item/pipe_meter))
to_chat(user, "You start destroying a pipe...")
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index ce0e492393..2af449df24 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -38,6 +38,7 @@
/obj/item/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
+ QDEL_NULL(soundloop)
return ..()
/obj/item/geiger_counter/process()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index e17f96495d..45911afc73 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -51,6 +51,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("closet", /obj/structure/closet, 2, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("canister", /obj/machinery/portable_atmospherics/canister, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("trash bin", /obj/structure/closet/crate/bin, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE),\
null, \
new/datum/stack_recipe("floor tile", /obj/item/stack/tile/plasteel, 1, 4, 20), \
new/datum/stack_recipe("metal rod", /obj/item/stack/rods, 1, 2, 60), \
@@ -733,7 +734,6 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("opaque plastic flaps", /obj/structure/plasticflaps/opaque, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \
new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \
- new /datum/stack_recipe("large trash cart", /obj/structure/closet/crate/bin,50),\
new /datum/stack_recipe("wet floor sign", /obj/item/caution, 2)))
/obj/item/stack/sheet/plastic
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index 028f36b1d1..67e54128a4 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -399,10 +399,12 @@
/obj/item/storage/bag/bio/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL //Allows you to pick up Lungs, Liver, and Stomach
STR.max_combined_w_class = 200
STR.max_items = 25
STR.insert_preposition = "in"
- STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube))
+ STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube, /obj/item/organ, /obj/item/reagent_containers/food/snacks/meat/slab, /obj/item/bodypart))
+ STR.cant_hold = typecacheof(list(/obj/item/organ/brain, /obj/item/organ/liver/cybernetic, /obj/item/organ/heart/cybernetic, /obj/item/organ/lungs/cybernetic, /obj/item/organ/tongue/cybernetic, /obj/item/organ/ears/cybernetic, /obj/item/organ/eyes/robotic, /obj/item/organ/cyberimp))
/obj/item/storage/bag/bio/holding
name = "bio bag of holding"
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index 8c0f315ca3..add2d12ee6 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -138,7 +138,7 @@
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
- return FALSE
+ return TRUE
return ..()
/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 1517b30d9c..88c8d51479 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -2,38 +2,21 @@
name = "\proper captain's locker"
req_access = list(ACCESS_CAPTAIN)
icon_state = "cap"
-/obj/structure/closet/secure_closet/captains/PopulateContents()
+/obj/structure/closet/secure_closet/captains/PopulateContents() //Excess clothing and such can be found in the Captain's Wardrobe. You can also find this file in code/modules/vending/wardrobes.
..()
- new /obj/item/clothing/suit/hooded/wintercoat/captain(src)
- new /obj/item/storage/backpack/captain(src)
- new /obj/item/storage/backpack/satchel/cap(src)
- new /obj/item/storage/backpack/duffelbag/captain(src)
- new /obj/item/clothing/neck/cloak/cap(src)
- new /obj/item/clothing/neck/petcollar(src)
+ new /obj/item/clothing/neck/petcollar(src) //I considered removing the pet stuff too but eh, who knows. We might get Renault back. Plus I guess you could use that collar for... other means. Aren't you supposed to be guarding the disk?
new /obj/item/pet_carrier(src)
- new /obj/item/clothing/shoes/sneakers/brown(src)
- new /obj/item/clothing/under/rank/captain(src)
- new /obj/item/clothing/under/rank/captain/skirt(src)
new /obj/item/clothing/suit/armor/vest/capcarapace(src)
- new /obj/item/clothing/head/caphat(src)
- new /obj/item/clothing/under/captainparade(src)
new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src)
- new /obj/item/clothing/head/caphat/parade(src)
- new /obj/item/clothing/head/caphat/beret(src)
- new /obj/item/clothing/suit/captunic(src)
- new /obj/item/clothing/under/rank/captain/femformal(src) //citadel edit
new /obj/item/clothing/head/crown/fancy(src)
new /obj/item/cartridge/captain(src)
new /obj/item/storage/box/silver_ids(src)
new /obj/item/radio/headset/heads/captain/alt(src)
new /obj/item/radio/headset/heads/captain(src)
- new /obj/item/clothing/glasses/sunglasses/gar/supergar(src)
- new /obj/item/clothing/gloves/color/captain(src)
new /obj/item/storage/belt/sabre(src)
new /obj/item/gun/energy/e_gun(src)
new /obj/item/door_remote/captain(src)
new /obj/item/storage/photo_album/Captain(src)
- new /obj/item/clothing/head/caphat/beret/white(src)
/obj/structure/closet/secure_closet/hop
name = "\proper head of personnel's locker"
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index a6c0b22ad7..b14303984e 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -47,6 +47,12 @@
if(manifest)
tear_manifest(user)
+/obj/structure/closet/crate/tool_interact(obj/item/W, mob/user)
+ if(W.tool_behaviour == TOOL_WIRECUTTER && manifest)
+ tear_manifest(user)
+ return TRUE
+ return ..()
+
/obj/structure/closet/crate/open(mob/living/user)
. = ..()
if(. && manifest)
diff --git a/code/game/objects/structures/crates_lockers/crates/bins.dm b/code/game/objects/structures/crates_lockers/crates/bins.dm
index 7646fdb35f..bc3697a550 100644
--- a/code/game/objects/structures/crates_lockers/crates/bins.dm
+++ b/code/game/objects/structures/crates_lockers/crates/bins.dm
@@ -4,8 +4,8 @@
icon_state = "largebins"
open_sound = 'sound/effects/bin_open.ogg'
close_sound = 'sound/effects/bin_close.ogg'
- material_drop = /obj/item/stack/sheet/plastic
- material_drop_amount = 40
+ material_drop = /obj/item/stack/sheet/metal
+ material_drop_amount = 10
anchored = TRUE
horizontal = FALSE
delivery_icon = null
diff --git a/code/game/world.dm b/code/game/world.dm
index 6b380e0f94..77361310b8 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -10,6 +10,10 @@ GLOBAL_LIST(topic_status_cache)
/world/New()
enable_debugger()
+#if DM_VERSION >= 513 && DM_BUILD >= 1506
+ world.Profile(PROFILE_START)
+#endif
+
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
SetupExternalRSC()
@@ -18,7 +22,7 @@ GLOBAL_LIST(topic_status_cache)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
- TgsNew()
+ TgsNew(minimum_required_security_level = TGS_SECURITY_TRUSTED)
GLOB.revdata = new
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index ad2eeb7289..cf6d54336e 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -1058,7 +1058,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
set name = "Start Line Profiling"
set desc = "Starts tracking line by line profiling for code lines that support it"
- PROFILE_START
+ LINE_PROFILE_START
message_admins("[key_name_admin(src)] started line by line profiling.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Line Profiling")
@@ -1069,7 +1069,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
set name = "Stops Line Profiling"
set desc = "Stops tracking line by line profiling for code lines that support it"
- PROFILE_STOP
+ LINE_PROFILE_STOP
message_admins("[key_name_admin(src)] stopped line by line profiling.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop Line Profiling")
diff --git a/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm b/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm
index 501bcdc1c4..7be9ef039b 100644
--- a/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm
+++ b/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm
@@ -68,8 +68,8 @@
/obj/item/clockwork/replica_fabricator/pre_attack(atom/target, mob/living/user, params)
if(!target || !user || !is_servant_of_ratvar(user) || istype(target, /obj/item/storage))
- return TRUE
- return fabricate(target, user)
+ return ..()
+ return !fabricate(target, user)
//A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE
/obj/item/clockwork/replica_fabricator/proc/fabricate(atom/target, mob/living/user, silent, no_table_check)
diff --git a/code/modules/antagonists/cult/ritual.dm b/code/modules/antagonists/cult/ritual.dm
index ba2a96289d..e92f562db3 100644
--- a/code/modules/antagonists/cult/ritual.dm
+++ b/code/modules/antagonists/cult/ritual.dm
@@ -116,7 +116,7 @@ This file contains the cult dagger and rune list code
if(user.blood_volume)
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
- if(istype(get_turf(user), /turf/open/floor/engine/cult))
+ if(istype(get_turf(user), /turf/open/floor/engine/cult) && !(ispath(rune_to_scribe, /obj/effect/rune/narsie)))
scribe_mod *= 0.5
if(!do_after(user, scribe_mod, target = get_turf(user)))
for(var/V in shields)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index fe3b5a3aeb..0d815703c5 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -381,6 +381,7 @@
playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 50, FALSE)
set_active()
update_ui_mode()
+ . = TRUE
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
if("anchor")
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 539b5ce57a..1e8cce32c0 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -251,6 +251,7 @@
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
+ reagent_transfer = 0
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user)
diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm
index 3562350473..c4bb6e8cdd 100644
--- a/code/modules/awaymissions/capture_the_flag.dm
+++ b/code/modules/awaymissions/capture_the_flag.dm
@@ -466,11 +466,13 @@
// MELEE GANG
/obj/item/claymore/ctf
- slot_flags = SLOT_BACK
+ slot_flags = ITEM_SLOT_BACK
+ armour_penetration = 100
+ total_mass = 1
/obj/item/claymore/ctf/pre_attack(atom/target, mob/user, params)
if(!is_ctf_target(target))
- return FALSE
+ return TRUE
return ..()
/obj/item/claymore/ctf/dropped()
diff --git a/code/modules/cargo/bounties/reagent.dm b/code/modules/cargo/bounties/reagent.dm
index ca76c80df7..391b4ff464 100644
--- a/code/modules/cargo/bounties/reagent.dm
+++ b/code/modules/cargo/bounties/reagent.dm
@@ -105,6 +105,7 @@ datum/bounty/reagent/complex_drink/New()
/datum/reagent/consumable/ethanol/hearty_punch,\
/datum/reagent/consumable/ethanol/manhattan_proj,\
/datum/reagent/consumable/ethanol/narsour,\
+ /datum/reagent/consumable/ethanol/cogchamp,\
/datum/reagent/consumable/ethanol/neurotoxin,\
/datum/reagent/consumable/ethanol/patron,\
/datum/reagent/consumable/ethanol/quadruple_sec,\
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index b8541dcd33..bf67286f11 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -6,7 +6,7 @@
k_elasticity = 0
unit_name = "crate"
export_types = list(/obj/structure/closet/crate)
- exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden)
+ exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden, /obj/structure/closet/crate/bin)
/datum/export/large/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much.
. = ..()
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index 7c1121d137..ab2f89ffc4 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -272,7 +272,7 @@
/datum/outfit/wizard
name = "Blue Wizard"
- uniform = /obj/item/clothing/under/color/lightpurple
+ uniform = /obj/item/clothing/under/color/lightpurple/trackless
suit = /obj/item/clothing/suit/wizrobe
shoes = /obj/item/clothing/shoes/sandal/magic
ears = /obj/item/radio/headset
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index e9b96af8eb..b61918c4b8 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -325,17 +325,16 @@
body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/ran
- name = "Shikigami costume"
+ name = "shikigami costume"
desc = "A costume that looks like a certain shikigami, is super fluffy."
icon_state = "ran_suit"
item_state = "ran_suit"
body_parts_covered = CHEST|GROIN|LEGS
flags_inv = HIDEJUMPSUIT|HIDETAUR
heat_protection = CHEST|GROIN|LEGS //fluffy tails!
-//2061
/obj/item/clothing/head/ran
- name = "Shikigami hat"
+ name = "shikigami hat"
desc = "A hat that looks like it keeps any fluffy ears contained super warm, has little charms over it."
icon_state = "ran_hat"
item_state = "ran_hat"
@@ -880,7 +879,6 @@
blood_overlay_type = "armor"
body_parts_covered = CHEST
resistance_flags = NONE
- mutantrace_variation = NONE
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 5, "bio" = 0, "rad" = 0, "fire" = -5, "acid" = -15) //nylon sucks against acid
/obj/item/clothing/suit/assu_suit
diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm
index 12cafb7e46..a18b7e5817 100644
--- a/code/modules/clothing/under/color.dm
+++ b/code/modules/clothing/under/color.dm
@@ -41,6 +41,10 @@
item_color = "black"
resistance_flags = NONE
+/obj/item/clothing/under/color/black/trackless
+ desc = "A black jumpsuit that has its sensors removed."
+ has_sensor = NO_SENSORS
+
/obj/item/clothing/under/skirt/color/black
name = "black jumpskirt"
icon_state = "black_skirt"
@@ -189,13 +193,16 @@
item_state = "b_suit"
item_color = "teal_skirt"
-
/obj/item/clothing/under/color/lightpurple
name = "purple jumpsuit"
icon_state = "lightpurple"
item_state = "p_suit"
item_color = "lightpurple"
+/obj/item/clothing/under/color/lightpurple/trackless
+ desc = "A magically colored jumpsuit. No sensors are attached!"
+ has_sensor = NO_SENSORS
+
/obj/item/clothing/under/skirt/color/lightpurple
name = "lightpurple jumpskirt"
icon_state = "lightpurple_skirt"
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
index 418b75e1ce..6a3d2c2233 100644
--- a/code/modules/error_handler/error_handler.dm
+++ b/code/modules/error_handler/error_handler.dm
@@ -19,6 +19,13 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
log_world("The bug with recursion runtimes has been fixed. Please remove the snowflake check from world/Error in [__FILE__]:[__LINE__]")
return //this will never happen.
+ else if(copytext(E.name,1,18) == "Out of resources!")//18 == length() of that string + 1
+ log_world("BYOND out of memory. Restarting")
+ log_game("BYOND out of memory. Restarting")
+ TgsEndProcess()
+ Reboot(reason = 1)
+ return ..()
+
if (islist(stack_trace_storage))
for (var/line in splittext(E.desc, "\n"))
if (text2ascii(line) != 32)
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index cd5c9d850b..7bd5e66430 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -107,9 +107,9 @@
smash(hit_atom, throwingdatum?.thrower, TRUE)
/obj/item/reagent_containers/food/drinks/proc/smash(atom/target, mob/thrower, ranged = FALSE)
- if(!isGlass)
+ if(!isGlass && !istype(src, /obj/item/reagent_containers/food/drinks/bottle)) //I don't like this but I also don't want to rework drink container hierarchy
return
- if(QDELING(src) || !target) //Invalid loc
+ if(QDELING(src) || (ranged && !target))
return
if(bartender_check(target) && ranged)
return
@@ -126,12 +126,69 @@
B.transform = M
B.pixel_x = rand(-12, 12)
B.pixel_y = rand(-12, 12)
- if(prob(33))
- new/obj/item/shard(drop_location())
- playsound(src, "shatter", 70, 1)
+ if(isGlass)
+ playsound(src, "shatter", 70, 1)
+ if(prob(33))
+ new/obj/item/shard(drop_location())
+ else
+ B.force = 0
+ B.throwforce = 0
+ B.desc = "A carton with the bottom half burst open. Might give you a papercut."
transfer_fingerprints_to(B)
qdel(src)
+/obj/item/reagent_containers/food/drinks/MouseDrop(atom/over, atom/src_location, atom/over_location, src_control, over_control, params)
+ var/mob/user = usr
+ . = ..()
+ if (!istype(src_location) || !istype(over_location))
+ return
+ if (!user || user.incapacitated() || !user.Adjacent(src))
+ return
+ if (!(locate(/obj/structure/table) in src_location) || !(locate(/obj/structure/table) in over_location))
+ return
+
+ //Are we an expert slider?
+ var/datum/action/innate/D = get_action_of_type(user, /datum/action/innate/drink_fling)
+ if(!D?.active)
+ if (!src_location.Adjacent(over_location)) // Regular users can only do short slides.
+ return
+ if (prob(10))
+ user.visible_message("\The [user] tries to slide \the [src] down the table, but fails miserably.", "You fail to slide \the [src] down the table!")
+ smash(over_location, user, FALSE)
+ return
+ user.visible_message("\The [user] slides \the [src] down the table.", "You slide \the [src] down the table!")
+ forceMove(over_location)
+ return
+ var/distance = MANHATTAN_DISTANCE(over_location, src)
+ if (distance >= 8 || distance == 0) // More than a full screen to go, or trying to slide to the same tile
+ return
+
+ // Geometrically checking if we're on a straight line.
+ var/datum/vector/V = atoms2vector(src, over_location)
+ var/datum/vector/V_norm = V.duplicate()
+ V_norm.normalize()
+ if (!V_norm.is_integer())
+ return // Only a cardinal vector (north, south, east, west) can pass this test
+
+ // Checks if there's tables on the path.
+ var/turf/dest = get_translated_turf(V)
+ var/turf/temp_turf = src_location
+
+ do
+ temp_turf = temp_turf.get_translated_turf(V_norm)
+ if (!locate(/obj/structure/table) in temp_turf)
+ var/datum/vector/V2 = atoms2vector(src, temp_turf)
+ vector_translate(V2, 0.1 SECONDS)
+ user.visible_message("\The [user] slides \the [src] down the table... and straight into the ground!", "You slide \the [src] down the table, and straight into the ground!")
+ smash(over_location, user, FALSE)
+ return
+ while (temp_turf != dest)
+
+ vector_translate(V, 0.1 SECONDS)
+ user.visible_message("\The [user] expertly slides \the [src] down the table.", "You slide \the [src] down the table. What a pro.")
+ return
+
+
////////////////////////////////////////////////////////////////////////////////
/// Drinks. END
////////////////////////////////////////////////////////////////////////////////
@@ -223,19 +280,24 @@
spillable = TRUE
/obj/item/reagent_containers/food/drinks/mug/on_reagent_change(changetype)
+ cut_overlays()
if(reagents.total_volume)
- icon_state = "tea"
+ var/mutable_appearance/MA = mutable_appearance(icon,"mugoverlay")
+ MA.color = mix_color_from_reagents(reagents.reagent_list)
+ add_overlay(MA)
else
icon_state = "tea_empty"
/obj/item/reagent_containers/food/drinks/mug/tea
name = "Duke Purple tea"
+ icon_state = "tea"
desc = "An insult to Duke Purple is an insult to the Space Queen! Any proper gentleman will fight you, if you sully this tea."
list_reagents = list(/datum/reagent/consumable/tea = 30)
/obj/item/reagent_containers/food/drinks/mug/coco
name = "Dutch hot coco"
desc = "Made in Space South America."
+ icon_state = "coco"
list_reagents = list(/datum/reagent/consumable/hot_coco = 30, /datum/reagent/consumable/sugar = 5)
foodtype = SUGAR
resistance_flags = FREEZE_PROOF
@@ -289,28 +351,6 @@
icon_state = "juicebox"
volume = 15 //I figure if you have to craft these it should at least be slightly better than something you can get for free from a watercooler
-/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/smash(atom/target, mob/thrower, ranged = FALSE)
- if(bartender_check(target) && ranged)
- return
- var/obj/item/broken_bottle/B = new (loc)
- B.icon_state = icon_state
- var/icon/I = new('icons/obj/drinks.dmi', src.icon_state)
- I.Blend(B.broken_outline, ICON_OVERLAY, rand(5), 1)
- I.SwapColor(rgb(255, 0, 220, 255), rgb(0, 0, 0, 0))
- B.icon = I
- B.name = "broken [name]"
- B.force = 0
- B.throwforce = 0
- B.desc = "A carton with the bottom half burst open. Might give you a papercut."
- if(ranged)
- var/matrix/M = matrix(B.transform)
- M.Turn(rand(-170, 170))
- B.transform = M
- B.pixel_x = rand(-12, 12)
- B.pixel_y = rand(-12, 12)
- transfer_fingerprints_to(B)
- qdel(src)
-
/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/on_reagent_change(changetype)
if (reagents.reagent_list.len)
switch(reagents.get_master_reagent_id())
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index 7f2b24ca11..07026e79de 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -16,40 +16,6 @@
isGlass = TRUE
foodtype = ALCOHOL
-
-/obj/item/reagent_containers/food/drinks/bottle/smash(mob/living/target, mob/thrower, ranged = FALSE)
- //Creates a shattering noise and replaces the bottle with a broken_bottle
- if(bartender_check(target) && ranged)
- return
- var/obj/item/broken_bottle/B = new (loc)
- if(!ranged)
- thrower.put_in_hands(B)
- else
- var/matrix/M = matrix(B.transform)
- M.Turn(rand(-170, 170))
- B.transform = M
- B.pixel_x = rand(-12, 12)
- B.pixel_y = rand(-12, 12)
- B.icon_state = icon_state
-
- var/icon/I = new('icons/obj/drinks.dmi', src.icon_state)
- I.Blend(B.broken_outline, ICON_OVERLAY, rand(5), 1)
- I.SwapColor(rgb(255, 0, 220, 255), rgb(0, 0, 0, 0))
- B.icon = I
-
- if(isGlass)
- if(prob(33))
- new/obj/item/shard(drop_location())
- playsound(src, "shatter", 70, 1)
- else
- B.force = 0
- B.throwforce = 0
- B.desc = "A carton with the bottom half burst open. Might give you a papercut."
- B.name = "broken [name]"
- transfer_fingerprints_to(B)
-
- qdel(src)
-
/obj/item/reagent_containers/food/drinks/bottle/attack(mob/living/target, mob/living/user)
if(!target)
@@ -109,7 +75,7 @@
//Keeping this here for now, I'll ask if I should keep it here.
/obj/item/broken_bottle
name = "broken bottle"
- desc = "A bottle with a sharp broken bottom."
+ desc = "A shattered glass container with sharp edges."
icon = 'icons/obj/drinks.dmi'
icon_state = "broken_bottle"
force = 9
diff --git a/code/modules/food_and_drinks/food/snacks_egg.dm b/code/modules/food_and_drinks/food/snacks_egg.dm
index f01966a0ff..c65e42c0e4 100644
--- a/code/modules/food_and_drinks/food/snacks_egg.dm
+++ b/code/modules/food_and_drinks/food/snacks_egg.dm
@@ -96,6 +96,16 @@
tastes = list("egg" = 4, "salt" = 1, "pepper" = 1)
foodtype = MEAT | FRIED | BREAKFAST
+/obj/item/reagent_containers/food/snacks/baconegg
+ name = "bacon and eggs"
+ desc = "A fried egg with a side of bacon. Delicious!"
+ icon_state = "baconegg"
+ bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1)
+ bitesize = 1
+ filling_color = "#FFFFF0"
+ tastes = list("egg" = 2, "bacon" = 2, "salt" = 1, "pepper" = 1)
+ foodtype = MEAT | FRIED | BREAKFAST
+
/obj/item/reagent_containers/food/snacks/boiledegg
name = "boiled egg"
desc = "A hard boiled egg."
diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm
index 1c8b70dc13..de1f00ae5d 100644
--- a/code/modules/food_and_drinks/food/snacks_pastry.dm
+++ b/code/modules/food_and_drinks/food/snacks_pastry.dm
@@ -353,6 +353,13 @@
tastes = list("muffin" = 3, "spookiness" = 1)
foodtype = GRAIN | FRUIT | SUGAR | BREAKFAST
+/obj/item/reagent_containers/food/snacks/muffin/poppy
+ name = "poppy muffin"
+ icon_state = "poppymuffin"
+ desc = "A classic lemon poppy seed muffin. Do not consume prior to drug testing."
+ tastes = list("muffin" = 3, "lemon" = 1, "seeds" = 1)
+ foodtype = GRAIN | SUGAR | BREAKFAST
+
/obj/item/reagent_containers/food/snacks/chawanmushi
name = "chawanmushi"
desc = "A legendary egg custard that makes friends out of enemies. Probably too hot for a cat to eat."
diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
index 9df650500a..c94118ddf1 100644
--- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
@@ -585,6 +585,14 @@
mix_message = "The mixture develops a sinister glow."
mix_sound = 'sound/effects/singlebeat.ogg'
+/datum/chemical_reaction/cogchamp
+ name = "CogChamp"
+ id = /datum/reagent/consumable/ethanol/cogchamp
+ results = list(/datum/reagent/consumable/ethanol/cogchamp = 3)
+ required_reagents = list(/datum/reagent/consumable/ethanol/cognac = 1, /datum/reagent/fuel = 1, /datum/reagent/consumable/ethanol/screwdrivercocktail = 1)
+ mix_message = "You hear faint sounds of gears turning as it mixes."
+ mix_sound = 'sound/effects/clockcult_gateway_closing.ogg'
+
/datum/chemical_reaction/quadruplesec
name = "Quadruple Sec"
id = /datum/reagent/consumable/ethanol/quadruple_sec
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm
index 6d495d1c79..c049cd7563 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm
@@ -13,6 +13,15 @@
result = /obj/item/reagent_containers/food/snacks/friedegg
subcategory = CAT_EGG
+/datum/crafting_recipe/food/baconegg
+ name = "Bacon and Eggs"
+ reqs = list(
+ /obj/item/reagent_containers/food/snacks/friedegg = 1,
+ /obj/item/reagent_containers/food/snacks/meat/bacon = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/baconegg
+ subcategory = CAT_EGG
+
/datum/crafting_recipe/food/omelette
name = "Omelette"
reqs = list(
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
index b545702e3e..62b8509eb1 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
@@ -377,6 +377,17 @@ datum/crafting_recipe/food/donut/meat
result = /obj/item/reagent_containers/food/snacks/muffin/booberry
subcategory = CAT_PASTRY
+/datum/crafting_recipe/food/poppymuffin
+ name = "Poppy muffin"
+ reqs = list(
+ /datum/reagent/consumable/milk = 5,
+ /obj/item/reagent_containers/food/snacks/pastrybase = 1,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/lemon = 1,
+ /obj/item/seeds/poppy = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/muffin/poppy
+ subcategory = CAT_PASTRY
+
/datum/crafting_recipe/food/chawanmushi
name = "Chawanmushi"
reqs = list(
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index 0ace449757..cffeceea9c 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -28,3 +28,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)
+ . = ..()
+ var/datum/action/innate/drink_fling/D = new
+ D.Grant(H)
diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm
index f63b459f10..133cb41c33 100644
--- a/code/modules/mining/equipment/resonator.dm
+++ b/code/modules/mining/equipment/resonator.dm
@@ -46,7 +46,7 @@
/obj/item/resonator/pre_attack(atom/target, mob/user, params)
if(check_allowed_items(target, 1))
CreateResonance(target, user)
- return TRUE
+ return ..()
//resonance field, crushes rock, damages mobs
/obj/effect/temp_visual/resonance
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 1ddb3a3189..ac4cee8c42 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -93,7 +93,7 @@
req_access = list()
circuit = /obj/item/circuitboard/computer/mining_shuttle/common
shuttleId = "mining_common"
- possible_destinations = "whiteship_home;lavaland_common_away;landing_zone_dock;mining_public"
+ possible_destinations = "lavaland_common_away;commonmining_home"
/**********************Mining car (Crate like thing, not the rail car)**************************/
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm
index 241ec8118e..92bc8e407b 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm
@@ -16,7 +16,7 @@
from doing this unless you absolutely know what you are doing, and have defined a
conversion in savefile.dm
*/
-/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female,var/roundstart = FALSE)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
+/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female, roundstart = FALSE, skip_prototype = TRUE)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
if(!istype(L))
L = list()
if(!istype(male))
@@ -25,7 +25,7 @@
female = list()
for(var/path in typesof(prototype))
- if(path == prototype)
+ if(path == prototype && skip_prototype)
continue
if(roundstart)
var/datum/sprite_accessory/P = path
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 534509523a..1be9a0d72d 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -581,6 +581,9 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
if(cultslurring)
cultslurring = max(cultslurring-1, 0)
+ if(clockcultslurring)
+ clockcultslurring = max(clockcultslurring-1, 0)
+
if(silent)
silent = max(silent-1, 0)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 84f0004805..031a2e9b94 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -89,6 +89,7 @@
var/stuttering = 0
var/slurring = 0
var/cultslurring = 0
+ var/clockcultslurring = 0
var/derpspeech = 0
var/list/implants = null
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index b20511986d..b0f627f82b 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -354,6 +354,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
if(cultslurring)
message = cultslur(message)
+
+ if(clockcultslurring)
+ message = CLOCK_CULT_SLUR(message)
message = capitalize(message)
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 05b2bc77ba..d8e910642d 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -1,3 +1,4 @@
+#define PAI_EMP_SILENCE_DURATION 3 MINUTES
/mob/living/silicon/pai/blob_act(obj/structure/blob/B)
return FALSE
@@ -8,7 +9,7 @@
return
take_holo_damage(50/severity)
Knockdown(400/severity)
- silent = max((3 MINUTES)/severity, silent)
+ silent = max(silent, (PAI_EMP_SILENCE_DURATION) / SSmobs.wait / severity)
if(holoform)
fold_in(force = TRUE)
emitter_next_use = world.time + emitter_emp_cd
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index f02d21c2af..873051c336 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -19,7 +19,7 @@
/mob/living/simple_animal/pet/dog/ComponentInitialize()
. = ..()
- AddElement(/datum/element/wuv, "yaps_happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
+ AddElement(/datum/element/wuv, "yaps happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
AddElement(/datum/element/mob_holder, held_icon)
//Corgis and pugs are now under one dog subtype
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index f4133bcf9d..17e0d3d25b 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -147,6 +147,10 @@
. += newletter
return sanitize(.)
+//Ratvarian Slurring!
+
+#define CLOCK_CULT_SLUR(phrase) sanitize(text2ratvar(phrase))
+
///Adds stuttering to the message passed in
/proc/stutter(phrase)
phrase = html_decode(phrase)
@@ -420,13 +424,10 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
return TRUE
/mob/proc/getImplant(type)
- if (!istype(src,/mob/living))
- return
- var/mob/living/L = src
- for (var/I in L.implants)
- if (istype(I,type))
- return I
- return null
+ return
+
+/mob/living/getImplant(type)
+ return locate(type) in implants
/proc/canGhostWrite(var/mob/A, var/obj/target, var/desc="", var/allow_all=FALSE)
if(allow_all & TRUE)
diff --git a/code/modules/ninja/outfit.dm b/code/modules/ninja/outfit.dm
index 7ee71db1f1..323deed5c4 100644
--- a/code/modules/ninja/outfit.dm
+++ b/code/modules/ninja/outfit.dm
@@ -1,6 +1,6 @@
/datum/outfit/ninja
name = "Space Ninja"
- uniform = /obj/item/clothing/under/color/black
+ uniform = /obj/item/clothing/under/color/black/trackless
suit = /obj/item/clothing/suit/space/space_ninja
glasses = /obj/item/clothing/glasses/night
mask = /obj/item/clothing/mask/gas/space_ninja
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index b15b701272..982afa49c8 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -107,7 +107,7 @@
var/icon_update_needed = FALSE
var/obj/machinery/computer/apc_control/remote_control = null
var/mob/living/carbon/hijacker
- var/hijackerlast = FALSE
+ var/hijackerlast = TRUE
/obj/machinery/power/apc/unlocked
locked = FALSE
@@ -399,14 +399,14 @@
if (hijacker)
var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack)
hijackerreturn = H && !H.stealthmode
- if(last_update_state == update_state && last_update_overlay == update_overlay && hijackerreturn ? hijackerlast : !hijackerlast)
+ if(last_update_state == update_state && last_update_overlay == update_overlay && hijackerreturn == hijackerlast)
return 0
if(last_update_state != update_state)
results += 1
- if(last_update_overlay != update_overlay || hijackerreturn ? !hijackerlast : hijackerlast)
+ if(last_update_overlay != update_overlay || hijackerreturn != hijackerlast)
results += 2
- if (hijackerreturn ? !hijackerlast : hijackerlast)
- hijackerlast = hijackerreturn ? TRUE : FALSE
+ if (hijackerreturn != hijackerlast)
+ hijackerlast = hijackerreturn
return results
// Used in process so it doesn't update the icon too much
diff --git a/code/modules/projectiles/ammunition/energy/gravity.dm b/code/modules/projectiles/ammunition/energy/gravity.dm
index eabb4f66d7..be7b618803 100644
--- a/code/modules/projectiles/ammunition/energy/gravity.dm
+++ b/code/modules/projectiles/ammunition/energy/gravity.dm
@@ -15,15 +15,15 @@
. = ..()
/obj/item/ammo_casing/energy/gravity/repulse
- projectile_type = /obj/item/projectile/gravityrepulse
+ projectile_type = /obj/item/projectile/gravity/repulse
select_name = "repulse"
/obj/item/ammo_casing/energy/gravity/attract
- projectile_type = /obj/item/projectile/gravityattract
+ projectile_type = /obj/item/projectile/gravity/attract
select_name = "attract"
/obj/item/ammo_casing/energy/gravity/chaos
- projectile_type = /obj/item/projectile/gravitychaos
+ projectile_type = /obj/item/projectile/gravity/chaos
select_name = "chaos"
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index e1b62620c7..be43bc48fd 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -150,7 +150,7 @@
if(istype(v, /obj/item/ammo_casing/energy)) //already set
ammo_type[v] = isnull(user_can_select)? TRUE : user_can_select
else
- C = new v //if you put non energycasing/type stuff in here you deserve the runtime
+ C = new v(src) //if you put non energycasing/type stuff in here you deserve the runtime
ammo_type[i] = C
ammo_type[C] = isnull(user_can_select)? TRUE : user_can_select
set_firemode_index(initial(current_firemode_index))
diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm
index 0465a611cd..70551d07b6 100644
--- a/code/modules/projectiles/pins.dm
+++ b/code/modules/projectiles/pins.dm
@@ -69,13 +69,10 @@
if(gun)
qdel(gun)
-
-
/obj/item/firing_pin/magic
name = "magic crystal shard"
desc = "A small enchanted shard which allows magical weapons to fire."
-
// Test pin, works only near firing range.
/obj/item/firing_pin/test_range
name = "test-range firing pin"
@@ -229,3 +226,16 @@
if(gun)
gun.pin = null
return ..()
+
+//Station Locked
+
+/obj/item/firing_pin/away
+ name = "station locked pin"
+ desc = "A firing pin that only will fire when off the station."
+
+/obj/item/firing_pin/away/pin_auth(mob/living/user)
+ var/area/station_area = get_area(src)
+ if(!station_area || is_station_level(station_area.z))
+ to_chat(user, "The pin beeps, refusing to fire.")
+ return FALSE
+ return TRUE
diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm
index 02ec167b80..c28d85511e 100644
--- a/code/modules/projectiles/projectile/bullets/shotgun.dm
+++ b/code/modules/projectiles/projectile/bullets/shotgun.dm
@@ -61,7 +61,7 @@
/obj/item/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
- damage = 10
+ damage = 12.5
/obj/item/projectile/bullet/pellet/shotgun_rubbershot
name = "rubbershot pellet"
diff --git a/code/modules/projectiles/projectile/special/gravity.dm b/code/modules/projectiles/projectile/special/gravity.dm
index 0ef9bc6653..ba21ecb28c 100644
--- a/code/modules/projectiles/projectile/special/gravity.dm
+++ b/code/modules/projectiles/projectile/special/gravity.dm
@@ -1,90 +1,65 @@
-/obj/item/projectile/gravityrepulse
+/obj/item/projectile/gravity
+ name = "gravity bolt"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "chronofield"
+ hitsound = 'sound/weapons/wave.ogg'
+ damage = 0
+ damage_type = BRUTE
+ nodamage = TRUE
+ var/power = 4
+ var/list/thrown = list() //normally we wouldn't need this but incase on_hit is called multiple times, yeah.. this is a good idea.
+
+/obj/item/projectile/gravity/Initialize(mapload)
+ . = ..()
+ var/obj/item/ammo_casing/energy/gravity/G = loc
+ if(istype(G))
+ power = min(G.gun.power, 15)
+
+/obj/item/projectile/gravity/on_hit()
+ . = ..()
+ var/turf/T = get_turf(src)
+ var/list/tothrow = list()
+ var/list/cachedrange = range(T, power)
+ for(var/mob/M in cachedrange)
+ tothrow += M
+ for(var/obj/O in cachedrange)
+ tothrow += O
+ var/safety = 50
+ for(var/i in tothrow)
+ if(!safety)
+ break
+ var/atom/movable/AM = i
+ if((AM == src) || (AM == firer) || (AM.move_resist > MOVE_FORCE_EXTREMELY_STRONG) || AM.anchored || thrown[AM])
+ continue
+ thrown[AM] = TRUE
+ safety--
+ var/target = get_target(T, AM)
+ if(!target)
+ return FALSE
+ AM.throw_at(target, power + 1, 1)
+ for(var/turf/F in range(T,power))
+ new /obj/effect/temp_visual/gravpush(F)
+
+/obj/item/projectile/gravity/proc/get_target(turf/origin, atom/movable/AM)
+ return origin
+
+/obj/item/projectile/gravity/repulse
name = "repulsion bolt"
- icon = 'icons/effects/effects.dmi'
- icon_state = "chronofield"
- hitsound = 'sound/weapons/wave.ogg'
- damage = 0
- damage_type = BRUTE
- nodamage = 1
color = "#33CCFF"
- var/turf/T
- var/power = 4
- var/list/thrown_items = list()
-/obj/item/projectile/gravityrepulse/Initialize()
- . = ..()
- var/obj/item/ammo_casing/energy/gravity/repulse/C = loc
- if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
- power = min(C.gun.power, 15)
+/obj/item/projectile/gravity/repulse/get_target(turf/origin, atom/movable/AM)
+ return get_turf_in_angle(Get_Angle(origin, AM), origin, power)
-/obj/item/projectile/gravityrepulse/on_hit()
- . = ..()
- T = get_turf(src)
- for(var/atom/movable/A in range(T, power))
- if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
- continue
- var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
- A.throw_at(throwtarget,power+1,1)
- thrown_items[A] = A
- for(var/turf/F in range(T,power))
- new /obj/effect/temp_visual/gravpush(F)
-
-/obj/item/projectile/gravityattract
+/obj/item/projectile/gravity/attract
name = "attraction bolt"
- icon = 'icons/effects/effects.dmi'
- icon_state = "chronofield"
- hitsound = 'sound/weapons/wave.ogg'
- damage = 0
- damage_type = BRUTE
- nodamage = 1
color = "#FF6600"
- var/turf/T
- var/power = 4
- var/list/thrown_items = list()
-/obj/item/projectile/gravityattract/Initialize()
- . = ..()
- var/obj/item/ammo_casing/energy/gravity/attract/C = loc
- if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
- power = min(C.gun.power, 15)
+/obj/item/projectile/gravity/attract/get_target(turf/origin, atom/movable/AM)
+ return origin
-/obj/item/projectile/gravityattract/on_hit()
- . = ..()
- T = get_turf(src)
- for(var/atom/movable/A in range(T, power))
- if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
- continue
- A.throw_at(T, power+1, 1)
- thrown_items[A] = A
- for(var/turf/F in range(T,power))
- new /obj/effect/temp_visual/gravpush(F)
-
-/obj/item/projectile/gravitychaos
+/obj/item/projectile/gravity/chaos
name = "gravitational blast"
- icon = 'icons/effects/effects.dmi'
- icon_state = "chronofield"
- hitsound = 'sound/weapons/wave.ogg'
- damage = 0
- damage_type = BRUTE
- nodamage = 1
color = "#101010"
- var/turf/T
- var/power = 4
- var/list/thrown_items = list()
-/obj/item/projectile/gravitychaos/Initialize()
- . = ..()
- var/obj/item/ammo_casing/energy/gravity/chaos/C = loc
- if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
- power = min(C.gun.power, 15)
-
-/obj/item/projectile/gravitychaos/on_hit()
- . = ..()
- T = get_turf(src)
- for(var/atom/movable/A in range(T, power))
- if(A == src|| (firer && A == src.firer) || A.anchored || thrown_items[A])
- continue
- A.throw_at(get_edge_target_turf(A, pick(GLOB.cardinals)), power+1, 1)
- thrown_items[A] = A
- for(var/turf/Z in range(T,power))
- new /obj/effect/temp_visual/gravpush(Z)
+/obj/item/projectile/gravity/chaos/get_target(turf/origin, atom/movable/AM)
+ return get_turf_in_angle(rand(0, 359), origin, power)
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 7a3a7eeec2..ccdbbcc915 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -1513,6 +1513,23 @@ All effects don't start immediately, but rather get worse over time; the rate is
M.stuttering = min(M.stuttering + 3, 3)
..()
+/datum/reagent/consumable/ethanol/cogchamp
+ name = "CogChamp"
+ description = "Now you can fill yourself with the power of Ratvar!"
+ color = rgb(255, 201, 49)
+ boozepwr = 10
+ quality = DRINK_FANTASTIC
+ taste_description = "a brass taste with a hint of oil"
+ glass_icon_state = "cogchamp"
+ glass_name = "CogChamp"
+ glass_desc = "Not even Ratvar's Four Generals could withstand this! Qevax Jryy!"
+ value = 8.13
+
+/datum/reagent/consumable/ethanol/cogchamp/on_mob_life(mob/living/carbon/M)
+ M.clockcultslurring = min(M.clockcultslurring + 3, 3)
+ M.stuttering = min(M.stuttering + 3, 3)
+ ..()
+
/datum/reagent/consumable/ethanol/triple_sec
name = "Triple Sec"
description = "A sweet and vibrant orange liqueur."
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index b6cb387b81..52eee9f8ea 100644
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -383,7 +383,7 @@
name = "Hot Chocolate"
description = "Made with love! And coco beans."
nutriment_factor = 3 * REAGENTS_METABOLISM
- color = "#403010" // rgb: 64, 48, 16
+ color = "#660000" // rgb: 221, 202, 134
taste_description = "creamy chocolate"
glass_icon_state = "chocolateglass"
glass_name = "glass of chocolate"
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index 23176f8a05..6d5c546e7e 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -105,11 +105,11 @@
/obj/item/reagent_containers/proc/bartender_check(atom/target)
. = FALSE
var/turf/T = get_turf(src)
- if(!T || target.CanPass(src, T) || !thrownby || !thrownby.actions)
+ if(!T || !target.CanPass(src, T) || !thrownby || !thrownby.actions)
return
- for(var/datum/action/innate/drink_fling/D in thrownby.actions)
- if(D.active)
- return TRUE
+ var/datum/action/innate/D = get_action_of_type(thrownby, /datum/action/innate/drink_fling)
+ if(D?.active)
+ return TRUE
/obj/item/reagent_containers/proc/ForceResetRotation()
transform = initial(transform)
@@ -131,13 +131,14 @@
if(thrownby)
log_combat(thrownby, M, "splashed", R)
reagents.reaction(target, TOUCH)
-
+
else if(bartender_check(target) && thrown)
visible_message("[src] lands onto the [target.name] without spilling a single drop.")
transform = initial(transform)
addtimer(CALLBACK(src, .proc/ForceResetRotation), 1)
return
+
else
if(isturf(target) && reagents.reagent_list.len && thrownby)
log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index eac8edcbc8..fd4282b1b0 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -423,9 +423,9 @@
else
unload_hypo(vial,user)
-/obj/item/hypospray/mkii/AltClick(mob/living/user)
+/obj/item/hypospray/mkii/CtrlClick(mob/living/user)
. = ..()
- if(user.canUseTopic(src, FALSE))
+ if(user.canUseTopic(src, FALSE) && user.get_active_held_item(src))
switch(mode)
if(HYPO_SPRAY)
mode = HYPO_INJECT
@@ -437,7 +437,7 @@
/obj/item/hypospray/mkii/examine(mob/user)
. = ..()
- . += "Alt-Click it to toggle its mode from spraying to injecting and vice versa."
+ . += "Ctrl-Click it to toggle its mode from spraying to injecting and vice versa."
#undef HYPO_SPRAY
#undef HYPO_INJECT
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index e8c8d14141..4562f348fc 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -172,6 +172,7 @@
/obj/item/reagent_containers/spray/drying_agent
name = "drying agent spray"
desc = "A spray bottle for drying agent."
+ icon_state = "cleaner_drying"
volume = 100
list_reagents = list(/datum/reagent/drying_agent = 100)
amount_per_transfer_from_this = 2
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 2bc56f2fbd..72b2c91e4c 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -289,6 +289,16 @@
////////////Janitor Designs//////////////
/////////////////////////////////////////
+/datum/design/mop
+ name = "Mop"
+ desc = "Just your everyday standard mop."
+ id = "mop"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1200, MAT_GLASS = 100)
+ build_path = /obj/item/mop
+ category = list("Equipment")
+ departmental_flags = DEPARTMENTAL_FLAG_SERVICE
+
/datum/design/advmop
name = "Advanced Mop"
desc = "An upgraded mop with a large internal capacity for holding water or other cleaning chemicals."
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index 1d65edbe9a..467679fea6 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -225,6 +225,16 @@
category = list("Firing Pins")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+/datum/design/pin_away
+ name = "Station Locked Pin"
+ desc = "This is a security firing pin which only authorizes users who are off station."
+ id = "pin_away"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1500, MAT_GLASS = 2000)
+ build_path = /obj/item/firing_pin/away
+ category = list("Firing Pins")
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
//////////////
//Guns////////
//////////////
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index d33ef29155..71cfe7c0f5 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -52,13 +52,12 @@ Nothing else in the console has ID requirements.
research_control = FALSE
/proc/CallMaterialName(ID)
+ if(GLOB.chemical_reagents_list[ID])
+ var/datum/reagent/reagent = GLOB.chemical_reagents_list[ID]
+ return reagent.name
if (ID[1] == "$" && GLOB.materials_list[ID])
var/datum/material/material = GLOB.materials_list[ID]
return material.name
-
- else if(GLOB.chemical_reagents_list[ID])
- var/datum/reagent/reagent = GLOB.chemical_reagents_list[ID]
- return reagent.name
return "ERROR: Report This"
/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any).
diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm
index 5037f23cd1..befaf3d96a 100644
--- a/code/modules/research/stock_parts.dm
+++ b/code/modules/research/stock_parts.dm
@@ -22,7 +22,7 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi
if(works_from_distance)
user.Beam(T, icon_state = "rped_upgrade", time = 5)
T.exchange_parts(user, src)
- return FALSE
+ return TRUE
return ..()
/obj/item/storage/part_replacer/afterattack(obj/machinery/T, mob/living/user, adjacent, params)
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index e1abfedde6..58aa922936 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -602,7 +602,7 @@
display_name = "Basic Tools"
description = "Basic mechanical, electronic, surgical and botanical tools."
prereq_ids = list("base")
- design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet")
+ design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet", "mop")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
export_price = 5000
@@ -666,7 +666,7 @@
display_name = "Weapon Development Technology"
description = "Our researchers have found new to weaponize just about everything now."
prereq_ids = list("engineering")
- design_ids = list("pin_testing", "tele_shield", "lasercarbine")
+ design_ids = list("pin_testing", "tele_shield", "lasercarbine", "pin_away")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
export_price = 5000
diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm
index 097be23c40..c9affe92f9 100644
--- a/code/modules/surgery/bodyparts/bodyparts.dm
+++ b/code/modules/surgery/bodyparts/bodyparts.dm
@@ -5,6 +5,7 @@
force = 3
throwforce = 3
icon = 'icons/mob/human_parts.dmi'
+ w_class = WEIGHT_CLASS_SMALL
icon_state = ""
layer = BELOW_MOB_LAYER //so it isn't hidden behind objects when on the floor
var/mob/living/carbon/owner = null
diff --git a/code/modules/surgery/experimental_dissection.dm b/code/modules/surgery/experimental_dissection.dm
index 40fe90a829..a94abafe4c 100644
--- a/code/modules/surgery/experimental_dissection.dm
+++ b/code/modules/surgery/experimental_dissection.dm
@@ -24,8 +24,8 @@
/datum/surgery_step/dissection
name = "dissection"
- implements = list(/obj/item/scalpel/augment = 75, /obj/item/scalpel/advanced = 60, /obj/item/scalpel = 45, /obj/item/kitchen/knife = 20, /obj/item/shard = 10)// special tools not only cut down time but also improve probability, doesn't use TOOL_SCALPEL because different scalpels have different probs
- time = 125
+ implements = list(/obj/item/scalpel/alien = 100, /obj/item/scalpel/advanced = 99, /obj/item/scalpel = 90, /obj/item/kitchen/knife = 45, /obj/item/shard = 25)// special tools not only cut down time but also improve probability, doesn't use TOOL_SCALPEL because different scalpels have different probs
+ time = 100
silicons_obey_prob = TRUE
repeatable = TRUE
@@ -110,4 +110,4 @@
requires_tech = TRUE
replaced_by = null
-#undef BASE_HUMAN_REWARD
\ No newline at end of file
+#undef BASE_HUMAN_REWARD
diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm
index 61f1e71d2e..70252cfb49 100644
--- a/code/modules/tgs/core/core.dm
+++ b/code/modules/tgs/core/core.dm
@@ -1,47 +1,60 @@
-/world/TgsNew(datum/tgs_event_handler/event_handler)
- var/tgs_version = world.params[TGS_VERSION_PARAMETER]
- if(!tgs_version)
+/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
+ var/current_api = TGS_READ_GLOBAL(tgs)
+ if(current_api)
+ TGS_ERROR_LOG("TgsNew(): TGS API datum already set ([current_api])! Was TgsNew() called more than once?")
return
- var/path = SelectTgsApi(tgs_version)
- if(!path)
- TGS_ERROR_LOG("Found unsupported API version: [tgs_version]. If this is a valid version please report this, backporting is done on demand.")
+#ifdef TGS_V3_API
+ minimum_required_security_level = TGS_SECURITY_TRUSTED
+#endif
+ var/raw_parameter = world.params[TGS_VERSION_PARAMETER]
+ if(!raw_parameter)
+ return
- TGS_INFO_LOG("Activating API for version [tgs_version]")
- var/datum/tgs_api/new_api = new path
+ var/datum/tgs_version/version = new(raw_parameter)
+ if(!version.Valid(FALSE))
+ TGS_ERROR_LOG("Failed to validate TGS version parameter: [raw_parameter]!")
+ return
- var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default)
- if(result && result != TGS_UNIMPLEMENTED)
- TGS_WRITE_GLOBAL(tgs, new_api)
- else
+ var/api_datum
+ switch(version.suite)
+ if(3)
+#ifndef TGS_V3_API
+ TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!")
+#else
+ switch(version.major)
+ if(2)
+ api_datum = /datum/tgs_api/v3210
+#endif
+ if(4)
+ switch(version.major)
+ if(0)
+ api_datum = /datum/tgs_api/v4
+
+ var/datum/tgs_version/max_api_version = TgsMaximumAPIVersion();
+ if(version.suite != null && version.major != null && version.minor != null && version.patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
+ TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
+ api_datum = /datum/tgs_api/latest
+
+ if(!api_datum)
+ TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
+ return
+
+ TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]")
+ var/datum/tgs_api/new_api = new api_datum(version)
+
+ TGS_WRITE_GLOBAL(tgs, new_api)
+
+ var/result = new_api.OnWorldNew(event_handler, minimum_required_security_level)
+ if(!result || result == TGS_UNIMPLEMENTED)
+ TGS_WRITE_GLOBAL(tgs, null)
TGS_ERROR_LOG("Failed to activate API!")
-/world/proc/SelectTgsApi(tgs_version)
- //remove the old 3.0 header
- tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "")
-
- var/list/version_bits = splittext(tgs_version, ".")
-
- var/super = text2num(version_bits[1])
- var/major = text2num(version_bits[2])
- var/minor = text2num(version_bits[3])
- var/patch = text2num(version_bits[4])
-
- switch(super)
- if(3)
- switch(major)
- if(2)
- return /datum/tgs_api/v3210
-
- if(super != null && major != null && minor != null && patch != null && tgs_version > TgsMaximumAPIVersion())
- TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
- return /datum/tgs_api/latest
-
/world/TgsMaximumAPIVersion()
- return "4.0.0.0"
+ return new /datum/tgs_version("4.0.x.x")
/world/TgsMinimumAPIVersion()
- return "3.2.0.0"
+ return new /datum/tgs_version("3.2.0.0")
/world/TgsInitializationComplete()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -71,7 +84,9 @@
return TGS_READ_GLOBAL(tgs) != null
/world/TgsVersion()
- return world.params[TGS_VERSION_PARAMETER]
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ return api.version
/world/TgsInstanceName()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -116,6 +131,11 @@
if(api)
api.ChatPrivateMessage(message, user)
+/world/TgsSecurityLevel()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.SecurityLevel()
+
/*
The MIT License
diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm
index 6af39a75df..fb2508059a 100644
--- a/code/modules/tgs/core/datum.dm
+++ b/code/modules/tgs/core/datum.dm
@@ -1,9 +1,14 @@
TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
/datum/tgs_api
+ var/datum/tgs_version/version
+
+/datum/tgs_api/New(datum/tgs_version/version)
+ . = ..()
+ src.version = version
/datum/tgs_api/latest
- parent_type = /datum/tgs_api/v3210
+ parent_type = /datum/tgs_api/v4
TGS_PROTECT_DATUM(/datum/tgs_api)
@@ -46,6 +51,9 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
/datum/tgs_api/proc/ChatPrivateMessage(message, admin_only)
return TGS_UNIMPLEMENTED
+/datum/tgs_api/proc/SecurityLevel()
+ return TGS_UNIMPLEMENTED
+
/*
The MIT License
diff --git a/code/modules/tgs/core/default_event_handler.dm b/code/modules/tgs/core/default_event_handler.dm
deleted file mode 100644
index 716715bb26..0000000000
--- a/code/modules/tgs/core/default_event_handler.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-/datum/tgs_event_handler/tgs_default/HandleEvent(event_code)
- //TODO
- return
-
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
diff --git a/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm
new file mode 100644
index 0000000000..b97745611f
--- /dev/null
+++ b/code/modules/tgs/core/tgs_version.dm
@@ -0,0 +1,22 @@
+/datum/tgs_version/New(raw_parameter)
+ src.raw_parameter = raw_parameter
+ deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
+ var/list/version_bits = splittext(deprefixed_parameter, ".")
+
+ suite = text2num(version_bits[1])
+ if(version_bits.len > 1)
+ major = text2num(version_bits[2])
+ if(version_bits.len > 2)
+ minor = text2num(version_bits[3])
+ if(version_bits.len == 4)
+ patch = text2num(version_bits[4])
+
+/datum/tgs_version/proc/Valid(allow_wildcards = FALSE)
+ if(suite == null)
+ return FALSE
+ if(allow_wildcards)
+ return TRUE
+ return !Wildcard()
+
+/datum/tgs_version/Wildcard()
+ return major == null || minor == null || patch == null
diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm
index 7ca906c840..247f1fda5d 100644
--- a/code/modules/tgs/includes.dm
+++ b/code/modules/tgs/includes.dm
@@ -1,6 +1,10 @@
#include "core\_definitions.dm"
#include "core\core.dm"
#include "core\datum.dm"
-#include "core\default_event_handler.dm"
+#include "core\tgs_version.dm"
+#ifdef TGS_V3_API
#include "v3210\api.dm"
#include "v3210\commands.dm"
+#endif
+#include "v4\api.dm"
+#include "v4\commands.dm"
diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm
index 2a95f7861e..c536995b04 100644
--- a/code/modules/tgs/v3210/api.dm
+++ b/code/modules/tgs/v3210/api.dm
@@ -56,8 +56,9 @@
/datum/tgs_api/v3210/proc/file2list(filename)
return splittext(trim_left(trim_right(file2text(filename))), "\n")
-/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version
+/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) //don't use event handling in this version
. = FALSE
+
comms_key = world.params[SERVICE_WORLD_PARAM]
instance_name = world.params[SERVICE_INSTANCE_PARAM]
if(!instance_name)
@@ -170,6 +171,7 @@
var/datum/tgs_revision_information/ri = new
ri.commit = commit
ri.origin_commit = originmastercommit
+ return ri
/datum/tgs_api/v3210/EndProcess()
sleep(world.tick_lag) //flush the buffers
@@ -187,9 +189,12 @@
/datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only)
ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]")
-/datum/tgs_api/v3210/ChatPrivateMessage(message, admin_only)
+/datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user)
return TGS_UNIMPLEMENTED
+/datum/tgs_api/v3210/SecurityLevel()
+ return TGS_SECURITY_TRUSTED
+
#undef REBOOT_MODE_NORMAL
#undef REBOOT_MODE_HARD
#undef REBOOT_MODE_SHUTDOWN
diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm
new file mode 100644
index 0000000000..b61cddbeba
--- /dev/null
+++ b/code/modules/tgs/v4/api.dm
@@ -0,0 +1,342 @@
+#define TGS4_PARAM_INFO_JSON "tgs_json"
+
+#define TGS4_INTEROP_ACCESS_IDENTIFIER "tgs_tok"
+
+#define TGS4_RESPONSE_SUCCESS "tgs_succ"
+
+#define TGS4_TOPIC_CHANGE_PORT "tgs_port"
+#define TGS4_TOPIC_CHANGE_REBOOT_MODE "tgs_rmode"
+#define TGS4_TOPIC_CHAT_COMMAND "tgs_chat_comm"
+#define TGS4_TOPIC_EVENT "tgs_event"
+#define TGS4_TOPIC_INTEROP_RESPONSE "tgs_interop"
+
+#define TGS4_COMM_NEW_PORT "tgs_new_port"
+#define TGS4_COMM_VALIDATE "tgs_validate"
+#define TGS4_COMM_SERVER_PRIMED "tgs_prime"
+#define TGS4_COMM_WORLD_REBOOT "tgs_reboot"
+#define TGS4_COMM_END_PROCESS "tgs_kill"
+#define TGS4_COMM_CHAT "tgs_chat_send"
+
+#define TGS4_PARAMETER_COMMAND "tgs_com"
+#define TGS4_PARAMETER_DATA "tgs_data"
+
+#define TGS4_PORT_CRITFAIL_MESSAGE " Must exit to let watchdog reboot..."
+
+#define EXPORT_TIMEOUT_DS 200
+
+/datum/tgs_api/v4
+ var/access_identifier
+ var/instance_name
+ var/json_path
+ var/chat_channels_json_path
+ var/chat_commands_json_path
+ var/server_commands_json_path
+ var/reboot_mode = TGS_REBOOT_MODE_NORMAL
+ var/security_level
+
+ var/requesting_new_port = FALSE
+
+ var/list/intercepted_message_queue
+
+ var/list/custom_commands
+
+ var/list/cached_test_merges
+ var/datum/tgs_revision_information/cached_revision
+
+ var/datum/tgs_event_handler/event_handler
+
+ var/export_lock = FALSE
+ var/list/last_interop_response
+
+/datum/tgs_api/v4/ApiVersion()
+ return "4.0.0.0"
+
+/datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level)
+ json_path = world.params[TGS4_PARAM_INFO_JSON]
+ if(!json_path)
+ TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!")
+ return
+ var/json_file = file2text(json_path)
+ if(!json_file)
+ TGS_ERROR_LOG("Missing specified json file: [json_path]")
+ return
+ var/cached_json = json_decode(json_file)
+ if(!cached_json)
+ TGS_ERROR_LOG("Failed to decode info json: [json_file]")
+ return
+
+ access_identifier = cached_json["accessIdentifier"]
+ server_commands_json_path = cached_json["serverCommandsJson"]
+
+ if(cached_json["apiValidateOnly"])
+ TGS_INFO_LOG("Validating API and exiting...")
+ Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]"))
+ del(world)
+
+ security_level = cached_json["securityLevel"]
+ chat_channels_json_path = cached_json["chatChannelsJson"]
+ chat_commands_json_path = cached_json["chatCommandsJson"]
+ src.event_handler = event_handler
+ instance_name = cached_json["instanceName"]
+
+ ListCustomCommands()
+
+ var/list/revisionData = cached_json["revision"]
+ if(revisionData)
+ cached_revision = new
+ cached_revision.commit = revisionData["commitSha"]
+ cached_revision.origin_commit = revisionData["originCommitSha"]
+
+ cached_test_merges = list()
+ var/list/json = cached_json["testMerges"]
+ for(var/entry in json)
+ var/datum/tgs_revision_information/test_merge/tm = new
+ tm.time_merged = text2num(entry["timeMerged"])
+
+ var/list/revInfo = entry["revision"]
+ if(revInfo)
+ tm.commit = revInfo["commitSha"]
+ tm.origin_commit = revInfo["originCommitSha"]
+
+ tm.title = entry["titleAtMerge"]
+ tm.body = entry["bodyAtMerge"]
+ tm.url = entry["url"]
+ tm.author = entry["author"]
+ tm.number = entry["number"]
+ tm.pull_request_commit = entry["pullRequestRevision"]
+ tm.comment = entry["comment"]
+
+ cached_test_merges += tm
+
+ return TRUE
+
+/datum/tgs_api/v4/OnInitializationComplete()
+ Export(TGS4_COMM_SERVER_PRIMED)
+
+ var/tgs4_secret_sleep_offline_sauce = 24051994
+ var/old_sleep_offline = world.sleep_offline
+ world.sleep_offline = tgs4_secret_sleep_offline_sauce
+ sleep(1)
+ if(world.sleep_offline == tgs4_secret_sleep_offline_sauce) //if not someone changed it
+ world.sleep_offline = old_sleep_offline
+
+/datum/tgs_api/v4/OnTopic(T)
+ var/list/params = params2list(T)
+ var/their_sCK = params[TGS4_INTEROP_ACCESS_IDENTIFIER]
+ if(!their_sCK)
+ return FALSE //continue world/Topic
+
+ if(their_sCK != access_identifier)
+ return "Invalid comms key!";
+
+ var/command = params[TGS4_PARAMETER_COMMAND]
+ if(!command)
+ return "No command!"
+
+ . = TGS4_RESPONSE_SUCCESS
+
+ switch(command)
+ if(TGS4_TOPIC_CHAT_COMMAND)
+ var/result = HandleCustomCommand(params[TGS4_PARAMETER_DATA])
+ if(result == null)
+ result = "Error running chat command!"
+ return result
+ if(TGS4_TOPIC_EVENT)
+ intercepted_message_queue = list()
+ var/list/event_notification = json_decode(params[TGS4_PARAMETER_DATA])
+ var/list/event_parameters = event_notification["Parameters"]
+
+ var/list/event_call = list(event_notification["Type"])
+ if(event_parameters)
+ event_call += event_parameters
+
+ if(event_handler != null)
+ event_handler.HandleEvent(arglist(event_call))
+
+ . = json_encode(intercepted_message_queue)
+ intercepted_message_queue = null
+ return
+ if(TGS4_TOPIC_INTEROP_RESPONSE)
+ last_interop_response = json_decode(params[TGS4_PARAMETER_DATA])
+ return
+ if(TGS4_TOPIC_CHANGE_PORT)
+ var/new_port = text2num(params[TGS4_PARAMETER_DATA])
+ if (!(new_port > 0))
+ return "Invalid port: [new_port]"
+
+ //the topic still completes, miraculously
+ //I honestly didn't believe byond could do it
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
+ if(!world.OpenPort(new_port))
+ return "Port change failed!"
+ return
+ if(TGS4_TOPIC_CHANGE_REBOOT_MODE)
+ var/new_reboot_mode = text2num(params[TGS4_PARAMETER_DATA])
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_REBOOT_MODE_CHANGE, reboot_mode, new_reboot_mode)
+ reboot_mode = new_reboot_mode
+ return
+
+ return "Unknown command: [command]"
+
+/datum/tgs_api/v4/proc/Export(command, list/data, override_requesting_new_port = FALSE)
+ if(!data)
+ data = list()
+ data[TGS4_PARAMETER_COMMAND] = command
+ var/json = json_encode(data)
+
+ while(requesting_new_port && !override_requesting_new_port)
+ sleep(1)
+
+ //we need some port open at this point to facilitate return communication
+ if(!world.port)
+ requesting_new_port = TRUE
+ if(!world.OpenPort(0)) //open any port
+ TGS_ERROR_LOG("Unable to open random port to retrieve new port![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+
+ //request a new port
+ export_lock = FALSE
+ var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]"), TRUE) //stringify this on purpose
+
+ if(!new_port_json)
+ TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+
+ var/new_port = new_port_json[TGS4_PARAMETER_DATA]
+ if(!isnum(new_port) || new_port <= 0)
+ TGS_ERROR_LOG("Malformed new port json ([json_encode(new_port_json)])![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+
+ if(new_port != world.port && !world.OpenPort(new_port))
+ TGS_ERROR_LOG("Unable to open port [new_port]![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+ requesting_new_port = FALSE
+
+ while(export_lock)
+ sleep(1)
+ export_lock = TRUE
+
+ last_interop_response = null
+ fdel(server_commands_json_path)
+ text2file(json, server_commands_json_path)
+
+ for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
+ sleep(1)
+
+ if(!last_interop_response)
+ TGS_ERROR_LOG("Failed to get export result for: [json]")
+ else
+ . = last_interop_response
+
+ export_lock = FALSE
+
+/datum/tgs_api/v4/OnReboot()
+ var/list/result = Export(TGS4_COMM_WORLD_REBOOT)
+ if(!result)
+ return
+
+ //okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter
+
+ var/port = result[TGS4_PARAMETER_DATA]
+ if(!isnum(port))
+ return //this is valid, server may just want use to reboot
+
+ if(port == 0)
+ //to byond 0 means any port and "none" means close vOv
+ port = "none"
+
+ if(!world.OpenPort(port))
+ TGS_ERROR_LOG("Unable to set port to [port]!")
+
+/datum/tgs_api/v4/InstanceName()
+ return instance_name
+
+/datum/tgs_api/v4/TestMerges()
+ return cached_test_merges
+
+/datum/tgs_api/v4/EndProcess()
+ Export(TGS4_COMM_END_PROCESS)
+
+/datum/tgs_api/v4/Revision()
+ return cached_revision
+
+/datum/tgs_api/v4/ChatBroadcast(message, list/channels)
+ var/list/ids
+ if(length(channels))
+ ids = list()
+ for(var/I in channels)
+ var/datum/tgs_chat_channel/channel = I
+ ids += channel.id
+ message = list("message" = message, "channelIds" = ids)
+ if(intercepted_message_queue)
+ intercepted_message_queue += list(message)
+ else
+ Export(TGS4_COMM_CHAT, message)
+
+/datum/tgs_api/v4/ChatTargetedBroadcast(message, admin_only)
+ var/list/channels = list()
+ for(var/I in ChatChannelInfo())
+ var/datum/tgs_chat_channel/channel = I
+ if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only)))
+ channels += channel.id
+ message = list("message" = message, "channelIds" = channels)
+ if(intercepted_message_queue)
+ intercepted_message_queue += list(message)
+ else
+ Export(TGS4_COMM_CHAT, message)
+
+/datum/tgs_api/v4/ChatPrivateMessage(message, datum/tgs_chat_user/user)
+ message = list("message" = message, "channelIds" = list(user.channel.id))
+ if(intercepted_message_queue)
+ intercepted_message_queue += list(message)
+ else
+ Export(TGS4_COMM_CHAT, message)
+
+/datum/tgs_api/v4/ChatChannelInfo()
+ . = list()
+ //no caching cause tgs may change this
+ var/list/json = json_decode(file2text(chat_channels_json_path))
+ for(var/I in json)
+ . += DecodeChannel(I)
+
+/datum/tgs_api/v4/proc/DecodeChannel(channel_json)
+ var/datum/tgs_chat_channel/channel = new
+ channel.id = channel_json["id"]
+ channel.friendly_name = channel_json["friendlyName"]
+ channel.connection_name = channel_json["connectionName"]
+ channel.is_admin_channel = channel_json["isAdminChannel"]
+ channel.is_private_channel = channel_json["isPrivateChannel"]
+ channel.custom_tag = channel_json["tag"]
+ return channel
+
+/datum/tgs_api/v4/SecurityLevel()
+ return security_level
+
+/*
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm
new file mode 100644
index 0000000000..af21a86fb6
--- /dev/null
+++ b/code/modules/tgs/v4/commands.dm
@@ -0,0 +1,69 @@
+/datum/tgs_api/v4/proc/ListCustomCommands()
+ var/results = list()
+ custom_commands = list()
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ var/datum/tgs_chat_command/stc = new I
+ var/command_name = stc.name
+ if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
+ TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
+ continue
+
+ if(results[command_name])
+ var/datum/other = custom_commands[command_name]
+ TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
+ continue
+ results += list(list("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only))
+ custom_commands[command_name] = stc
+
+ var/commands_file = chat_commands_json_path
+ if(!commands_file)
+ return
+ text2file(json_encode(results), commands_file)
+
+/datum/tgs_api/v4/proc/HandleCustomCommand(command_json)
+ var/list/data = json_decode(command_json)
+ var/command = data["command"]
+ var/user = data["user"]
+ var/params = data["params"]
+
+ var/datum/tgs_chat_user/u = new
+ u.id = user["id"]
+ u.friendly_name = user["friendlyName"]
+ u.mention = user["mention"]
+ u.channel = DecodeChannel(user["channel"])
+
+ var/datum/tgs_chat_command/sc = custom_commands[command]
+ if(sc)
+ var/result = sc.Run(u, params)
+ if(result == null)
+ result = ""
+ return result
+ return "Unknown command: [command]!"
+
+/*
+
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm
index efb0905f6b..9dda649b24 100644
--- a/code/modules/vending/snack.dm
+++ b/code/modules/vending/snack.dm
@@ -4,22 +4,25 @@
product_slogans = "Try our new nougat bar!;Twice the calories for half the price!"
product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!"
icon_state = "snack"
- products = list(/obj/item/reagent_containers/food/snacks/candy = 6,
- /obj/item/reagent_containers/food/drinks/dry_ramen = 6,
- /obj/item/reagent_containers/food/snacks/chips = 6,
- /obj/item/reagent_containers/food/snacks/sosjerky = 6,
- /obj/item/reagent_containers/food/snacks/no_raisin = 6,
- /obj/item/reagent_containers/food/snacks/spacetwinkie = 6,
- /obj/item/reagent_containers/food/snacks/cheesiehonkers = 6)
+ products = list(/obj/item/reagent_containers/food/snacks/candy = 5,
+ /obj/item/reagent_containers/food/snacks/chocolatebar = 5,
+ /obj/item/reagent_containers/food/drinks/dry_ramen = 5,
+ /obj/item/reagent_containers/food/snacks/chips = 5,
+ /obj/item/reagent_containers/food/snacks/sosjerky = 5,
+ /obj/item/reagent_containers/food/snacks/no_raisin = 5,
+ /obj/item/reagent_containers/food/snacks/spacetwinkie = 5,
+ /obj/item/reagent_containers/food/snacks/cheesiehonkers = 5,
+ /obj/item/reagent_containers/food/snacks/cornchips = 5)
contraband = list(
- /obj/item/reagent_containers/food/snacks/cracker = 12,
- /obj/item/reagent_containers/food/snacks/beans = 2,
- /obj/item/reagent_containers/food/snacks/honeybar = 6,
- /obj/item/reagent_containers/food/snacks/syndicake = 6)
+ /obj/item/reagent_containers/food/snacks/cracker = 10,
+ /obj/item/reagent_containers/food/snacks/honeybar = 5,
+ /obj/item/reagent_containers/food/snacks/syndicake = 5,
+ /obj/item/reagent_containers/food/snacks/beans = 2)
premium = list(
/obj/item/reagent_containers/food/snacks/lollipop = 2,
/obj/item/reagent_containers/food/snacks/spiderlollipop = 2,
/obj/item/reagent_containers/food/snacks/chococoin = 1,
+ /obj/item/storage/box/marshmallow = 1,
/obj/item/storage/box/donkpockets = 2)
refill_canister = /obj/item/vending_refill/snack
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index 0a3ded63bd..5d2e08734e 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -399,3 +399,35 @@
/obj/item/vending_refill/wardrobe/viro_wardrobe
machine_name = "ViroDrobe"
+
+/obj/machinery/vending/wardrobe/cap_wardrobe
+ name = "Captain's Wardrobe"
+ desc = "The latest and greatest in Nanotrasen fashion for your great leader."
+ icon_state = "capsdrobe"
+ icon_deny = "capsdrobe-deny"
+ product_ads = "Only the greatest for a commander such as ours."
+ req_access = list(ACCESS_CAPTAIN)
+ vend_reply = "A wonderful day to you, great leader."
+ products = list(/obj/item/clothing/suit/hooded/wintercoat/captain = 1,
+ /obj/item/storage/backpack/captain = 1,
+ /obj/item/storage/backpack/satchel/cap = 1,
+ /obj/item/storage/backpack/duffelbag/captain = 1,
+ /obj/item/clothing/neck/cloak/cap = 1,
+ /obj/item/clothing/shoes/sneakers/brown = 2,
+ /obj/item/clothing/under/rank/captain = 1,
+ /obj/item/clothing/under/rank/captain/skirt = 1,
+ /obj/item/clothing/head/caphat = 1,
+ /obj/item/clothing/head/caphat/parade = 1,
+ /obj/item/clothing/head/caphat/beret = 1,
+ /obj/item/clothing/head/caphat/beret/white = 1,
+ /obj/item/clothing/under/captainparade = 1,
+ /obj/item/clothing/suit/captunic = 1,
+ /obj/item/clothing/under/rank/captain/femformal = 1,
+ /obj/item/clothing/glasses/sunglasses/gar/supergar = 1,
+ /obj/item/clothing/gloves/color/captain = 1)
+ refill_canister = /obj/item/vending_refill/wardrobe/cap_wardrobe
+
+/obj/item/vending_refill/wardrobe/cap_wardrobe
+ machine_name = "Captain's Wardrobe"
+ icon_state = "refill_caps"
+
\ No newline at end of file
diff --git a/config/config.txt b/config/config.txt
index b91e8fce33..0ab0cc911e 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -448,8 +448,14 @@ MINUTE_CLICK_LIMIT 400
##How long to wait between messaging admins about occurences of a unique error
#ERROR_MSG_DELAY 50
-## Send a message to IRC when starting a new game
-#IRC_ANNOUNCE_NEW_GAME
+## Chat Announce Options
+## Various messages to be sent to game chats
+## Uncommenting these will enable them, by default they will be broadcast to Game chat channels on TGS3 or non-admin channels on TGS4
+## If using TGS4, the string option can be set as a chat channel tag to limit the message to channels of that tag type (case-sensitive)
+## i.e. CHAT_ANNOUNCE_NEW_GAME chat_channel_tag
+
+## Send a message with the station name starting a new game
+#CHAT_ANNOUNCE_NEW_GAME
## Allow admin hrefs that don't use the new token system, will eventually be removed
DEBUG_ADMIN_HREFS
@@ -500,3 +506,5 @@ FAIL2TOPIC_MAX_FAILS 5
## Firewall rule name used on physical server
FAIL2TOPIC_RULE_NAME _dd_fail2topic
+## Enable automatic profiling - Byond 513.1506 and newer only.
+#AUTO_PROFILE
diff --git a/html/changelogs/AutoChangeLog-pr-10861.yml b/html/changelogs/AutoChangeLog-pr-10861.yml
new file mode 100644
index 0000000000..5071cc30f9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10861.yml
@@ -0,0 +1,4 @@
+author: "ancientpower"
+delete-after: True
+changes:
+ - rscadd: "Ported drink sliding from /vg/station."
diff --git a/html/changelogs/AutoChangeLog-pr-10913.yml b/html/changelogs/AutoChangeLog-pr-10913.yml
new file mode 100644
index 0000000000..8bf549a154
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10913.yml
@@ -0,0 +1,4 @@
+author: "Feasel"
+delete-after: True
+changes:
+ - balance: "Buffed dissection success chances."
diff --git a/html/changelogs/AutoChangeLog-pr-10943.yml b/html/changelogs/AutoChangeLog-pr-10943.yml
new file mode 100644
index 0000000000..f45d8b1d39
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10943.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed a little inconvenience with the character setup preview dummy having extra unwarranted bits."
diff --git a/html/changelogs/AutoChangeLog-pr-10955.yml b/html/changelogs/AutoChangeLog-pr-10955.yml
new file mode 100644
index 0000000000..46ff62efaf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10955.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "gravity gun repulse and chaos now work in all angles, rather than just basic cardinals and diagonals. fun."
diff --git a/html/changelogs/AutoChangeLog-pr-10961.yml b/html/changelogs/AutoChangeLog-pr-10961.yml
new file mode 100644
index 0000000000..eaaafab472
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10961.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed an issue with the nearsighted prescription glasses taking over worn eyewear."
diff --git a/html/changelogs/AutoChangeLog-pr-10969.yml b/html/changelogs/AutoChangeLog-pr-10969.yml
new file mode 100644
index 0000000000..fdd53cdd2f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10969.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "crowbarring manifests off crates"
diff --git a/html/changelogs/AutoChangeLog-pr-10973.yml b/html/changelogs/AutoChangeLog-pr-10973.yml
new file mode 100644
index 0000000000..8946cddb43
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10973.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "Firing pins that only works when not on station"
diff --git a/html/changelogs/AutoChangeLog-pr-10987.yml b/html/changelogs/AutoChangeLog-pr-10987.yml
new file mode 100644
index 0000000000..abea439a86
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10987.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - imageadd: "Fixed dozen missing privates sprites."
diff --git a/html/changelogs/AutoChangeLog-pr-10989.yml b/html/changelogs/AutoChangeLog-pr-10989.yml
new file mode 100644
index 0000000000..0a5b2ad3c8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10989.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "A little fix concerning some R&D and reagents."
diff --git a/html/changelogs/AutoChangeLog-pr-10992.yml b/html/changelogs/AutoChangeLog-pr-10992.yml
new file mode 100644
index 0000000000..1d6d10396b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10992.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - balance: "Buckshot individual pellet damage up from 10 to 12.5. Still firing 6 pellets."
diff --git a/html/changelogs/AutoChangeLog-pr-10994.yml b/html/changelogs/AutoChangeLog-pr-10994.yml
new file mode 100644
index 0000000000..c27a1afc00
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10994.yml
@@ -0,0 +1,4 @@
+author: "DeltaFire15"
+delete-after: True
+changes:
+ - balance: "Nar'Sie runes no longer benefit from runed floors."
diff --git a/html/changelogs/AutoChangeLog-pr-10996.yml b/html/changelogs/AutoChangeLog-pr-10996.yml
new file mode 100644
index 0000000000..8bb7646cd6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10996.yml
@@ -0,0 +1,6 @@
+author: "PersianXerxes"
+delete-after: True
+changes:
+ - rscadd: "SMES and PACMAN attached to gulag Security to power electrified windows
+remove: Removed Sec vendor from gulag Security"
+ - tweak: "Separation of gulag and public mining"
diff --git a/html/changelogs/AutoChangeLog-pr-10997.yml b/html/changelogs/AutoChangeLog-pr-10997.yml
new file mode 100644
index 0000000000..a380a04f68
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10997.yml
@@ -0,0 +1,12 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - rscadd: "Bacon and Eggs food item. Delicious! Added a Lemony Poppy Seed Muffin."
+ - tweak: "Snack Vendor has Chocolates, Tortilla Chips, and a Marshmallow Box"
+ - balance: "Mops can now be printed at service lathe once tools are researched."
+ - balance: "Biobags can hold most organic limbs/organs, but not brains, implants, or cybernetics."
+ - balance: "Trash Bag can now hold limbs (but not heads.)"
+ - balance: "Most snack vendor items reduced by 1."
+ - bugfix: "Trash Cans are now actually craftable, and only require metal instead of plastic."
+ - imageadd: "Bacon and Eggs, Drying Agent Bottle."
+ - imageadd: "Mugs now show reagent colors of contained reagents. Thanks Seris!"
diff --git a/html/changelogs/AutoChangeLog-pr-11003.yml b/html/changelogs/AutoChangeLog-pr-11003.yml
new file mode 100644
index 0000000000..4c551c4d09
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11003.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "Energy weapons now once again have their lens in contents."
diff --git a/html/changelogs/AutoChangeLog-pr-11005.yml b/html/changelogs/AutoChangeLog-pr-11005.yml
new file mode 100644
index 0000000000..d0576c7ba8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11005.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - tweak: "Hyposprays now switch modes on CTRL click, instead of alt, as it was already reserved for dispension amount."
diff --git a/html/changelogs/AutoChangeLog-pr-11012.yml b/html/changelogs/AutoChangeLog-pr-11012.yml
new file mode 100644
index 0000000000..1d233f0692
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11012.yml
@@ -0,0 +1,4 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - rscadd: "Adds the CogChamp drink and Sprite"
diff --git a/html/changelogs/AutoChangeLog-pr-11018.yml b/html/changelogs/AutoChangeLog-pr-11018.yml
new file mode 100644
index 0000000000..2795317861
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11018.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "pais are no longer muted for literally a whole hour on emp."
diff --git a/html/changelogs/AutoChangeLog-pr-11020.yml b/html/changelogs/AutoChangeLog-pr-11020.yml
new file mode 100644
index 0000000000..52677b98e9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11020.yml
@@ -0,0 +1,4 @@
+author: "Zellular"
+delete-after: True
+changes:
+ - imageadd: "Movement state for pupdozer and its decals"
diff --git a/html/changelogs/AutoChangeLog-pr-11022.yml b/html/changelogs/AutoChangeLog-pr-11022.yml
new file mode 100644
index 0000000000..ac317e56b0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11022.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "auto profiler subsystem from tg"
diff --git a/html/changelogs/AutoChangeLog-pr-11024.yml b/html/changelogs/AutoChangeLog-pr-11024.yml
new file mode 100644
index 0000000000..3af136f15e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11024.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "Brig cells now are more accurate by using timeofday instead of realtime"
diff --git a/html/changelogs/AutoChangeLog-pr-11026.yml b/html/changelogs/AutoChangeLog-pr-11026.yml
new file mode 100644
index 0000000000..c99aad2d14
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11026.yml
@@ -0,0 +1,5 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - balance: "The suit full of spiders also known as a ninja now is a tactical turtleneck"
+ - balance: "The Wiznerds now dont have suits set to max"
diff --git a/html/changelogs/AutoChangeLog-pr-11027.yml b/html/changelogs/AutoChangeLog-pr-11027.yml
new file mode 100644
index 0000000000..4e5f5b46ba
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11027.yml
@@ -0,0 +1,5 @@
+author: "ShadeAware"
+delete-after: True
+changes:
+ - rscadd: "The Captain's Wardrobe, a special one-of-a-kind and ID-locked wardrobe for the Captain that holds all of their snowflakey gear.
+Remove: Snowflake gear from the Captain's locker, since now it has its own vendor."
diff --git a/html/changelogs/AutoChangeLog-pr-11029.yml b/html/changelogs/AutoChangeLog-pr-11029.yml
new file mode 100644
index 0000000000..17f34bbfa6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11029.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - tweak: "default should be... default"
diff --git a/html/changelogs/AutoChangeLog-pr-11032.yml b/html/changelogs/AutoChangeLog-pr-11032.yml
new file mode 100644
index 0000000000..6d026e444a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11032.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "Cryo now actually transfers reagents at tier 4 on enter rather than every 80 machine fire()-process()s regardless of if it was \"reset\" by open/close."
diff --git a/html/changelogs/AutoChangeLog-pr-11035.yml b/html/changelogs/AutoChangeLog-pr-11035.yml
new file mode 100644
index 0000000000..94a57db904
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11035.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "ctf claymores now actually spawn (and fit)."
diff --git a/html/changelogs/AutoChangeLog-pr-11038.yml b/html/changelogs/AutoChangeLog-pr-11038.yml
new file mode 100644
index 0000000000..b157c31af8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11038.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "STOP_PROCESSING now removes from currentrun to prevent another process cycle from happening where it shouldn't."
diff --git a/html/changelogs/AutoChangeLog-pr-11043.yml b/html/changelogs/AutoChangeLog-pr-11043.yml
new file mode 100644
index 0000000000..28217af630
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11043.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "apc icons"
diff --git a/html/changelogs/AutoChangeLog-pr-11044.yml b/html/changelogs/AutoChangeLog-pr-11044.yml
new file mode 100644
index 0000000000..7dec153778
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11044.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscadd: "The syndicate uplinks interface has been redone in TGUI Next"
diff --git a/icons/mob/suit_digi.dmi b/icons/mob/suit_digi.dmi
index bb00742a17..c6bda60d38 100644
Binary files a/icons/mob/suit_digi.dmi and b/icons/mob/suit_digi.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index b03a1cdeae..17d6024586 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi
index 93acc07453..2639d6860a 100644
Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ
diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi
index a719150f11..670269da00 100644
Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ
diff --git a/icons/obj/janitor.dmi b/icons/obj/janitor.dmi
index cb3870dc61..9128654a5b 100644
Binary files a/icons/obj/janitor.dmi and b/icons/obj/janitor.dmi differ
diff --git a/icons/obj/vending.dmi b/icons/obj/vending.dmi
index 7d08a83fb7..b11e43d665 100644
Binary files a/icons/obj/vending.dmi and b/icons/obj/vending.dmi differ
diff --git a/icons/obj/vending_restock.dmi b/icons/obj/vending_restock.dmi
index c9437d7d80..035c01de6b 100644
Binary files a/icons/obj/vending_restock.dmi and b/icons/obj/vending_restock.dmi differ
diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm
index 3a586d061d..5c7d5a39e9 100644
--- a/modular_citadel/code/modules/arousal/arousal.dm
+++ b/modular_citadel/code/modules/arousal/arousal.dm
@@ -10,12 +10,6 @@
var/hidden_undershirt = FALSE
var/hidden_socks = FALSE
-//Species vars
-/datum/species
- var/list/cum_fluids = list("semen")
- var/list/milk_fluids = list("milk")
- var/list/femcum_fluids = list("femcum")
-
//Mob procs
/mob/living/carbon/human/proc/underwear_toggle()
set name = "Toggle undergarments"
@@ -46,20 +40,12 @@
var/list/obj/item/organ/genital/genit_list = list()
if(!client?.prefs.arousable || (aphro && (client?.prefs.cit_toggles & NO_APHRO)) || (maso && !HAS_TRAIT(src, TRAIT_MASO)))
return // no adjusting made here
- if(strength>0)
- for(var/obj/item/organ/genital/G in internal_organs)
- if(!G.aroused_state && prob(strength*G.sensitivity))
- G.set_aroused_state(TRUE)
- G.update_appearance()
- if(G.aroused_state)
- genit_list += G
- else
- for(var/obj/item/organ/genital/G in internal_organs)
- if(G.aroused_state && prob(strength*G.sensitivity))
- G.set_aroused_state(FALSE)
- G.update_appearance()
- if(G.aroused_state)
- genit_list += G
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(G.genital_flags & GENITAL_CAN_AROUSE && !G.aroused_state && prob(strength*G.sensitivity))
+ G.set_aroused_state(strength > 0)
+ G.update_appearance()
+ if(G.aroused_state)
+ genit_list += G
return genit_list
/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found.
diff --git a/modular_citadel/code/modules/arousal/genitals.dm b/modular_citadel/code/modules/arousal/genitals.dm
index ca01ef46cb..b7f178bca5 100644
--- a/modular_citadel/code/modules/arousal/genitals.dm
+++ b/modular_citadel/code/modules/arousal/genitals.dm
@@ -32,23 +32,20 @@
else
update()
-/obj/item/organ/genital/Destroy()
- if(linked_organ)
- update_link(TRUE)//this should remove any other links it has
- return ..()
-
/obj/item/organ/genital/proc/set_aroused_state(new_state)
+ if(!(genital_flags & GENITAL_CAN_AROUSE))
+ return FALSE
if(!((HAS_TRAIT(owner,TRAIT_PERMABONER) && !new_state) || HAS_TRAIT(owner,TRAIT_NEVERBONER) && new_state))
aroused_state = new_state
return aroused_state
-/obj/item/organ/genital/proc/update(removing = FALSE)
+/obj/item/organ/genital/proc/update()
if(QDELETED(src))
return
update_size()
update_appearance()
- if(linked_organ_slot || (linked_organ && removing))
- update_link(removing)
+ if(linked_organ_slot || (linked_organ && !owner))
+ update_link()
//exposure and through-clothing code
/mob/living/carbon
@@ -113,17 +110,7 @@
set desc = "Allows you to toggle which genitals are showing signs of arousal."
var/list/genital_list = list()
for(var/obj/item/organ/genital/G in internal_organs)
- var/datum/sprite_accessory/S
- switch(G.type)
- if(/obj/item/organ/genital/penis)
- S = GLOB.cock_shapes_list[G.shape]
- if(/obj/item/organ/genital/testicles)
- S = GLOB.balls_shapes_list[G.shape]
- if(/obj/item/organ/genital/vagina)
- S = GLOB.vagina_shapes_list[G.shape]
- if(/obj/item/organ/genital/breasts)
- S = GLOB.breasts_shapes_list[G.shape]
- if(S?.alt_aroused)
+ if(G.genital_flags & GENITAL_CAN_AROUSE)
genital_list += G
if(!genital_list.len) //There's nothing that can show arousal
return
@@ -174,19 +161,18 @@
return TRUE
return FALSE
-/obj/item/organ/genital/proc/update_link(removing = FALSE)
- if(!removing && owner)
+/obj/item/organ/genital/proc/update_link()
+ if(owner)
if(linked_organ)
- return
+ return FALSE
linked_organ = owner.getorganslot(linked_organ_slot)
if(linked_organ)
linked_organ.linked_organ = src
linked_organ.upon_link()
upon_link()
return TRUE
- else
- if(linked_organ)
- linked_organ.linked_organ = null
+ if(linked_organ)
+ linked_organ.linked_organ = null
linked_organ = null
return FALSE
@@ -200,10 +186,13 @@
update()
RegisterSignal(owner, COMSIG_MOB_DEATH, .proc/update_appearance)
-/obj/item/organ/genital/Remove(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
- update(TRUE)
- if(!QDELETED(owner))
- UnregisterSignal(owner, COMSIG_MOB_DEATH)
+/obj/item/organ/genital/Remove(special = FALSE)
+ . = ..()
+ var/mob/living/carbon/human/H = .
+ update()
+ if(!QDELETED(H))
+ UnregisterSignal(H, COMSIG_MOB_DEATH)
+ H.update_genitals()
//proc to give a player their genitals and stuff when they log in
/mob/living/carbon/human/proc/give_genitals(clean = FALSE)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
@@ -238,12 +227,6 @@
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
return
-/datum/species/proc/genitals_layertext(layer)
- switch(layer)
- if(GENITALS_BEHIND_LAYER)
- return "BEHIND"
- if(GENITALS_FRONT_LAYER)
- return "FRONT"
//procs to handle sprite overlays being applied to humans
@@ -262,8 +245,90 @@
update_genitals()
/mob/living/carbon/human/proc/update_genitals()
- if(!QDELETED(src))
- dna.species.handle_genitals(src)
+ if(QDELETED(src))
+ return
+ var/static/list/relevant_layers = list("[GENITALS_BEHIND_LAYER]" = "BEHIND", "[GENITALS_FRONT_LAYER]" = "FRONT")
+ var/static/list/layers_num
+ if(!layers_num)
+ for(var/L in relevant_layers)
+ LAZYSET(layers_num, L, text2num(L))
+ for(var/L in relevant_layers) //Less hardcode
+ remove_overlay(layers_num[L])
+ remove_overlay(GENITALS_EXPOSED_LAYER)
+ if(!LAZYLEN(internal_organs) || ((NOGENITALS in dna.species.species_traits) && !genital_override) || HAS_TRAIT(src, TRAIT_HUSK))
+ return
+
+ //start scanning for genitals
+
+ var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
+ var/list/genitals_to_add
+ var/list/fully_exposed
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
+ LAZYADD(gen_index[G.layer_index], G)
+ for(var/L in gen_index)
+ if(L) //skip nulls
+ LAZYADD(genitals_to_add, L)
+ if(!genitals_to_add)
+ return
+ //Now we added all genitals that aren't internal and should be rendered
+ //start applying overlays
+ for(var/layer in relevant_layers)
+ var/list/standing = list()
+ var/layertext = relevant_layers[layer]
+ for(var/A in genitals_to_add)
+ var/obj/item/organ/genital/G = A
+ var/datum/sprite_accessory/S
+ var/size = G.size
+ switch(G.type)
+ if(/obj/item/organ/genital/penis)
+ S = GLOB.cock_shapes_list[G.shape]
+ if(/obj/item/organ/genital/testicles)
+ S = GLOB.balls_shapes_list[G.shape]
+ if(/obj/item/organ/genital/vagina)
+ S = GLOB.vagina_shapes_list[G.shape]
+ if(/obj/item/organ/genital/breasts)
+ S = GLOB.breasts_shapes_list[G.shape]
+
+ if(!S || S.icon_state == "none")
+ continue
+ var/aroused_state = G.aroused_state && S.alt_aroused
+
+ var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
+ if(S.center)
+ genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
+
+ if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
+ genital_overlay.color = "#[skintone2hex(skin_tone)]"
+ else
+ switch(S.color_src)
+ if("cock_color")
+ genital_overlay.color = "#[dna.features["cock_color"]]"
+ if("balls_color")
+ genital_overlay.color = "#[dna.features["balls_color"]]"
+ if("breasts_color")
+ genital_overlay.color = "#[dna.features["breasts_color"]]"
+ if("vag_color")
+ genital_overlay.color = "#[dna.features["vag_color"]]"
+
+ genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size][dna.species.use_skintones ? "_s" : ""]_[aroused_state]_[layertext]"
+
+ if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
+ genital_overlay.layer = -GENITALS_EXPOSED_LAYER
+ LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
+ else
+ standing += genital_overlay
+
+ if(LAZYLEN(standing))
+ overlays_standing[layers_num[layer]] = standing
+
+ if(LAZYLEN(fully_exposed))
+ overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
+ apply_overlay(GENITALS_EXPOSED_LAYER)
+
+ for(var/L in relevant_layers)
+ apply_overlay(layers_num[L])
+
//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
/mob/living/carbon/human/proc/emergent_genital_call()
@@ -292,86 +357,3 @@
else if (willyCheck == FALSE)
dna.features["cock_color"] = dna.features["breasts_color"]
return TRUE
-
-/datum/species/proc/handle_genitals(mob/living/carbon/human/H)//more like handle sadness
- if(!H)//no args
- CRASH("H = null")
- if(!LAZYLEN(H.internal_organs) || ((NOGENITALS in species_traits) && !H.genital_override) || HAS_TRAIT(H, TRAIT_HUSK))
- return
- var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_FRONT_LAYER)
-
- for(var/L in relevant_layers) //Less hardcode
- H.remove_overlay(L)
- H.remove_overlay(GENITALS_EXPOSED_LAYER)
- //start scanning for genitals
-
- var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
- var/list/genitals_to_add
- var/list/fully_exposed
- for(var/obj/item/organ/genital/G in H.internal_organs)
- if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
- LAZYADD(gen_index[G.layer_index], G)
- for(var/L in gen_index)
- if(L) //skip nulls
- LAZYADD(genitals_to_add, L)
- if(!genitals_to_add)
- return
- //Now we added all genitals that aren't internal and should be rendered
- //start applying overlays
- for(var/layer in relevant_layers)
- var/list/standing = list()
- var/layertext = genitals_layertext(layer)
- for(var/A in genitals_to_add)
- var/obj/item/organ/genital/G = A
- var/datum/sprite_accessory/S
- var/size = G.size
- var/aroused_state = G.aroused_state
- switch(G.type)
- if(/obj/item/organ/genital/penis)
- S = GLOB.cock_shapes_list[G.shape]
- if(/obj/item/organ/genital/testicles)
- S = GLOB.balls_shapes_list[G.shape]
- if(/obj/item/organ/genital/vagina)
- S = GLOB.vagina_shapes_list[G.shape]
- if(/obj/item/organ/genital/breasts)
- S = GLOB.breasts_shapes_list[G.shape]
-
- if(!S || S.icon_state == "none")
- continue
-
- var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
- genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]"
-
- if(S.center)
- genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
-
- if(use_skintones && H.dna.features["genitals_use_skintone"])
- genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
- else
- switch(S.color_src)
- if("cock_color")
- genital_overlay.color = "#[H.dna.features["cock_color"]]"
- if("balls_color")
- genital_overlay.color = "#[H.dna.features["balls_color"]]"
- if("breasts_color")
- genital_overlay.color = "#[H.dna.features["breasts_color"]]"
- if("vag_color")
- genital_overlay.color = "#[H.dna.features["vag_color"]]"
-
- genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]-s_[aroused_state]_[layertext]"
-
- if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
- genital_overlay.layer = -GENITALS_EXPOSED_LAYER
- LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
- else
- standing += genital_overlay
-
- if(LAZYLEN(standing))
- H.overlays_standing[layer] = standing
-
- if(LAZYLEN(fully_exposed))
- H.overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
- H.apply_overlay(GENITALS_EXPOSED_LAYER)
-
- for(var/L in relevant_layers)
- H.apply_overlay(L)
diff --git a/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm b/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
index 0c1763b8e2..594fc83e46 100644
--- a/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
+++ b/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
@@ -85,36 +85,36 @@
//Vaginas
/datum/sprite_accessory/vagina
icon = 'modular_citadel/icons/obj/genitals/vagina_onmob.dmi'
- icon_state = null
name = "vagina"
color_src = "vag_color"
+ alt_aroused = TRUE
/datum/sprite_accessory/vagina/human
icon_state = "human"
name = "Human"
- alt_aroused = TRUE
/datum/sprite_accessory/vagina/tentacles
icon_state = "tentacle"
name = "Tentacle"
- alt_aroused = TRUE
/datum/sprite_accessory/vagina/dentata
icon_state = "dentata"
name = "Dentata"
- alt_aroused = TRUE
/datum/sprite_accessory/vagina/hairy
icon_state = "hairy"
name = "Hairy"
+ alt_aroused = FALSE
/datum/sprite_accessory/vagina/spade
icon_state = "spade"
name = "Spade"
+ alt_aroused = FALSE
/datum/sprite_accessory/vagina/furred
icon_state = "furred"
name = "Furred"
+ alt_aroused = FALSE
/datum/sprite_accessory/vagina/gaping
icon_state = "gaping"
@@ -125,7 +125,6 @@
icon = 'modular_citadel/icons/obj/genitals/breasts_onmob.dmi'
name = "breasts"
color_src = "breasts_color"
- alt_aroused = TRUE
/datum/sprite_accessory/breasts/pair
icon_state = "pair"
diff --git a/modular_citadel/code/modules/arousal/organs/breasts.dm b/modular_citadel/code/modules/arousal/organs/breasts.dm
index 1537d5cebe..dc7923e448 100644
--- a/modular_citadel/code/modules/arousal/organs/breasts.dm
+++ b/modular_citadel/code/modules/arousal/organs/breasts.dm
@@ -1,3 +1,6 @@
+#define BREASTS_ICON_MIN_SIZE 1
+#define BREASTS_ICON_MAX_SIZE 6
+
/obj/item/organ/genital/breasts
name = "breasts"
desc = "Female milk producing organs."
@@ -9,7 +12,7 @@
fluid_id = /datum/reagent/consumable/milk
fluid_rate = MILK_RATE
shape = "pair"
- genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION|GENITAL_CAN_AROUSE
masturbation_verb = "massage"
arousal_verb = "Your breasts start feeling sensitive"
unarousal_verb = "Your breasts no longer feel sensitive"
@@ -49,21 +52,21 @@
var/datum/reagent/R = GLOB.chemical_reagents_list[fluid_id]
if(R)
desc += " They're leaking [lowertext(R.name)]."
- var/string
+ var/datum/sprite_accessory/S = GLOB.breasts_shapes_list[shape]
+ var/icon_shape = S ? S.icon_state : "pair"
+ var/icon_size = CLAMP(breast_values[size], BREASTS_ICON_MIN_SIZE, BREASTS_ICON_MAX_SIZE)
+ icon_state = "breasts_[icon_shape]_[breast_values[icon_size]]"
if(owner)
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "breasts_[GLOB.breasts_shapes_icons[shape]]_[size]-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["breasts_color"]]"
- string = "breasts_[GLOB.breasts_shapes_icons[shape]]_[size]"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
- icon_state = sanitize_text(string)
//Allows breasts to grow and change size, with sprite changes too.
//maximum wah
@@ -130,3 +133,6 @@
cached_size = size
size = breast_values[size]
prev_size = cached_size
+
+#undef BREASTS_ICON_MIN_SIZE
+#undef BREASTS_ICON_MAX_SIZE
\ No newline at end of file
diff --git a/modular_citadel/code/modules/arousal/organs/penis.dm b/modular_citadel/code/modules/arousal/organs/penis.dm
index c6d3c764ac..38f463a97c 100644
--- a/modular_citadel/code/modules/arousal/organs/penis.dm
+++ b/modular_citadel/code/modules/arousal/organs/penis.dm
@@ -8,13 +8,13 @@
masturbation_verb = "stroke"
arousal_verb = "You pop a boner"
unarousal_verb = "Your boner goes down"
- genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE
linked_organ_slot = ORGAN_SLOT_TESTICLES
fluid_transfer_factor = 0.5
size = 2 //arbitrary value derived from length and girth for sprites.
layer_index = PENIS_LAYER_INDEX
var/length = 6 //inches
- var/prev_length = 6 //really should be renamed to prev_length
+ var/prev_length = 6
var/girth = 4.38
var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
@@ -73,22 +73,21 @@
/obj/item/organ/genital/penis/update_appearance()
. = ..()
- var/string
+ var/datum/sprite_accessory/S = GLOB.cock_shapes_list[shape]
+ var/icon_shape = S ? S.icon_state : "human"
+ icon_state = "penis_[icon_shape]_[size]"
var/lowershape = lowertext(shape)
desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] [name]. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(girth, 0.25)] inch[round(girth, 0.25) != 1 ? "es" : ""] in girth."
-
if(owner)
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "penis_[GLOB.cock_shapes_icons[shape]]_[size]-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["cock_color"]]"
- string = "penis_[GLOB.cock_shapes_icons[shape]]_[size]"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
/obj/item/organ/genital/penis/get_features(mob/living/carbon/human/H)
diff --git a/modular_citadel/code/modules/arousal/organs/testicles.dm b/modular_citadel/code/modules/arousal/organs/testicles.dm
index 9573020417..eb6c1536cb 100644
--- a/modular_citadel/code/modules/arousal/organs/testicles.dm
+++ b/modular_citadel/code/modules/arousal/organs/testicles.dm
@@ -43,19 +43,19 @@
/obj/item/organ/genital/testicles/update_appearance()
. = ..()
desc = "You see an [size_name] pair of testicles."
+ var/datum/sprite_accessory/S = GLOB.balls_shapes_list[shape]
+ var/icon_shape = S ? S.icon_state : "single"
+ icon_state = "testicles_[icon_shape]_[size]"
if(owner)
- var/string
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "testicles_[GLOB.balls_shapes_icons[shape]]_[size]-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["balls_color"]]"
- string = "testicles_[GLOB.balls_shapes_icons[shape]]_[size]"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H)
diff --git a/modular_citadel/code/modules/arousal/organs/vagina.dm b/modular_citadel/code/modules/arousal/organs/vagina.dm
index 3f1bcc5f9b..311afe75b0 100644
--- a/modular_citadel/code/modules/arousal/organs/vagina.dm
+++ b/modular_citadel/code/modules/arousal/organs/vagina.dm
@@ -6,7 +6,7 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = "vagina"
size = 1 //There is only 1 size right now
- genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE
masturbation_verb = "finger"
arousal_verb = "You feel wetness on your crotch"
unarousal_verb = "You no longer feel wet"
@@ -22,7 +22,7 @@
/obj/item/organ/genital/vagina/update_appearance()
. = ..()
- var/string //Keeping this code here, so making multiple sprites for the different kinds is easier.
+ icon_state = "vagina"
var/lowershape = lowertext(shape)
var/details
@@ -55,13 +55,11 @@
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "vagina-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["vag_color"]]"
- string = "vagina"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
/obj/item/organ/genital/vagina/get_features(mob/living/carbon/human/H)
diff --git a/modular_citadel/icons/mob/markings_notmammals.dmi b/modular_citadel/icons/mob/markings_notmammals.dmi
index 79c97968ed..43452c8d14 100644
Binary files a/modular_citadel/icons/mob/markings_notmammals.dmi and b/modular_citadel/icons/mob/markings_notmammals.dmi differ
diff --git a/modular_citadel/icons/mob/widerobot.dmi b/modular_citadel/icons/mob/widerobot.dmi
index a77a109eb9..3f48c3902c 100644
Binary files a/modular_citadel/icons/mob/widerobot.dmi and b/modular_citadel/icons/mob/widerobot.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/breasts.dmi b/modular_citadel/icons/obj/genitals/breasts.dmi
index d70207ee69..24656062d2 100644
Binary files a/modular_citadel/icons/obj/genitals/breasts.dmi and b/modular_citadel/icons/obj/genitals/breasts.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi
index 371041b6f4..578086c4c3 100644
Binary files a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi and b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/penis.dmi b/modular_citadel/icons/obj/genitals/penis.dmi
index 517758b248..c3fd184a7c 100644
Binary files a/modular_citadel/icons/obj/genitals/penis.dmi and b/modular_citadel/icons/obj/genitals/penis.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/penis_onmob.dmi b/modular_citadel/icons/obj/genitals/penis_onmob.dmi
index 4f84c52360..4a92bffa2a 100644
Binary files a/modular_citadel/icons/obj/genitals/penis_onmob.dmi and b/modular_citadel/icons/obj/genitals/penis_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi b/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi
index ae2339e2e0..e8efbec216 100644
Binary files a/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi and b/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/testicles.dmi b/modular_citadel/icons/obj/genitals/testicles.dmi
index 3d7a5f4f48..386eab0956 100644
Binary files a/modular_citadel/icons/obj/genitals/testicles.dmi and b/modular_citadel/icons/obj/genitals/testicles.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/vagina.dmi b/modular_citadel/icons/obj/genitals/vagina.dmi
index 39bdd48e89..1bbe62ba0e 100644
Binary files a/modular_citadel/icons/obj/genitals/vagina.dmi and b/modular_citadel/icons/obj/genitals/vagina.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/vagina_onmob.dmi b/modular_citadel/icons/obj/genitals/vagina_onmob.dmi
index ab71b22e3b..6c34ffadfa 100644
Binary files a/modular_citadel/icons/obj/genitals/vagina_onmob.dmi and b/modular_citadel/icons/obj/genitals/vagina_onmob.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index ac66fb7d12..e55c425c1c 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -152,6 +152,7 @@
#include "code\__HELPERS\type2type_vr.dm"
#include "code\__HELPERS\typelists.dm"
#include "code\__HELPERS\unsorted.dm"
+#include "code\__HELPERS\vector.dm"
#include "code\__HELPERS\view.dm"
#include "code\__HELPERS\sorts\__main.dm"
#include "code\__HELPERS\sorts\InsertSort.dm"
@@ -273,6 +274,7 @@
#include "code\controllers\subsystem\pathfinder.dm"
#include "code\controllers\subsystem\persistence.dm"
#include "code\controllers\subsystem\ping.dm"
+#include "code\controllers\subsystem\profiler.dm"
#include "code\controllers\subsystem\radiation.dm"
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\research.dm"
@@ -291,7 +293,6 @@
#include "code\controllers\subsystem\vis_overlays.dm"
#include "code\controllers\subsystem\vore.dm"
#include "code\controllers\subsystem\vote.dm"
-#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\processing\chemistry.dm"
#include "code\controllers\subsystem\processing\circuit.dm"
#include "code\controllers\subsystem\processing\fastprocess.dm"
@@ -302,6 +303,7 @@
#include "code\controllers\subsystem\processing\processing.dm"
#include "code\controllers\subsystem\processing\projectiles.dm"
#include "code\controllers\subsystem\processing\quirks.dm"
+#include "code\controllers\subsystem\processing\weather.dm"
#include "code\controllers\subsystem\processing\wet_floors.dm"
#include "code\datums\action.dm"
#include "code\datums\ai_laws.dm"
diff --git a/tgui-next/packages/tgui/interfaces/Uplink.js b/tgui-next/packages/tgui/interfaces/Uplink.js
index 075355221a..4f2b047c9b 100644
--- a/tgui-next/packages/tgui/interfaces/Uplink.js
+++ b/tgui-next/packages/tgui/interfaces/Uplink.js
@@ -103,6 +103,7 @@ export class Uplink extends Component {
compact={compact_mode}
items={items}
hoveredItem={hoveredItem}
+ telecrystals={telecrystals}
onBuyMouseOver={item => this.setHoveredItem(item)}
onBuyMouseOut={item => this.setHoveredItem({})}
onBuy={item => act(ref, 'buy', {
diff --git a/tgui-next/packages/tgui/routes.js b/tgui-next/packages/tgui/routes.js
index 7aa2546c70..76c2834c7b 100644
--- a/tgui-next/packages/tgui/routes.js
+++ b/tgui-next/packages/tgui/routes.js
@@ -91,6 +91,7 @@ import { TankDispenser } from './interfaces/TankDispenser';
import { Teleporter } from './interfaces/Teleporter';
import { ThermoMachine } from './interfaces/ThermoMachine';
import { TurbineComputer } from './interfaces/TurbineComputer';
+import { Uplink } from './interfaces/Uplink';
import { VaultController } from './interfaces/VaultController';
import { Wires } from './interfaces/Wires';
import { AtmosRelief } from './interfaces/AtmosRelief';
@@ -500,6 +501,11 @@ const ROUTES = {
component: () => TurbineComputer,
scrollable: false,
},
+ uplink: {
+ component: () => Uplink,
+ scrollable: true,
+ theme: 'syndicate',
+ },
vault_controller: {
component: () => VaultController,
scrollable: false,
diff --git a/tools/tgs4_scripts/PostCompile.bat b/tools/tgs4_scripts/PostCompile.bat
deleted file mode 100644
index 47aae169c6..0000000000
--- a/tools/tgs4_scripts/PostCompile.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@echo off
-
-powershell -NoProfile -ExecutionPolicy Bypass -File PostCompile.ps1 -game_path %1
diff --git a/tools/tgs4_scripts/PostCompile.ps1 b/tools/tgs4_scripts/PostCompile.ps1
deleted file mode 100644
index 8e46fa8061..0000000000
--- a/tools/tgs4_scripts/PostCompile.ps1
+++ /dev/null
@@ -1,18 +0,0 @@
-param(
- $game_path
-)
-
-Write-Host "Deploying tgstation compilation..."
-
-cd $game_path
-
-mkdir build
-
-#.github is a little special cause of the prefix
-mv .github build/.github
-
-mv * build #thank god it's that easy
-
-&"build/tools/deploy.sh" $game_path $game_path/build
-
-Remove-Item build -Recurse